File manager - Edit - /home/opticamezl/www/newok/fabrik.tar
Back
Controllers/Controller.php 0000604 00000002567 15172625755 0011733 0 ustar 00 <?php /** * Created by PhpStorm. * User: rob * Date: 24/05/2016 * Time: 09:56 */ namespace Fabrik\Controllers; use \Joomla\Utilities\ArrayHelper; use \JFactory; class Controller extends \JControllerLegacy { /** * @var JApplicationCMS */ protected $app; /** * @var JUser */ protected $user; /** * @var string */ protected $package; /** * @var JSession */ protected $session; /** * @var JDocument */ protected $doc; /** * @var JDatabaseDriver */ protected $db; /** * @var Registry */ protected $config; /** * Constructor * * @param array $config A named configuration array for object construction. * */ public function __construct($config = array()) { $this->app = ArrayHelper::getValue($config, 'app', JFactory::getApplication()); $this->user = ArrayHelper::getValue($config, 'user', JFactory::getUser()); $this->package = $this->app->getUserState('com_fabrik.package', 'fabrik'); $this->session = ArrayHelper::getValue($config, 'session', JFactory::getSession()); $this->doc = ArrayHelper::getValue($config, 'doc', JFactory::getDocument()); $this->db = ArrayHelper::getValue($config, 'db', JFactory::getDbo()); $this->config = ArrayHelper::getValue($config, 'config', JFactory::getConfig()); parent::__construct($config); } } Helpers/Googlemap.php 0000604 00000004145 15172625755 0010610 0 ustar 00 <?php /** * Google Map helper class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use \stdClass; /** * Google Map class * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class Googlemap { /** * Set the google map style * * @param object $params Element/vis parameters (contains gmap_styles property as json string) * * @since 3.0.7 * * @return array Styles */ public static function styleJs($params) { $styles = $params->get('gmap_styles'); $styles = is_string($styles) ? json_decode($styles) : $styles; if (!$styles) { return array(); } // Map Feature type to style $features = $styles->style_feature; // What exactly to style in the feature type (road, fill, border etc) $elements = $styles->style_element; $styleKeys = $styles->style_styler_key; $styleValues = $styles->style_styler_value; // First merge any identical feature styles $stylers = array(); for ($i = 0; $i < count($features); $i ++) { $feature = ArrayHelper::getValue($features, $i); $element = ArrayHelper::getValue($elements, $i); $key = $feature . '|' . $element; if (!array_key_exists($key, $stylers)) { $stylers[$key] = array(); } $aStyle = new \stdClass; $styleKey = ArrayHelper::getValue($styleKeys, $i); $styleValue = ArrayHelper::getValue($styleValues, $i); if ($styleKey && $styleValue) { $aStyle->$styleKey = $styleValue; $stylers[$key][] = $aStyle; } } $return = array(); foreach ($stylers as $styleKey => $styler) { $o = new \stdClass; $bits = explode('|', $styleKey); if ( $bits[0] !== 'all') { $o->featureType = $bits[0]; } $o->elementType = $bits[1]; $o->stylers = $styler; $return[] = $o; } return $return; } } Helpers/ArrayHelper.php 0000604 00000027315 15172625755 0011120 0 ustar 00 <?php /** * Array helper class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use \stdClass; /** * Array helper class * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class ArrayHelper { /** * Get a value from a nested array * * @param array $array Data to search * @param string $key Search key, use key.dot.format to get nested value * @param string $default Default value if key not found * @param bool $allowObjects Should objects found in $array be converted into arrays * * @return mixed */ public static function getNestedValue($array, $key, $default = null, $allowObjects = false) { $keys = explode('.', $key); foreach ($keys as $key) { if (is_object($array) && $allowObjects) { $array = self::fromObject($array); } if (!is_array($array)) { return $default; } if (array_key_exists($key, $array)) { $array = $array[$key]; } else { return $default; } } return $array; } /** * update the data that gets posted via the form and stored by the form * model. Used in elements to modify posted data see fabrikfileupload * * @param array &$array array to set value for * @param string $key (in key.dot.format) to set a recursive array * @param string $val value to set key to * * @return null */ public static function setValue(&$array, $key, $val) { if (strstr($key, '.')) { $nodes = explode('.', $key); $count = count($nodes); $pathNodes = $count - 1; if ($pathNodes < 0) { $pathNodes = 0; } $ns = $array; for ($i = 0; $i <= $pathNodes; $i++) { /** * If any node along the registry path does not exist, create it * if (!isset($this->formData[$nodes[$i]])) { //this messed up for joined data */ if (!isset($ns[$nodes[$i]])) { $ns[$nodes[$i]] = array(); } $ns = $ns[$nodes[$i]]; } $ns = $val; $ns = $array; for ($i = 0; $i <= $pathNodes; $i++) { /** * If any node along the registry path does not exist, create it * if (!isset($this->formData[$nodes[$i]])) { //this messed up for joined data */ if (!isset($ns[$nodes[$i]])) { $ns[$nodes[$i]] = array(); } $ns = $ns[$nodes[$i]]; } $ns = $val; } else { $array[$key] = $val; } } /** * 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 bool $recurse into each value and set any arrays to objects * * @return object The object mapped from the given array * * @since 1.5 */ public static function toObject(&$array, $class = 'stdClass', $recurse = true) { $obj = null; if (is_array($array)) { $obj = new $class; foreach ($array as $k => $v) { if (is_array($v) && $recurse) { $obj->$k = self::toObject($v, $class); } else { $obj->$k = $v; } } } return $obj; } /** * returns copy of array $ar1 with those entries removed * whose keys appear as keys in any of the other function args * * @param array $ar1 first array * @param array $ar2 second array * * @return array */ public function array_key_diff($ar1, $ar2) { /** * , $ar3, $ar4, ... * */ $aSubtrahends = array_slice(func_get_args(), 1); foreach ($ar1 as $key => $val) { foreach ($aSubtrahends as $aSubtrahend) { if (array_key_exists($key, $aSubtrahend)) { unset($ar1[$key]); } } } return $ar1; } /** * filters array of objects removing those when key does not match * the value * * @param array &$array of objects - passed by ref * @param string $key to search on * @param string $value of key to keep from array * * @return unknown_type */ public static function filter(&$array, $key, $value) { for ($i = count($array) - 1; $i >= 0; $i--) { if ($array[$i]->$key !== $value) { unset($array[$i]); } } } /** * get the first object in an array whose key = value * * @param array $array of objects * @param string $key to search on * @param string $value to search on * * @return mixed value or false */ public function get($array, $key, $value) { for ($i = count($array) - 1; $i >= 0; $i--) { if ($array[$i]->$key == $value) { return $array[$i]; } } return false; } /** * Extract an array of single property values from an array of objects * * @param array $array the array of objects to search * @param string $key the key to extract the values on. * * @return array of single key values */ public static function extract($array, $key) { $return = array(); foreach ($array as $object) { $return[] = $object->$key; } return $return; } /** * Returns first key in an array, used if we aren't sure if array is assoc or * not, and just want the first row. * * @param array $array the array to get the first key for * * @since 3.0.6 * * @return string the first array key. */ public static function firstKey($array) { reset($array); return key($array); } /** * Array is empty, or only has one entry which itself is an empty string * * @param array $array The array to test * @param bool $even_emptierish If true, use empty() to test single key, if false only count empty string or null as empty * * @since 3.0.8 * * @return bool is array empty(ish) */ public static function emptyIsh($array, $even_emptierish = false) { if (empty($array)) { return true; } if (count($array) > 1) { return false; } $val = self::getValue($array, self::firstKey($array), ''); return $even_emptierish ? empty($val) : $val === '' || !isset($val); } /** * Workaround for J! 3.4 change in FArrayHelper::getValue(), which now forces $array to be, well, an array. * We've been a bit naughty and using it for things like SimpleXMLElement. So for J! 3.4 release, 2/25/2015, * globally replaced all use of JArrayHelper::getValue() with FArrayHelper::getValue(). This code is just a * copy of the J! code, it just doesn't specify "array $array". * * @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 */ public static function getValue(&$array, $name, $default = null, $type = '') { if (is_object($array)) { $array = self::fromObject($array); } $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 = 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; } /** * * Wrapper for srray_fill, 'cos PHP <5.6 tosses a warning if $num is not positive, * and we often call it with 0 length * * @param int $start_index * @param int $num * @param mixed $value * * @return array */ public static function array_fill($start_index, $num, $value) { if ($num > 0) { return array_fill($start_index, $num, $value); } else { return array(); } } /** * 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 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; } /** * Wrapper for standard array_chunk that allows for flipping a grid. So without flipping, chunking ... * * 1, 2, 3, 4, 5 * * ... into a chunksize of 2 becomes ... * * 1, 2 * 3, 4 * 5 * * With flipping, it becomes ... * * 1, 4 * 2, 5 * 3 * * This is useful for building Bootstrap style grids from unchunked arrays. * * @param $array * @param $cols * @param bool $flip * * @return array * * @since 3.8 */ public static function chunk($array, $cols, $flip = false) { $chunked = array_chunk($array, $cols); if ($flip) { $rows = count($chunked); $ridx = 0; $cidx = 0; $flipped = array(); foreach($chunked as $row) { foreach($row as $val) { $flipped[$ridx][$cidx] = $val; $ridx++; if($ridx >= $rows) { $cidx++; $ridx = 0; } } } return $flipped; } return $chunked; } } Helpers/Uploader.php 0000604 00000015333 15172625755 0010452 0 ustar 00 <?php /** * Fabrik upload helper * * @package Joomla * @subpackage Fabrik * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use JFactory; use JFile; use JFolder; use Joomla\Registry\Registry; use RuntimeException; /** * Fabrik upload helper * * @package Joomla * @subpackage Fabrik * @since 3.0 */ class Uploader extends \JObject { /** * Form model * * @var object */ protected $form = null; /** * Move uploaded file error * * @var bool */ public $moveError = false; /** * Upload * * @param object $formModel form model */ public function __construct($formModel) { $this->form = $formModel; } /** * Perform upload of files * * @return bool true if error occurred */ public function upload() { $groups = $this->form->getGroupsHiarachy(); foreach ($groups as $groupModel) { $elementModels = $groupModel->getPublishedElements(); foreach ($elementModels as $elementModel) { if ($elementModel->isUpload()) { $elementModel->processUpload(); } } } } /** * Moves a file from one location to another * * @param string $pathFrom File to move * @param string $pathTo Location to move file to * @param bool $overwrite Should we overwrite existing files * * @deprecated (don't think its used) * * @return bool do we overwrite any existing files found at pathTo? */ public function move($pathFrom, $pathTo, $overwrite = true) { if (file_exists($pathTo)) { if ($overwrite) { unlink($pathTo); $ok = rename($pathFrom, $pathTo); } else { $ok = false; } } else { $ok = rename($pathFrom, $pathTo); } return $ok; } /** * Make a recursive folder structure * * @param string $folderPath Path to folder - e.g. /images/stories * @param hex $mode Folder permissions * * @return void */ public function _makeRecursiveFolders($folderPath, $mode = 0755) { if (!JFolder::exists($folderPath)) { if (!JFolder::create($folderPath, $mode)) { throw new RuntimeException("Could not make dir $folderPath "); } } } /** * Iterates through $_FILE data to see if any files have been uploaded * * @deprecated (don't see it being used) * * @return bool true if files uploaded */ public function check() { if (isset($_FILES) and !empty($_FILES)) { foreach ($_FILES as $f) { if ($f['name'] != '') { return true; } } } return false; } /** * Checks if the file can be uploaded * * @param array $file File information * @param string &$err An error message to be returned * @param Registry &$params Params * * @return bool */ public static function canUpload($file, &$err, &$params) { if (empty($file['name'])) { $err = 'Please input a file for upload'; return false; } if (!is_uploaded_file($file['tmp_name'])) { // Handle potential malicious attack $err = Text::_('File has not been uploaded'); return false; } jimport('joomla.filesystem.file'); $format = StringHelper::strtolower(JFile::getExt($file['name'])); $allowable = explode(',', StringHelper::strtolower($params->get('ul_file_types'))); $format = StringHelper::ltrimword($format, '.'); $format2 = ".$format"; if (!in_array($format, $allowable) && !in_array($format2, $allowable)) { $err = 'WARNFILETYPE'; return false; } $maxSize = (int) $params->get('upload_maxsize', 0); if ($maxSize > 0 && (int) $file['size'] > $maxSize) { $err = 'WARNFILETOOLARGE'; return false; } $ignored = array(); $user = JFactory::getUser(); $imginfo = null; if ($params->get('restrict_uploads', 1)) { $images = explode(',', $params->get('image_extensions')); if (in_array($format, $images)) { // If its an image run it through getimagesize if (($imginfo = getimagesize($file['tmp_name'])) === false) { $err = 'WARNINVALIDIMG'; return false; } } elseif (!in_array($format, $ignored)) { // If its not an image...and we're not ignoring it } } $xss_check = file_get_contents($file['tmp_name'], false, null, 0, 256); $html_tags = array('abbr', 'acronym', 'address', 'applet', 'area', 'audioscope', 'base', 'basefont', 'bdo', 'bgsound', 'big', 'blackface', 'blink', 'blockquote', 'body', 'bq', 'br', 'button', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'comment', 'custom', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'fn', 'font', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'iframe', 'ilayer', 'img', 'input', 'ins', 'isindex', 'keygen', 'kbd', 'label', 'layer', 'legend', 'li', 'limittext', 'link', 'listing', 'map', 'marquee', 'menu', 'meta', 'multicol', 'nobr', 'noembed', 'noframes', 'noscript', 'nosmartquotes', 'object', 'ol', 'optgroup', 'option', 'param', 'plaintext', 'pre', 'rt', 'ruby', 's', 'samp', 'script', 'select', 'server', 'shadow', 'sidebar', 'small', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'tt', 'ul', 'var', 'wbr', 'xml', 'xmp', '!DOCTYPE', '!--'); foreach ($html_tags as $tag) { // A tag is '<tagname ', so we need to add < and a space or '<tagname>' if (StringHelper::stristr($xss_check, '<' . $tag . ' ') || StringHelper::stristr($xss_check, '<' . $tag . '>')) { $err = 'WARNIEXSS'; return false; } } return true; } /** * Recursive file name incrementation until no file with existing name * exists * * @param string $origFileName Initial file name * @param string $newFileName This recursions file name * @param int $version File version * @params object $storage Storage adapter * * @return string New file name */ public static function incrementFileName($origFileName, $newFileName, $version, $storage) { if ($storage->exists($newFileName)) { $bits = explode('.', $newFileName); $ext = array_pop($bits); $f = implode('.', $bits); $f = StringHelper::rtrim($f, $version - 1); $newFileName = $f . $version . "." . $ext; $version++; $newFileName = self::incrementFileName($origFileName, $newFileName, $version, $storage); } return $newFileName; } } Helpers/Lizt.php 0000604 00000002722 15172625755 0007617 0 ustar 00 <?php /** * List Helper class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); /** * List Helper class * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0.6 */ class Lizt { /** * Get a list of elements which match a set of criteria * * @param object $listModel list model to search * @param array $filter array of element properties to match on * * @throws \Exception * * @return array */ public static function getElements($listModel, $filter = array()) { $found = array(); $groups = $listModel->getFormGroupElementData(); foreach ($groups as $groupModel) { $elementModels = $groupModel->getMyElements(); foreach ($elementModels as $elementModel) { $item = $elementModel->getElement(); $ok = true; foreach ($filter as $key => $val) { if ($item->$key != $val) { $ok = false; } } if ($ok) { $found[] = $elementModel; } } } if (empty($found)) { $filterNames = implode(', ', $filter); throw new \Exception(Text::sprintf('COM_FABRIK_ERR_NO_ELEMENTS_MATCHED_FILTER', $filterNames)); } return $found; } } Helpers/LayoutFile.php 0000604 00000006052 15172625755 0010752 0 ustar 00 <?php /** * @package Joomla.Libraries * @subpackage Layout * * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Fabrik\Helpers; use Joomla\CMS\Layout\FileLayout; use \JLoader; use \JPath; defined('JPATH_BASE') or die; /** * Base class for rendering a display layout * loaded from from a layout file * * @package Joomla.Libraries * @subpackage Layout * @see http://docs.joomla.org/Sharing_layouts_across_views_or_extensions_with_JLayout * @since 3.0 */ class LayoutFile extends \JLayoutFile { /** * Method to finds the full real file path, checking possible overrides * * @return string The full path to the layout file * * @since 3.0 */ protected function getPath() { \JLoader::import('joomla.filesystem.path'); $layoutId = $this->getLayoutId(); $includePaths = $this->getIncludePaths(); $suffixes = $this->getSuffixes(); $this->addDebugMessage('<strong>Layout:</strong> ' . $this->layoutId); if (!$layoutId) { $this->addDebugMessage('<strong>There is no active layout</strong>'); return; } if (!$includePaths) { $this->addDebugMessage('<strong>There are no folders to search for layouts:</strong> ' . $layoutId); return; } $hash = md5( json_encode( array( 'paths' => $includePaths, 'suffixes' => $suffixes, ) ) ); if (isset(static::$cache[$layoutId][$hash])) { $this->addDebugMessage('<strong>Cached path:</strong> ' . static::$cache[$layoutId][$hash]); return static::$cache[$layoutId][$hash]; } $this->addDebugMessage('<strong>Include Paths:</strong> ' . print_r($includePaths, true)); // Search for suffixed versions. Example: tags.j31.php if ($suffixes) { $this->addDebugMessage('<strong>Suffixes:</strong> ' . print_r($suffixes, true)); foreach ($suffixes as $suffix) { $rawPath = str_replace('.', '/', $this->layoutId) . '.' . $suffix . '.php'; $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath); if ($foundLayout = \JPath::find($this->includePaths, $rawPath)) { $this->addDebugMessage('<strong>Found layout:</strong> ' . $this->fullPath); static::$cache[$layoutId][$hash] = $foundLayout; return static::$cache[$layoutId][$hash]; } } } // Standard version $rawPath = str_replace('.', '/', $this->layoutId) . '.php'; $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath); $foundLayout = \JPath::find($this->includePaths, $rawPath); if (!$foundLayout) { $this->addDebugMessage('<strong>Unable to find layout: </strong> ' . $layoutId); static::$cache[$layoutId][$hash] = ''; return; } $this->addDebugMessage('<strong>Found layout:</strong> ' . $foundLayout); static::$cache[$layoutId][$hash] = $foundLayout; return static::$cache[$layoutId][$hash]; } } Helpers/Image.php 0000604 00000006630 15172625755 0007721 0 ustar 00 <?php /** * Image manipulation helper * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access use \RuntimeException; use \JHtml; /** * Image manipulation class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html * @since 1.0 */ class Image { /** * Get an array of available graphics libraries * * @return array */ public static function getLibs() { $libs = array(); $gds = self::testGD(); foreach ($gds as $key => $val) { $libs[] = JHtml::_('select.option', $key, $val); } $im = self::testImagemagick(); foreach ($im as $key => $val) { $libs[] = JHtml::_('select.option', $key, $val); } return $libs; } /** * load in the correct image library * * @param string $lib image lib to load * * @throws RuntimeException * * @return \Fabrik\Helpers\Image\Image image lib */ public static function loadLib($lib) { if ($lib === 'value') { throw new RuntimeException("Fabrik: No image image processing library is available, make sure GD is installed in PHP and check your upload element settings!"); } $className = '\Fabrik\Helpers\Image\Image' . strtolower($lib); try { $class = new $className; } catch (RuntimeException $e) { throw new RuntimeException("Fabrik: can't load image class: $className"); } return $class; } /** * Test if the GD library is available * * @return array */ protected static function testGD() { $gd = array(); $gdVersion = null; $gdInfo = null; if (function_exists('gd_info')) { $gdInfo = gd_info(); $gdVersion = $gdInfo['GD Version']; } else { ob_start(); @phpinfo(INFO_MODULES); $output = ob_get_contents(); ob_end_clean(); $matches[1] = ''; if ($output !== '') { if (preg_match("/GD Version[ \t]*(<[^>]+>[ \t]*)+([^<>]+)/s", $output, $matches)) { $gdVersion = $matches[2]; } else { return $gd; } } } if (function_exists('imagecreatetruecolor') && function_exists('imagecreatefromjpeg')) { $gdVersion = isset($gdVersion) ? $gdVersion : 2; $gd['gd2'] = "GD: " . $gdVersion; } elseif (function_exists('imagecreatefromjpeg')) { $gdVersion = isset($gdVersion) ? $gdVersion : 1; $gd['gd1'] = "GD: " . $gdVersion; } return $gd; } /** * Test if Imagemagick is installed on the server * * @return array */ protected static function testImagemagick() { $im = array(); if (function_exists('NewMagickWand')) { $im['IM'] = 'Magick wand'; } else { /* $status = ''; $output = array(); @exec('convert -version', $output, $status); $im = array(); if ($status && class_exists('Imagick')) { if (preg_match("/imagemagick[ \t]+([0-9\.]+)/i", $output[0], $matches)) { $im['IM'] = $matches[0]; } } unset($output, $status); */ if (class_exists('Imagick')) { $im['IM'] = 'Imagick'; } } return $im; } } Helpers/Stripe.php 0000604 00000002550 15172625755 0010142 0 ustar 00 <?php /** * PDF Set up helper * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); /** * Stripe set up helper * * @package Joomla * @subpackage Fabrik.helpers * @since 3.1rc3 */ class Stripe { /** * Setup Stripe API * * @param object $puke throw exception if not installed (true) or just return false * * @return bool */ public static function setupStripe(\Joomla\Registry\Registry $params, $plugin) { $testMode = $params->get($plugin . '_test_mode', '0') === '1'; if ($testMode) { $secretKey = trim($params->get($plugin . '_test_secret_key', '')); } else { $secretKey = trim($params->get($plugin . '_secret_key', '')); } if (empty($secretKey)) { return false; } \Stripe\Stripe::setApiKey($secretKey); \Stripe\Stripe::setApiVersion('2018-01-23'); \Stripe\Stripe::setAppInfo( "Joomla Fabrik " . $plugin, "3.8.0", "http://fabrikar.com" ); /* try { $balance = \Stripe\Balance::retrieve(); } catch (\Exception $e) { return false; } */ return true; } } Helpers/FCipher.php 0000604 00000021347 15172625755 0010221 0 ustar 00 <?php /** * @package ${NAMESPACE} * @subpackage * * @copyright A copyright * @license A "Slug" license name e.g. GPL2 */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\Crypt\Cipher; use Joomla\CMS\Crypt\Key; use Joomla\CMS\Crypt\Cipher\SodiumCipher; //use Joomla\CMS\Encrypt\Aes; class FCipher { private $key; private $cipher; private $type; public function __construct($type = 'aes') { $this->type = $type; if ($type === 'crypt') { $this->cipher = new Cipher\CryptoCipher(); $this->key = $this->getKey(); } else if ($type === 'simple') { $this->key = $this->oldKey(); } else if ($type === 'sodium') { $this->cipher = new SodiumCipher(); $this->key = $this->getKey(); } else { $config = \JFactory::getConfig(); $secret = $config->get('secret', ''); if (trim($secret) == '') { throw new RuntimeException('You must supply a secret code in your Joomla configuration.php file'); } $this->cipher = new \FOFEncryptAes($secret, 256); } } public function encrypt($data) { try { if ($this->type === 'crypt') { return bin2hex($this->cipher->encrypt($data, $this->key)); } else if ($this->type === 'sodium') { $this->cipher->setNonce(\Sodium\randombytes_buf(\Sodium\CRYPTO_BOX_NONCEBYTES)); return bin2hex($this->cipher->encrypt($data, $this->key)); } else if ($this->type === 'simple') { return $this->oldEncrypt($data, $this->key); } else { return $this->cipher->encryptString($data); } } catch (\Exception $e) { return false; } } public function decrypt($data) { try { if ($this->type === 'crypt') { return $this->cipher->decrypt(hex2bin($data), $this->key); } else if ($this->type === 'sodium') { $this->cipher->setNonce(\Sodium\randombytes_buf(\Sodium\CRYPTO_BOX_NONCEBYTES)); return bin2hex($this->cipher->decrypt($data, $this->key)); } else if ($this->type === 'simple') { return $this->oldDecrypt($data, $this->key); } else { //return rtrim($this->cipher->decryptString($data), "\0"); return $this->cipher->decryptString($data); } } catch (\Exception $e) { return false; } } private function getKey() { $fbConfig = \JComponentHelper::getParams('com_fabrik'); $privateKey = $fbConfig->get('fabrik_private_key', ''); $publicKey = $fbConfig->get('fabrik_public_key', ''); if (empty($privateKey)) { $key = $this->generateKey(); } else { $key = new Key('crypto', hex2bin($privateKey), hex2bin($publicKey)); } return $key; } private function generateKey() { $fbConfig = \JComponentHelper::getParams('com_fabrik'); $key = $this->cipher->generateKey(); //$privateKey = $key->getPrivate(); //$publicKey = $key->getPublic(); $privateKey = $key->private; $publicKey = $key->public; $fbConfig->set('fabrik_private_key', bin2hex($privateKey)); $fbConfig->set('fabrik_public_key', bin2hex($publicKey)); $componentid = \JComponentHelper::getComponent('com_fabrik')->id; $table = \JTable::getInstance('extension'); $table->load($componentid); $table->bind(array('params' => $fbConfig->toString())); // check for error if (!$table->check()) { echo $table->getError(); return false; } // Save to database if (!$table->store()) { echo $table->getError(); return false; } return $key; } /** * Method to decrypt a data string. * * NOTE - this is the old deprecated J! simple crypt, only here for legacy (converting old to new) * * @param string $data The encrypted string to decrypt. * @param object $key The key[/pair] object to use for decryption. * * @return string The decrypted data string. * * @since 12.1 * @throws \InvalidArgumentException */ public function oldDecrypt($data, $key) { // Validate key. if ($key->type != 'simple') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected simple.'); } $decrypted = ''; $tmp = $key->public; // Convert the HEX input into an array of integers and get the number of characters. $chars = $this->_hexToIntArray($data); $charCount = count($chars); // Repeat the key as many times as necessary to ensure that the key is at least as long as the input. for ($i = 0; $i < $charCount; $i = strlen($tmp)) { $tmp = $tmp . $tmp; } // Get the XOR values between the ASCII values of the input and key characters for all input offsets. for ($i = 0; $i < $charCount; $i++) { $decrypted .= chr($chars[$i] ^ ord($tmp[$i])); } return $decrypted; } /** * Method to encrypt a data string. * * NOTE - this is the old deprecated J! simple crypt, only here for legacy (converting old to new) * * @param string $data The data string to encrypt. * @param object $key The key[/pair] object to use for encryption. * * @return string The encrypted data string. * * @since 12.1 * @throws \InvalidArgumentException */ public function oldEncrypt($data, $key) { // Validate key. if ($key->type != 'simple') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected simple.'); } $encrypted = ''; $tmp = $key->private; // Split up the input into a character array and get the number of characters. $chars = preg_split('//', $data, -1, PREG_SPLIT_NO_EMPTY); $charCount = count($chars); // Repeat the key as many times as necessary to ensure that the key is at least as long as the input. for ($i = 0; $i < $charCount; $i = strlen($tmp)) { $tmp = $tmp . $tmp; } // Get the XOR values between the ASCII values of the input and key characters for all input offsets. for ($i = 0; $i < $charCount; $i++) { $encrypted .= $this->_intToHex(ord($tmp[$i]) ^ ord($chars[$i])); } return $encrypted; } /** * Method to generate a new encryption key[/pair] object. * * @param array $options Key generation options. * * @return Key * * @since 12.1 */ public function oldKey(array $options = array()) { // Create the new encryption key[/pair] object. $key = new \stdClass(); // Just a random key of a given length. $key->type = 'simple'; $key->private = \JFactory::getConfig()->get('secret'); $key->public = $key->private; return $key; } /** * Convert hex to an integer * * @param string $s The hex string to convert. * @param integer $i The offset? * * @return integer * * @since 11.1 */ private function _hexToInt($s, $i) { $j = (int) $i * 2; $k = 0; $s1 = (string) $s; // Get the character at position $j. $c = substr($s1, $j, 1); // Get the character at position $j + 1. $c1 = substr($s1, $j + 1, 1); switch ($c) { case 'A': $k += 160; break; case 'B': $k += 176; break; case 'C': $k += 192; break; case 'D': $k += 208; break; case 'E': $k += 224; break; case 'F': $k += 240; break; case ' ': $k += 0; break; default: (int) $k = $k + (16 * (int) $c); break; } switch ($c1) { case 'A': $k += 10; break; case 'B': $k += 11; break; case 'C': $k += 12; break; case 'D': $k += 13; break; case 'E': $k += 14; break; case 'F': $k += 15; break; case ' ': $k += 0; break; default: $k += (int) $c1; break; } return $k; } /** * Convert hex to an array of integers * * @param string $hex The hex string to convert to an integer array. * * @return array An array of integers. * * @since 11.1 */ private function _hexToIntArray($hex) { $array = array(); $j = (int) strlen($hex) / 2; for ($i = 0; $i < $j; $i++) { $array[$i] = (int) $this->_hexToInt($hex, $i); } return $array; } /** * Convert an integer to a hexadecimal string. * * @param integer $i An integer value to convert to a hex string. * * @return string * * @since 11.1 */ private function _intToHex($i) { // Sanitize the input. $i = (int) $i; // Get the first character of the hexadecimal string if there is one. $j = (int) ($i / 16); if ($j === 0) { $s = ' '; } else { $s = strtoupper(dechex($j)); } // Get the second character of the hexadecimal string. $k = $i - $j * 16; $s = $s . strtoupper(dechex($k)); return $s; } } Helpers/StringHelper.php 0000604 00000064543 15172625755 0011314 0 ustar 00 <?php /** * String helpers * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use stdClass; /** * String helpers * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class StringHelper extends \Joomla\String\StringHelper { /** * UTF-8 aware - replace the first word * * @param string $str The string to be trimmed * @param string $word The word to trim * @param string $whitespace Ignore but preserve leading whitespace * * @return string the trimmed string */ public static function ltrimword($str, $word = false, $whitespace = false) { if ($word === false) { return $str; } if ($whitespace) { $word = preg_quote($word, '#'); $str = preg_replace("#^(\s*)($word)(.*)#i", "$1$3", $str); } else { $pos = StringHelper::strpos($str, $word); if ($pos === 0) { $str = StringHelper::substr($str, StringHelper::strlen($word)); } } return $str; } /** * Right trim a word from a string * * @param string &$str The string to be trimmed * @param string $word The word to trim * * @return string the trimmed string */ public static function rtrimword(&$str, $word = false) { $l = StringHelper::strlen($word); $end = StringHelper::substr($str, -$l); if ($end === $word) { return StringHelper::substr($str, 0, StringHelper::strlen($str) - $l); } else { return $str; } } /** * UTF-8 aware - remove the first word * CASE INSENSITIVE * * @param string $str The string to be trimmed * @param string $word The word to trim * * @return string the trimmed string */ public static function ltrimiword($str, $word = false) { $pos = stripos($str, $word); if ($pos === 0) { $str = StringHelper::substr($str, StringHelper::strlen($word)); } return $str; } /** * Formats a string to return a safe db col name e.g. * table.field is returned as `table`.`field` * table is return as `table` * * @param string $col Col name to format * * @return string in `table`.field` format */ public static function safeColName($col) { $db = Worker::getDbo(); $col = str_replace('`', '', $col); $splitter = ''; if (strstr($col, '___')) { $splitter = '___'; } if (strstr($col, '.')) { $splitter = '.'; } if ($splitter == '') { return $db->quoteName($col); } if (strstr($col, $splitter)) { $col = explode($splitter, $col); foreach ($col as &$c) { $c = $db->quoteName($c); } return implode('.', $col); } return $col; } /** * Inverse of safeColName takes `table`.`field` * and returns table___field * * @param string $col In `table`.`field` format * * @return string in table___field format */ public static function safeColNameToArrayKey($col) { $col = str_replace(array('`.`', '.'), '___', $col); $col = str_replace('`', '', $col); return $col; } /** * Takes tablename.element or tablename___elementname * (with or without quotes) and returns elementname * * @param string $col Column name to shorten * * @return string element name */ public static function shortColName($col) { if (strstr($col, '.')) { $bits = explode('.', $col); $col = array_pop($bits); } elseif (strstr($col, '___')) { $bits = explode('___', $col); $col = array_pop($bits); } $col = str_replace('`', '', $col); return $col; } /** * Get a shortened version of the element label - so that the admin pages * don't get too stretched when we populate drop-downs with the label * * @param string $label Complete element label * * @return string shortened element label */ public static function getShortDdLabel($label) { $label = strip_tags($label); preg_replace('/<[a-z][a-z0-9]*[^<>]*>/', '', $label); if (StringHelper::strlen($label) > 50) { $label = StringHelper::substr($label, 0, 47) . '...'; } $label = trim($label); return $label; } /** * Sanitize db fields names, can't just do regex on A-Z as languages like Chinese should be allowed * * @param string $str Field name * * @since 3.0.7 * * @return string */ public static function dbFieldName($str) { $name = \JFilterInput::getInstance()->clean($str, 'CMD'); // Chinese characters? if ($name === '') { $name = str_replace(array(' ', '.', '-'), '', $str); } return $name; } /** * Is it a raw element name, i.e. ends in _raw * * @param string $str Element name * * @since 3.3 * * @return bool */ public static function isRawName($str) { return substr($str, -4, 4) == '_raw'; } /** * Strip _raw off the end * * @param string $str Element name * * @since 3.3 * * @return bool */ public static function stripRawName($str) { return self::rtrimword($str, '_raw'); } /** * Clean variable names for use as fabrik element names * whitespace compressed and replaced with '_' * replace all non-alphanumeric chars except _ and - with '_' * 28/06/2011 replaces umlauts with eu * 22/11/2011 added IGNORE to default enc otherwise iconv chops everything after first unconvertable char * 05/02/2012 changed name to iclean, removed strtolower() and added clean() as wrapper that does strtolower * * @param string $str To clean * @param string $fromEnc From encoding * @param string $toEnc To encoding * * @return string cleaned */ public static function iclean($str, $fromEnc = "UTF-8", $toEnc = "ASCII//IGNORE//TRANSLIT") { // Replace umlauts $out = ''; for ($i = 0; $i < StringHelper::strlen($str); $i++) { $ch = ord($str{$i}); switch ($ch) { case 195: $out .= ''; break; case 164: $out .= 'ae'; break; case 188: $out .= 'ue'; break; case 182: $out .= 'oe'; break; case 132: $out .= 'Ae'; break; case 156: $out .= 'Ue'; break; case 150: $out .= 'Oe'; break; // Fix for cleaning value of 1 case 0: $out = '1'; break; default: $out .= chr($ch); } } $str = $out; if (function_exists('iconv')) { /* $$$ rob added @ in case its farsi which creates a notice: * https://github.com/Fabrik/fabrik/issues/72 */ // Replace accented characters with ascii equivalent e.g. é => e $str1 = (@iconv($fromEnc, $toEnc, $str)); if ($str1) { $str = $str1; } $str = (str_replace("'", '', $str)); } // Compress internal whitespace and replace with _ $str = preg_replace('/\s+/', '_', $str); // Replace all non-alphanumeric chars except _ and - with '_' return preg_replace('/\W+/', '_', $str); } /** * Wrapper for iclean(), that does strtolower on output of clean() * * @param string $str To clean * @param string $fromEnc From encoding * @param string $toEnc To encoding * * @return string cleaned */ public static function clean($str, $fromEnc = "UTF-8", $toEnc = "ASCII//IGNORE//TRANSLIT") { return StringHelper::strtolower(self::iclean($str, $fromEnc, $toEnc)); } /** * truncateHtml can truncate a string up to a number of characters while preserving whole words and HTML tags * * (ripped off from Cake PHP framework) * * @param string $text String to truncate. * @param integer $length Length of returned string, including ellipsis. * @param string $ending Ending to be appended to the trimmed string. * @param boolean $exact If false, $text will not be cut mid-word * @param boolean $considerHtml If true, HTML tags would be handled correctly * * @return string Trimmed string. */ public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) { if ($considerHtml) { // If the plain text is shorter than the maximum length, return the whole text if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { return $text; } // Splits all html-tags to scanable lines preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER); $totalLength = strlen($ending); $open_tags = array(); $truncate = ''; foreach ($lines as $lineMatchings) { // If there is any html-tag in this line, handle it and add it (uncounted) to the output if (!empty($lineMatchings[1])) { // If it's an "empty element" with or without xhtml-conform closing slash if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $lineMatchings[1])) { // Do nothing if tag is a closing tag } else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $lineMatchings[1], $tagMatchings)) { // Delete tag from $open_tags list $pos = array_search($tagMatchings[1], $open_tags); if ($pos !== false) { unset($open_tags[$pos]); } // If tag is an opening tag } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $lineMatchings[1], $tagMatchings)) { // Add tag to the beginning of $open_tags list array_unshift($open_tags, strtolower($tagMatchings[1])); } // Add html-tag to $truncate'd text $truncate .= $lineMatchings[1]; } // Calculate the length of the plain text part of the line; handle entities as one character $contentLength = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $lineMatchings[2])); if ($totalLength+$contentLength> $length) { // The number of characters which are left $left = $length - $totalLength; $entitiesLength = 0; // Search for html entities if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $lineMatchings[2], $entities, PREG_OFFSET_CAPTURE)) { // Calculate the real length of all entities in the legal range foreach ($entities[0] as $entity) { if ($entity[1]+1-$entitiesLength <= $left) { $left--; $entitiesLength += strlen($entity[0]); } else { // No more characters left break; } } } $truncate .= substr($lineMatchings[2], 0, $left + $entitiesLength); // Maximum length is reached, so get off the loop break; } else { $truncate .= $lineMatchings[2]; $totalLength += $contentLength; } // If the maximum length is reached, get off the loop if($totalLength>= $length) { break; } } } else { if (strlen($text) <= $length) { return $text; } else { $truncate = substr($text, 0, $length - strlen($ending)); } } // If the words shouldn't be cut in the middle... if (!$exact) { // ...search the last occurrence of a space... $spacePos = strrpos($truncate, ' '); if (isset($spacePos)) { // ...and cut the text in this position $truncate = substr($truncate, 0, $spacePos); } } // add the defined ending to the text $truncate .= $ending; if($considerHtml) { // Close all unclosed html-tags foreach ($open_tags as $tag) { $truncate .= '</' . $tag . '>'; } } return $truncate; } /** * Truncate text possibly setting a tip to show all of the text * * @param string $text text to truncate * @param array $opts optional options array * * @return string */ public static function truncate($text, $opts = array()) { $origText = $text; $wordCount = ArrayHelper::getValue($opts, 'wordcount', 10); $showTip = ArrayHelper::getValue($opts, 'tip', true); $title = ArrayHelper::getValue($opts, 'title', ''); $strippedText = htmlspecialchars(strip_tags($text), ENT_QUOTES);; if (ArrayHelper::getValue($opts, 'html_format', false)) { $summary = self::truncateHtml($text, $wordCount); } else if (ArrayHelper::getValue($opts, 'chars', false)) { if (!empty($wordCount)) { $summary = mb_strimwidth($text, 0, $wordCount, "..."); } else { $summary = $text; } } else { $text = htmlspecialchars(strip_tags($text), ENT_QUOTES); $text = explode(' ', $text); $summary = array_slice($text, 0, $wordCount); if (count($text) > $wordCount) { $summary[] = " ..."; } $summary = implode(' ', $summary); } if ($showTip && $origText != $summary) { Html::tips(); if ($title !== '') { $title .= "::"; } $tip = htmlspecialchars('<div class="truncate_text">' . $title . $strippedText . '</div>'); $jOpts = new stdClass; $jOpts->notice = true; $jOpts->position = ArrayHelper::getValue($opts, 'position', 'top'); $jOpts = json_encode($jOpts); $summary = '<span class="fabrikTip" opts=\'' . $jOpts . '\' title="' . $tip . '">' . $summary . '</span>'; } return $summary; } /** * Removes a querystring key from a url/querystring * * @param string $url Or querystring * @param string $key To remove * * @return string url/querystring */ public static function removeQSVar($url, $key) { $pair = explode('?', $url); if (count($pair) === 2) { $url = $pair[0]; $bits = ArrayHelper::getValue($pair, 1); } else { $url = ''; $bits = ArrayHelper::getValue($pair, 0); } $glue = strstr($bits, '&') ? '&' : '&'; $bits = explode($glue, $bits); $a = array(); foreach ($bits as $bit) { if (strstr($bit, '=')) { list($thisKey, $val) = explode('=', $bit); if ($thisKey !== $key) { $a[] = $bit; } } } if (!empty($a)) { $url .= '?' . implode($glue, $a); } return $url; } /** * Encode a query string (that already has &s in it) * * @param $qs * * @return string */ public static function encodeqs($qs) { if (empty($qs)) { return ''; } $new_qs = array(); foreach (explode('&', $qs) as $arg) { $bits = explode('=', $arg); $key = ArrayHelper::getValue($bits, 0, ''); $val = ArrayHelper::getValue($bits, 1, ''); $new_qs[] = $key . '=' . urlencode($val); } return implode('&', $new_qs); } /** * Takes a complete URL, and urlencodes any query string args * * @param string $url To encode * * @return encoded url */ public static function encodeurl($url) { if (strstr($url, '?')) { list($site, $qs) = explode('?', $url); if (!empty($qs)) { $url = $site . '?' . self::encodeqs($qs); } } if (strstr($url, '{')) { /* $$$ hugh special case for some Google URL's that use encoded JSON objects in the path part of the URL * so we need to re-encode {, }, " and :. Except of course for the : in http(s):. */ list($http, $rest) = explode(':', $url, 2); if (!empty($rest)) { $patterns = array('#\{#', '#\}#', '#"#', '#\\\\#', '#:#'); $replacements = array('%7B', '%7D', '%22', '%5C', '%3A'); $rest = preg_replace($patterns, $replacements, $rest); $url = $http . ':' . $rest; } } return $url; } /** * Prepare a string for presentation in html. * * @param string &$string To convert for html * * @return void */ public static function forHtml(&$string) { // Special chars such as <> $string = htmlspecialchars($string, ENT_QUOTES); // Show umlauts correctly in ajax error messages. $string = mb_convert_encoding($string, 'HTML-ENTITIES', "UTF-8"); } /** * See if it looks like a string uses {table___element} placeholders * Doesn't do any sanity testing to see if it's actually a valid element * name, just goes by pattern patching word___word * * @param string $str String to test * * @return bool * * @since 3.0.1 */ public static function usesElementPlaceholders($str) { return preg_match("#\{\w+___\w+\}#", $str); } /** * Convert standard Fabrik coords string into lat, long, zoom object. * Copied from map element, as we end up needing this elsewhere. * * @param string $v coordinates * @param int $zoomLevel default zoom level * * @return object coords array and zoom level int */ public static function mapStrToCoords($v, $zoomLevel = 4) { $o = new stdClass; $o->coords = array('', ''); $o->zoomlevel = (int) $zoomLevel; if (strstr($v, ',')) { $ar = explode(':', $v); $o->zoomlevel = count($ar) == 2 ? array_pop($ar) : $zoomLevel; $v = self::ltrimword($ar[0], '('); $v = rtrim($v, ')'); $v = str_replace(' ', '', $v); $o->coords = explode(',', $v); } else { $o->coords = array(0, 0); } // $$$ hugh - added these as I always think it's what they are! $o->lat = $o->coords[0]; $o->long = $o->coords[1]; $o->zoom = $o->zoomlevel; return $o; } /** * Covert HEX colour to RGB colour * * @param string $hex HEX colour string * * @return string RGB string */ public static function hex2rgb($hex) { $hex = str_replace('#', '', $hex); if (strlen($hex) === 3) { $r = hexdec(substr($hex, 0, 1) . substr($hex, 0, 1)); $g = hexdec(substr($hex, 1, 1) . substr($hex, 1, 1)); $b = hexdec(substr($hex, 2, 1) . substr($hex, 2, 1)); } else { $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); } return $r . ',' . $g . ',' . $b; } /** * Translator JText wrapper - removes tags and compares raw text * so "<p>STRING_TO_TRANSLATE</p>" is translated even if wrapped in a <p> tag. * * @param string $text Text to translate * * @return string */ public static function translate($text) { $plain = strip_tags($text); $translated = Text::_($plain); if ($translated !== $plain) { $text = str_replace($plain, $translated, $text); } return $text; } /** * Is the string a CONCAT statement? * * @param string $text Text to test * * @return bool */ public static function isConcat($text) { return preg_match('/^\s*(CONCAT|CONCAT_WS)\b/i', preg_quote($text)); } /** * Strip whitespace (or only spaces) from a string * * @param string $text Text to strip * @param bool $only_spaces If true, only strip spaces (not tabs, etc), default is false * * @return string */ public static function stripSpace($text, $only_spaces = false) { if ($only_spaces) { return str_replace(' ', '', $text); } else { return preg_replace('#\s+#', '', $text); } } /** * See if date string is a valid date in MySQL format. * * NOTE - I could have sworn we had a function somewhere to do this, but I can't find it! * Needed it in the main system plugin, for handling J! search plugin dates, as J! * will pitch a fatal error if we pass it an invalid date string. So if there is * already a way of doing this, feel free to dump this func and modify the system plugin * in onDoContentSearch(). * * @param bool $time_optional if set to true, the time part is optional * * @return bool */ public static function isMySQLDate($date, $time_optional = false) { $date_re = '(((\d{4})(-)(0[13578]|10|12)(-)(0[1-9]|[12][0-9]|3[01]))|((\d{4})(-)(0[469]|1??1)(-)([0][1-9]|[12][0-9]|30))|'; $date_re .= '((\d{4})(-)(02)(-)(0[1-9]|1[0-9]|2[0-8]))|(([02468]??[048]00)(-)(02)(-)(29))|(([13579][26]00)(-)(02)(-)(29))|'; $date_re .= '(([0-9][0-9][0][48])(-)(0??2)(-)(29))|(([0-9][0-9][2468][048])(-)(02)(-)(29))|(([0-9][0-9][13579][26])(-)(02??)(-)(29)))'; $time_re = '(\s([0-1][0-9]|2[0-4]):([0-5][0-9]):([0-5][0-9]))'; if ($time_optional) { return preg_match("#^" . $date_re . "$#", $date) || preg_match("#^" . $date_re . $time_re . "$#", $date); } else { return preg_match("#^" . $date_re . $time_re . "$#", $date); } } /** * Replace last occurrence of a string * * @param string $search Text to search for * @param string $replace Text to replace the search string * @param string $subject The text to search in * * @return string */ public static function replaceLast($search, $replace, $subject) { $pos = strripos($subject, $search); if ($pos !== false) { $subject = substr_replace($subject, $replace, $pos, strlen($search)); } return $subject; } /** * DB value quote a single string or an array of strings, first checking to see if they are * already quoted. Which the J! $db->quote() doesn't do, unfortunately. * Does NOT modify the input. Does not quote if value starts with SELECT. * * @param unknown $values * @param bool $commaSeparated individually quote a comma separated string of values * * @return mixed quoted values */ public static function safeQuote($values, $commaSeparated = true) { $values2 = $values; if ($commaSeparated) { $values2 = explode(',', $values2); } if (is_array($values2)) { foreach ($values2 as &$v) { $v = self::safeQuoteOne($v); } } else { $values2 = self::safeQuoteOne($values2); } if ($commaSeparated) { $values2 = implode(',', $values2); } return $values2; } /** * Return DB value quoted single string. Does not quote if value starts with SELECT, * or if value is already single quoted. * * @param string $value * * @return mixed quoted values */ public static function safeQuoteOne($value) { $value = trim($value); if (is_string($value) && !preg_match('/^\s*SELECT\s+/i', $value)) { if (!preg_match("#^'.*'$#", $value)) { $db = \JFactory::getDbo(); $value = $db->quote($value); } } return $value; } /** * Wrapper for safeQuoteName because I'm a dumbass and got my mords wuddled when I created * the safeNameQuote() function. * * @param unknown $values * @param bool $commaSeparated individually quote a comma separated string of values * * @return mixed quoted values */ public static function safeQuoteName($values, $commaSeparated = true) { return self::safeNameQuote($values, $commaSeparated); } /** * DB name quote a single string or an array of strings, first checking to see if they are * already quoted. Which the J! $db->quote() doesn't do, unfortunately. * Does NOT modify the input. Does not quote if value starts with CONCAT. * * @param string|array $values * @param bool $commaSeparated individually quote a comma separated string of values * * @return mixed quoted values */ public static function safeNameQuote($values, $commaSeparated = true) { $values2 = $values; if ($commaSeparated) { $values2 = explode(',', $values2); } if (is_array($values2)) { foreach ($values2 as &$v) { $v = self::safeNameQuoteOne($v); } } else { $values2 = self::safeNameQuoteOne($values2); } if ($commaSeparated) { $values2 = implode(',', $values2); } return $values2; } /** * Return DB value quoted single string. Does not quote if value starts with SELECT, * or if value is already single quoted. * * @param string $value * * @return mixed quoted values */ public static function safeNameQuoteOne($value) { $value = trim($value); if (is_string($value) && !preg_match('/^\s*(CONCAT|CONCAT_WS)\s*\(/i', $value)) { if (!preg_match("#^`.*`$#", $value)) { $db = \JFactory::getDbo(); $value = $db->quoteName($value); } } return $value; } /** * Return appropriate query string sepchar * * @param string $url * * @return string query string sepchar */ public static function qsSepChar($url) { if (strstr($url, '?')) { if (substr($url, -1) === '?') { return ''; } else { return '&'; } } else { return '?'; } } /** * Get a validated server remote address (I.P.). If not valid return '' * * @return string */ public static function filteredIp() { return filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) !== false ? $_SERVER['REMOTE_ADDR'] : ''; } public static function getRowClass($value, $prefix) { $value = preg_replace('/[^A-Z|a-z|0-9]/', '-', $value); $value = self::ltrim($value, '-'); $value = self::rtrim($value, '-'); // $$$ rob 24/02/2011 can't have numeric class names so prefix with element name // $$$ hugh can't have class names which start with a number, so need preg_match, not is_numeric() if (preg_match('#^\d#', $value)) { $value = $prefix . $value; } else { // 12/10/2016 - for consistency, start adding the prefixed version every time $value .= " " . $prefix . $value; } return $value; } /** * Apply nl2br only outside of chevroned tags, eq. not between <...> * * @param $string * * @return mixed */ public static function safeNl2br($string) { // https://stackoverflow.com/questions/4603780/preg-replace-only-outside-tags-were-not-talking-full-html-parsing-jus $parts = preg_split('/(<(?:[^"\'>]|"[^"<]*"|\'[^\'<]*\')*>)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE); for ($i=0, $n=count($parts); $i<$n; $i+=2) { $parts[$i] = nl2br($parts[$i]); } return implode('', $parts); } /** * Bitlify a link * * @param string $link the link to shorten * @param string $username Bitly username * @param string $apikey Bitly API key * @param string $encode urlencode */ public static function bitlify ($link, $login, $apikey, $encode = true) { if (!strstr($link, 'bit.ly/') && $link !== '') { require_once JPATH_SITE . '/components/com_fabrik/libs/bitly/bitly.php'; $bitly = new \bitly($login, $apikey); if ($encode) { $link = self::encodeurl($link); } $link = (string) $bitly->shorten($link); } return $link; } } Helpers/Image/Imagegd.php 0000604 00000025711 15172625755 0011257 0 ustar 00 <?php /** * @package Joomla * @subpackage Fabrik.image * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers\Image; defined('_JEXEC') or die('Restricted access'); use \JHtml; use \JFactory; use \JFile; use \Error; use \Fabrik\Helpers\StringHelper; /** * GD image manipulation class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html * @since 1.0 */ class Imagegd extends Image { /** * Create an image object from a file path * * @param string $file file to create image from * * @throws \Exception * * @return array (image, header string) */ public function imageFromFile($file) { $fromFile = $this->storage->preRenderPath($file); // Load image $img = null; $ext = $this->getImgType($fromFile); if (!$ext) { return array(false, false); } ini_set('display_errors', true); $memory = \FabrikWorker::getMemoryLimit(true); $intMemory = \FabrikWorker::getMemoryLimit(); if ($intMemory < (64 * 1024 * 1024)) { ini_set('memory_limit', '50M'); } if ($ext == 'jpg' || $ext == 'jpeg') { $img = @imagecreatefromjpeg($fromFile); $header = "image/jpeg"; } elseif ($ext == 'png') { $img = @imagecreatefrompng($fromFile); $header = "image/png"; // Only if your version of GD includes GIF support } elseif ($ext == 'gif') { if (function_exists('imagecreatefromgif')) { $img = @imagecreatefromgif($fromFile); $header = "image/gif"; } else { throw new \Exception("imagecreate from gif not available"); } } if ($intMemory < (64 * 1024 * 1024)) { ini_set('memory_limit', $memory); } return array($img, $header); } /** * Create a gd image from a file * * @param string $source path to file * * @return image */ protected function imageCreateFrom($source) { $ext = StringHelper::strtolower(JFile::getExt($source)); switch ($ext) { case 'jpg': case 'jpeg': $source = imagecreatefromjpeg($source); break; case 'png': $source = imagecreatefrompng($source); break; case 'gif': $source = imagecreatefromgif($source); break; } return $source; } /** * Convert an image object into a file * * @param string $destCropFile file path * @param object $image image object to save * * @return bool True on success */ public function imageToFile($destCropFile, $image) { $ext = StringHelper::strtolower(JFile::getExt($destCropFile)); ob_start(); switch ($ext) { case 'jpg': case 'jpeg': $source = imagejpeg($image, null, 100); break; case 'png': $source = imagepng($image, null); break; case 'gif': $source = imagegif($image, null); break; } $image_p = ob_get_contents(); ob_end_clean(); return $this->storage->write($destCropFile, $image_p); } /** * Rotate an image * * @param string $source filepath * @param string $dest output path, if empty defaults to source * @param int $degrees number of degrees to rotate * * @return array (image object, rotated images width, rotated images height) */ public function rotate($source, $dest = '', $degrees = 0) { if (empty($dest)) { $dest = $source; } $source = $this->imageCreateFrom($source); $app = JFactory::getApplication(); // Rotates the image $rotate = imagerotate($source, $degrees, 0); if ($rotate === false) { $app->enqueueMessage('Image rotation failed', 'notice'); } $this->imageToFile($dest, $rotate); list($width, $height) = getimagesize($dest); return array($rotate, $width, $height); } /* * Check for EXIF orientation data, and rotate image accordingly * * @param string path to image file */ public function rotateImageFromExif($src, $dest) { if (function_exists('exif_read_data')) { $exif = exif_read_data($src); if ($exif && isset($exif['Orientation'])) { $orientation = $exif['Orientation']; if ($orientation != 1) { $deg = 0; switch ($orientation) { case 3: $deg = 180; break; case 6: $deg = 270; break; case 8: $deg = 90; break; } if ($deg) { self::rotate($src, $dest, $deg); } } } } } /** * Scale an image * * @param string $file file to scale * @param string $dest save location * @param int $percentage scale percentage * @param int $destX start scale from x coord * @param int $destY start scale from y coord * * @return object image */ public function scale($file, $dest = '', $percentage = 100, $destX = 0, $destY = 0) { list($image, $header) = $this->imageFromFile($file); jimport('joomla.filesystem.file'); list($width, $height) = getimagesize($file); $new_width = $width * ((float) $percentage / 100); $new_height = $height * ((float) $percentage / 100); $image_p = imagecreatetruecolor($new_width, $new_height); imagecopyresampled($image_p, $image, $destX, $destY, 0, 0, $new_width, $new_height, $width, $height); if ($dest != '') { $this->imageToFile($dest, $image_p); } return $image_p; } /** * Resize an image to a specific width/height * * @param int $maxWidth maximum image Width (px) * @param int $maxHeight maximum image Height (px) * @param string $origFile current images folder path (must have trailing end slash) * @param string $destFile destination folder path for resized image (must have trailing end slash) * @param int $quality Percentage image save quality 100 = no compression, 0 = max compression * * @throws Error * * @return object image */ public function resize($maxWidth, $maxHeight, $origFile, $destFile, $quality = 100) { // Check if the file exists if (!$this->storage->exists($origFile)) { throw new RuntimeException("Fabrik: no file found for $origFile"); } // Load image list($img, $header) = $this->imageFromFile($origFile); if (!$img) { return $img; } $ext = StringHelper::strtolower(end(explode('.', $origFile))); // If an image was successfully loaded, test the image for size if ($img) { // Handle image transparency for original image if (function_exists('imagealphablending')) { imagealphablending($img, false); imagesavealpha($img, true); } // Get image size and scale ratio $width = imagesx($img); $height = imagesy($img); $scale = min($maxWidth / $width, $maxHeight / $height); // If the image is larger than the max shrink it if ($scale < 1) { $new_width = floor($scale * $width); $new_height = floor($scale * $height); // Create a new temporary image $tmp_img = imagecreatetruecolor($new_width, $new_height); // Handle image transparency for resized image if (function_exists('imagealphablending')) { imagealphablending($tmp_img, false); imagesavealpha($tmp_img, true); } // Copy and resize old image into new image imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height); imagedestroy($img); $img = $tmp_img; } } // Create error image if necessary if (!$img) { throw new Error("resize: no image created for $origFile, extension = $ext, destination = $destFile"); } // Save the file $this->writeImg($img, $destFile, $header, $quality); $this->thumbPath = $destFile; } /** * Crop an image to specific dimensions * * @param string $origFile path to image to crop from * @param string $destFile path to cropped file * @param int $srcX x coord on $origFile to start crop from * @param int $srcY y coord on $origFile to start crop from * @param int $dstW cropped image width * @param int $dstH cropped image height * @param int $dstX destination x coord of destination point * @param int $dstY destination y coord of destination point * @param string $bg hex background colour * * @return void */ public function crop($origFile, $destFile, $srcX, $srcY, $dstW, $dstH, $dstX = 0, $dstY = 0, $bg = '#FFFFFF') { // Convert hex to rgb colours. list($r, $g, $b) = sscanf($bg, '#%2x%2x%2x'); list($origImg, $header) = $this->imageFromFile($origFile); $destImg = imagecreatetruecolor($dstW, $dstH); $bg = imagecolorallocate($destImg, $r, $g, $b); // Draw a bg rectangle imagefilledrectangle($destImg, 0, 0, (int) $dstW, (int) $dstH, $bg); $this->writeImg($destImg, $destFile, $header); $srcW = imagesx($destImg); $srcH = imagesy($destImg); $origW = imagesx($origImg); $origH = imagesy($origImg); // If the orig image is smaller than the destination then increase its canvas and fill it with the bg if ($origW < $srcW || $origH < $srcH) { $srcBg = imagecreatetruecolor($srcW, $srcH); imagefilledrectangle($srcBg, 0, 0, (int) $srcW, (int) $srcH, $bg); imagecopyresampled($srcBg, $origImg, 0, 0, 0, 0, $origW, $origH, $origW, $origH); $origImg = $srcBg; } imagecopyresampled($destImg, $origImg, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH); $this->writeImg($destImg, $destFile, $header); } /** * Write an image to the server * * @param object $img image object * @param string $destFile file path to save to * @param string $header image type * @param int $quality Percentage image save quality 100 = no compression, 0 = max compression * * @throws Error * * @return void */ public function writeImg($img, $destFile, $header, $quality = 100) { if ($quality < 0) { $quality = 0; } if ($quality > 100) { $quality = 100; } if ($header == "image/jpeg") { ob_start(); imagejpeg($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { if ($header == "image/png") { $quality = round((100 - $quality) * 9 / 100); ob_start(); imagepng($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { if (function_exists("imagegif")) { ob_start(); imagegif($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { throw new Error('trying to save a gif by imagegif support not present in the GD library'); } } } } } Helpers/Image/Imagegd2.php 0000604 00000010703 15172625755 0011334 0 ustar 00 <?php /** * @package Joomla * @subpackage Fabrik.image * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers\Image; defined('_JEXEC') or die('Restricted access'); use \JHtml; use \JFactory; use \RuntimeException; use \Fabrik\Helpers\StringHelper; /** * GD2 image manipulation class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html * @since 1.0 */ class Imagegd2 extends Imagegd { /** * Resize an image to a specific width/height * * @param int $maxWidth maximum image Width (px) * @param int $maxHeight maximum image Height (px) * @param string $origFile current images folder path (must have trailing end slash) * @param string $destFile destination folder path for resized image (must have trailing end slash) * @param int $quality Percentage image save quality 100 = no compression, 0 = max compression * * @return object image */ public function resize($maxWidth, $maxHeight, $origFile, $destFile, $quality = 100) { $app = JFactory::getApplication(); // Check if the file exists if (!$this->storage->exists($origFile)) { throw new RuntimeException("no file found for $origFile"); } $fromFile = $this->storage->preRenderPath($origFile); // Load image $img = null; $ext = $this->getImgType($fromFile); if (!$ext) { return; } ini_set('display_errors', true); $memory = \FabrikWorker::getMemoryLimit(true); $intMemory = \FabrikWorker::getMemoryLimit(); if ($intMemory < (64 * 1024 * 1024)) { ini_set('memory_limit', '50M'); } if ($ext == 'jpg' || $ext == 'jpeg') { $img = @imagecreatefromjpeg($fromFile); $header = "image/jpeg"; } elseif ($ext == 'png') { $img = @imagecreatefrompng($fromFile); $header = "image/png"; // Only if your version of GD includes GIF support } elseif ($ext == 'gif') { if (function_exists('imagecreatefromgif')) { $img = @imagecreatefromgif($fromFile); $header = "image/gif"; } else { $app->enqueueMessage("imagecreate from gif not available"); } } // If an image was successfully loaded, test the image for size if ($img) { // Handle image transparency for original image if (function_exists('imagealphablending')) { imagealphablending($img, false); imagesavealpha($img, true); } // Get image size and scale ratio $width = imagesx($img); $height = imagesy($img); $scale = min($maxWidth / $width, $maxHeight / $height); // If the image is larger than the max shrink it if ($scale < 1) { $new_width = floor($scale * $width); $new_height = floor($scale * $height); // Create a new temporary image $tmp_img = imagecreatetruecolor($new_width, $new_height); // Copy and resize old image into new image imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height); imagedestroy($img); $img = $tmp_img; } } if (!$img) { $app->enqueueMessage("no image created for $origFile, extension = $ext , destination = $destFile "); } /* save the file * write them out to output buffer first so that we can use JFile to write them to the server (potential using J ftp layer) */ if ($header == "image/jpeg") { ob_start(); imagejpeg($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { if ($header == "image/png") { ob_start(); $quality = round((100 - $quality) * 9 / 100); imagepng($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { if (function_exists("imagegif")) { ob_start(); imagegif($img, null, $quality); $image = ob_get_contents(); ob_end_clean(); $this->storage->write($destFile, $image); } else { $app->enqueueMessage("GD gif support not available: could not resize image"); } } } $this->thumbPath = $destFile; if ($intMemory < (64 * 1024 * 1024)) { ini_set('memory_limit', $memory); } } } Helpers/Image/Image.php 0000604 00000017343 15172625755 0010746 0 ustar 00 <?php /** * @package Joomla * @subpackage Fabrik.image * @copyright Copyright (C) 2005-2016 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers\Image; use Fabrik\Helpers\ArrayHelper; use Fabrik\Helpers\StringHelper; use \JFactory; use \JFile; use \JFolder; /** * Base image manipulation class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005 Fabrik. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @since 1.0 */ class Image { /** * Thumbnail image path * * @var string */ protected $thumbPath = null; /** * Storage class file/amazons3 etc. * * @var object */ public $storage = null; /** * Set the filesystem storage manager * * @param object &$storage storage object * * @return void */ public function setStorage(&$storage) { $this->storage = $storage; } /** * Get the image type git/jpg/png * * @param string $filename image file path * * @return string */ public function getImgType($filename) { $info = getimagesize($filename); switch ($info[2]) { case 1: return 'gif'; break; case 2: return 'jpg'; break; case 3: return 'png'; break; default: $pathInfo = pathInfo($filename); if (ArrayHelper::getValue($pathInfo, 'extension', '') === 'pdf') { return 'pdf'; }; return false; break; } } /** * Resize an image to a specific width/height * * @param int $maxWidth maximum image Width (px) * @param int $maxHeight maximum image Height (px) * @param string $origFile current images folder path (must have trailing end slash) * @param string $destFile destination folder path for resized image (must have trailing end slash) * @param int $quality Percentage image save quality 100 = no compression, 0 = max compression * * @return object image */ public function resize($maxWidth, $maxHeight, $origFile, $destFile, $quality = 100) { } /** * Grab an image from a remote URI and store in cache, then serve cached image * * @param string $src Remote URI to image * @param string $path Local folder to store the image in e.g. 'cache/com_fabrik/images' * @param string $file Local filename * @param integer $lifeTime Number of days to cache the image for * * @return boolean|string Local URI to cached image */ public static function cacheRemote($src, $path, $file, $lifeTime = 29) { /** * $$$ @FIXME we may need to find something other than file_get_contents($src) * to use for this, as it requires allow_url_fopen to be enabled in PHP to fetch a URL, * which a lot of shared hosts don't allow. * * -Rob - well JFile::read is deprecated and in the code it says to use file_get_contents * The Joomla updater won't work with out file_get_contents so I think we should make it a requirement * Wiki updated here - http://fabrikar.com/forums/index.php?wiki/prerequisites/ * * hugh - Okie Dokie. */ /** * $$$ @FIXME - hugh - as we're producing files with names like: * * center=34.732267,-86.587593.zoom=10.size=300x250.maptype=roadmap.mobile=true.markers=34.732267,-86.587593.sensor=false.png * * ... we should probably clean $file, replace non alphanumeric chars with * underscores, as filenames with things like commas, = signs etc. could be problematic, both in * the file system, and on the IMG URL. * * EDIT - hopefully just md5()'ing the file should fix the above, needed to do it as we finally had * someone report a problem with invalid file name, see ... * * https://github.com/Fabrik/fabrik/pull/1307 * * So ... just preserve original extension (if any) and append it to md5() of file name. */ $ext = pathinfo($file, PATHINFO_EXTENSION); $file = md5($file); if (!empty($ext)) { $file .= '.' . $ext; } $folder = JPATH_SITE . '/' . ltrim($path, '/'); // For SSL a user agent may need to be set. ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)'); if (!JFolder::exists($folder)) { JFolder::create($folder); } // make sure we have one, and only one, / on the end of folder. Really should add a helper for this which looks for legacy \ as well! $folder = rtrim($folder, '/') . '/'; $cacheFile = $folder . $file; // result to test file_put_contents() with $res = false; // Check for cached version if (JFile::exists($cacheFile)) { // Check its age- Google T&C allow you to store for no more than 30 days. $createDate = JFactory::getDate(filemtime($cacheFile)); $now = JFactory::getDate(); $interval = $now->diff($createDate); $daysOld = (float) $interval->format('%R%a'); if ($daysOld < -$lifeTime) { // Remove out of date JFile::delete($cacheFile); // Grab image from Google and store $res = file_put_contents($cacheFile, file_get_contents($src)); } else { $res = true; } } else { // No cached image, grab image from remote URI and store locally $res = file_put_contents($cacheFile, file_get_contents($src)); } if ($res === false) { $src = false; } else { $src = COM_FABRIK_LIVESITE . $path . $file; } return $src; } /** * Exif to number * * @param string $value Value * @param string $format Format * * @return string */ public static function exifToNumber($value, $format) { $pos = StringHelper::strpos($value, '/'); if ($pos === false) { return sprintf($format, $value); } else { $bits = explode('/', $value, 2); $base = ArrayHelper::getValue($bits, 0); $divider = ArrayHelper::getValue($bits, 1); return ($divider == 0) ? sprintf($format, 0) : sprintf($format, ($base / $divider)); } } /** * Exif to coordinate * * @param string $reference Reference * @param string $coordinate Coordinates * * @return string */ public static function exifToCoordinate($reference, $coordinate) { $prefix = ($reference == 'S' || $reference == 'W') ? '-' : ''; return $prefix . sprintf('%.6F', self::exifToNumber($coordinate[0], '%.6F') + (((self::exifToNumber($coordinate[1], '%.6F') * 60) + (self::exifToNumber($coordinate[2], '%.6F'))) / 3600) ); } /** * Get coordinates * * @param string $filename File name * * @return array|boolean */ public static function getExifCoordinates($filename) { if (extension_loaded('exif')) { $exif = exif_read_data($filename, 'EXIF'); if (isset($exif['GPSLatitudeRef']) && isset($exif['GPSLatitude']) && isset($exif['GPSLongitudeRef']) && isset($exif['GPSLongitude'])) { return array(self::exifToCoordinate($exif['GPSLatitudeRef'], $exif['GPSLatitude']), self::exifToCoordinate($exif['GPSLongitudeRef'], $exif['GPSLongitude'])); } } return false; } /** * Set coordinates to DMS * * @param string $coordinate Image coordinate * @param number $pos Position * @param number $neg Negative * * @return string */ public static function coordinate2DMS($coordinate, $pos, $neg) { $sign = $coordinate >= 0 ? $pos : $neg; $coordinate = abs($coordinate); $degree = intval($coordinate); $coordinate = ($coordinate - $degree) * 60; $minute = intval($coordinate); $second = ($coordinate - $minute) * 60; return sprintf("%s %d° %02d′ %05.2f″", $sign, $degree, $minute, $second); } } Helpers/Image/Imageim.php 0000604 00000012531 15172625755 0011266 0 ustar 00 <?php /** * @package Joomla * @subpackage Fabrik.image * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers\Image; defined('_JEXEC') or die('Restricted access'); use \JHtml; use \Fabrik\Helpers\StringHelper; use \NewMagickWand; /** * Image magic image manipulation class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html * @since 1.0 */ class Imageim extends Image { /** * Resize an image to a specific width/height * * @param int $maxWidth maximum image Width (px) * @param int $maxHeight maximum image Height (px) * @param string $origFile current images folder path (must have trailing end slash) * @param string $destFile destination folder path for resized image (must have trailing end slash) * @param int $quality Percentage image save quality 100 = no compression, 0 = max compression * * @return object image */ public function resize($maxWidth, $maxHeight, $origFile, $destFile, $quality = 100) { // Check if the file exists if (!$this->storage->exists($origFile)) { throw new RuntimeException("no file found for $origFile"); } $fromFile = $this->storage->preRenderPath($origFile); $ext = $this->getImgType($fromFile); if (!$ext) { // False so not an image type so cant resize // $$$ hugh - testing making thumbs for PDF's, so need a little tweak here $origInfo = pathinfo($fromFile); if (StringHelper::strtolower($origInfo['extension']) != 'pdf') { return; } } ini_set('display_errors', true); // See if the imagick image lib is installed if (class_exists('Imagick')) { /* $$$ hugh - having a go at handling PDF thumbnails, which should work as long as the server * has ghostscript (GS) installed. Don't have a generic test for GS being available, so * it'll just fail if no GS. */ $origInfo = pathinfo($fromFile); if (StringHelper::strtolower($origInfo['extension']) == 'pdf') { $pdfThumbType = 'png'; // OK, it's a PDF, so first we need to add the page number we want to the source filename $pdfFile = $fromFile . '[0]'; /* if (is_callable('exec')) { $destFile = str_replace('.pdf', '.png', $destFile); // Output File $convert = "convert " . $pdfFile . " -colorspace RGB -resize " . $maxWidth . " " . $destFile; // Command creating exec($convert); // Execution of complete command. } else { */ // Now just load it, set format, resize, save and garbage collect. // Hopefully IM will call the right delegate (ghostscript) to load the PDF. $im = new \Imagick($pdfFile); $im->setImageFormat($pdfThumbType); $im->thumbnailImage($maxWidth, $maxHeight, true); $im->writeImage($destFile); // as destroy() is deprecated $im->clear(); /* } */ } else { $im = new \Imagick; /* Read the image file */ $im->readImage($fromFile); /* Thumbnail the image ( width 100, preserve dimensions ) */ $im->thumbnailImage($maxWidth, $maxHeight, true); /* Write the thumbnail to disk */ $im->writeImage($destFile); /* Free resources associated to the Imagick object */ $im->destroy(); } $this->thumbPath = $destFile; } else { $resource = NewMagickWand(); if (!MagickReadImage($resource, $fromFile)) { echo "ERROR!"; print_r(MagickGetException($resource)); } $resource = MagickTransformImage($resource, '0x0', $maxWidth . 'x' . $maxWidth); $this->thumbPath = $destFile; MagickWriteImage($resource, $destFile); } } /* * Check for EXIF orientation data, and rotate image accordingly * * @param string path to image file */ public function rotateImageFromExif($src, $dest) { if (function_exists('exif_read_data')) { $exif = exif_read_data($src); if ($exif && isset($exif['Orientation'])) { $orientation = $exif['Orientation']; if ($orientation != 1) { $deg = 0; switch ($orientation) { case 3: $deg = 180; break; case 6: $deg = 270; break; case 8: $deg = 90; break; } if ($deg) { self::rotate($src, $dest, $deg); } } } } } /** * Rotate an image * * @param string $source filepath * @param string $dest output path, if empty defaults to source * @param int $degrees number of degrees to rotate * * @return array (image object, rotated images width, rotated images height) */ public function rotate($source, $dest = '', $degrees = 0) { if (empty($dest)) { $dest = $source; } $source = $this->imageCreateFrom($source); $app = JFactory::getApplication(); // Rotates the image $rotate = imagerotate($source, $degrees, 0); if ($rotate === false) { $app->enqueueMessage('Image rotation failed', 'notice'); } $this->imageToFile($dest, $rotate); list($width, $height) = getimagesize($dest); return array($rotate, $width, $height); } } Helpers/Pagination.php 0000604 00000035424 15172625755 0010773 0 ustar 00 <?php /** * Makes the list navigation html to traverse the list data * * @package Joomla * @subpackage Fabrik * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use \JVersion; use \JRoute; use \JFactory; use \stdClass; use \JPaginationObject; use \JFile; use \JHtml; /** * Makes the list navigation html to traverse the list data * * @param int the total number of records in the table * @param int number of records to show per page * @param int which record number to start at */ jimport('joomla.html.pagination'); /** * Extension to the normal page-nav functions * $total, $limitstart, $limit * * @package Fabrik * @since 3.0 */ class Pagination extends \JPagination { /** * Action url * * @var string */ public $url = ''; /** * Pagination ID * * @var string */ protected $id = ''; /** * Show the total number of records found * * @var bool */ public $showTotal = false; /** * Add an 'all' option to the display # dropdown * * @var bool */ public $showAllOption = false; /** * The lists unique reference * * @var string */ protected $listRef = null; /** * Show 'x of y pages' * * @var bool */ public $showDisplayNum = true; /** * Add a 'show all' option to display # select list * * @var bool */ public $viewAll = false; /** * Set the pagination ID * * @param int $id id * * @return void */ public function setId($id) { $this->id = $id; } /** * Return the pagination footer * * @param int $listRef List reference * @param string $tmpl List template * * @return string Pagination footer */ public function getListFooter($listRef = '', $tmpl = 'default') { $app = JFactory::getApplication(); $this->listRef = $listRef; $this->tmpl = $tmpl; $list = array(); $list['limit'] = $this->limit; $list['limitstart'] = $this->limitstart; $list['total'] = $this->total; $list['limitfield'] = $this->showDisplayNum ? $this->getLimitBox($tmpl) : ''; $list['pagescounter'] = $this->getPagesCounter(); if ($this->showTotal) { $list['pagescounter'] .= ' ' . Text::_('COM_FABRIK_TOTAL') . ': ' . $list['total']; } $list['pageslinks'] = $this->getPagesLinks($listRef, $tmpl); $chromePath = JPATH_THEMES . '/' . $app->getTemplate() . '/html/pagination.php'; if (file_exists($chromePath)) { require_once $chromePath; if (function_exists('pagination_list_footer')) { // Cant allow for it to be overridden } } return $this->_list_footer($list); } /** * Creates a dropdown box for selecting how many records to show per page * * @param string $tmpl List template * * @return string The html for the limit # input box */ public function getLimitBox($tmpl = 'default') { $paths = array(); $displayData = new stdClass; $displayData->id = $this->id; $displayData->startLimit = $this->startLimit; $displayData->showAllOption = $this->showAllOption; $displayData->viewAll = $this->viewAll; $displayData->limit = $this->limit; $layout = $this->getLayout('pagination.fabrik-pagination-limitbox'); return $layout->render($displayData); } /** * Method to create an active pagination link to the item * * @param JPaginationObject $item The object with which to make an active link. * * @return string HTML link */ protected function _item_active(\Joomla\CMS\Pagination\PaginationObject $item) { $displayData = new stdClass; $displayData->item = $item; $layout = $this->getLayout('pagination.fabrik-pagination-item-active'); return $layout->render($displayData); } /** * Method to create an inactive pagination string * * @param JPaginationObject $item The item to be processed * * @return string * * @since 1.5 */ protected function _item_inactive(\Joomla\CMS\Pagination\PaginationObject $item) { $displayData = new stdClass; $displayData->item = $item; $layout = $this->getLayout('pagination.fabrik-pagination-item-inactive'); return $layout->render($displayData); } /** * Create and return the pagination page list string, i.e. Previous, Next, 1 2 3 ... x. * * @param int $listRef Unique list reference * @param string $tmpl List template name * * @return string Pagination page list string. * * @since 11.1 */ public function getPagesLinks($listRef = 0, $tmpl = 'default') { // Build the page navigation list $data = $this->_buildDataObject(); $list = array(); $itemOverride = false; $listOverride = false; $chromePath = COM_FABRIK_FRONTEND . '/views/list/tmpl/' . $tmpl . '/default_pagination.php'; if (JFile::exists($chromePath)) { require_once $chromePath; if (function_exists('fabrik_pagination_item_active') && function_exists('fabrik_pagination_item_inactive')) { // Can't allow this as the js code we use for the items is different $itemOverride = true; } if (function_exists('fabrik_pagination_list_render')) { $listOverride = true; } } // Build the select list if ($data->all->base !== null) { $list['all']['active'] = true; $list['all']['data'] = $itemOverride ? fabrik_pagination_item_active($data->all, $this->listRef) : $this->_item_active($data->all); } else { $list['all']['active'] = false; $list['all']['data'] = $itemOverride ? fabrik_pagination_item_inactive($data->all) : $this->_item_inactive($data->all); } if ($data->start->base !== null) { $list['start']['active'] = true; $list['start']['data'] = $itemOverride ? fabrik_pagination_item_active($data->start, $this->listRef) : $this->_item_active($data->start); } else { $list['start']['active'] = false; $list['start']['data'] = $itemOverride ? fabrik_pagination_item_inactive($data->start) : $this->_item_inactive($data->start); } if ($data->previous->base !== null) { $list['previous']['active'] = true; $list['previous']['data'] = $itemOverride ? fabrik_pagination_item_active($data->previous, $this->listRef) : $this->_item_active($data->previous); } else { $list['previous']['active'] = false; $list['previous']['data'] = $itemOverride ? fabrik_pagination_item_inactive($data->previous) : $this->_item_inactive($data->previous); } // Make sure it exists $list['pages'] = array(); foreach ($data->pages as $i => $page) { if ($page->base !== null) { $list['pages'][$i]['active'] = true; $list['pages'][$i]['data'] = $itemOverride ? fabrik_pagination_item_active($page, $this->listRef) : $this->_item_active($page); } else { $list['pages'][$i]['active'] = false; $list['pages'][$i]['data'] = $itemOverride ? fabrik_pagination_item_inactive($page) : $this->_item_inactive($page); } } if ($data->next->base !== null) { $list['next']['active'] = true; $list['next']['data'] = $itemOverride ? fabrik_pagination_item_active($data->next, $this->listRef) : $this->_item_active($data->next); } else { $list['next']['active'] = false; $list['next']['data'] = $itemOverride ? fabrik_pagination_item_inactive($data->next) : $this->_item_inactive($data->next); } if ($data->end->base !== null) { $list['end']['active'] = true; $list['end']['data'] = $itemOverride ? fabrik_pagination_item_active($data->end, $this->listRef) : $this->_item_active($data->end); } else { $list['end']['active'] = false; $list['end']['data'] = $itemOverride ? fabrik_pagination_item_inactive($data->end) : $this->_item_inactive($data->end); } if ($this->total > $this->limit) { return ($listOverride) ? fabrik_pagination_list_render($list, $this->listRef) : $this->_list_render($list); } else { return ''; } } /** * Create the html for a list footer * * @param array $list Pagination list data structure. * * @return string HTML for a list start, previous, next,end */ protected function _list_render($list) { $displayData = new stdClass; $displayData->list = $list; $layout = $this->getLayout('pagination.fabrik-pagination-links'); return $layout->render($displayData); } /** * THIS SEEMS GOOFY TO HAVE TO OVERRIDE DEFAULT FUNCTION - BUT! * THE ORIGINAL SETS THE PAGE TO EMPTY IF ITS 0 - APPARENTLY TO DO WITH * ROUTING - THIS HAS BEEN REMOVED HERE * * PERHAPS THE FABRIK ROUTING ISN'T RIGHT? * * OCCURS EVEN WITHOUT SEF URLS ON THOUGH? :s * * Create and return the pagination data object * * @return object Pagination data object */ protected function _buildDataObject() { // Initialize variables $data = new stdClass; $this->url = preg_replace("/limitstart{$this->id}=(.*)?(&|)/", '', $this->url); $this->url = StringHelper::rtrimword($this->url, "&"); // $$$ hugh - need to work out if we need & or ? $sepchar = strstr($this->url, '?') ? '&' : '?'; $data->all = new JPaginationObject(Text::_('COM_FABRIK_VIEW_ALL')); $data->all->key = 'all'; if (!$this->viewAll) { $data->all->base = '0'; $data->all->link = JRoute::_("{$sepchar}limitstart="); } // Set the start and previous data objects $data->start = new JPaginationObject(Text::_('COM_FABRIK_START')); $data->start->key = 'start'; $data->previous = new JPaginationObject(Text::_('COM_FABRIK_PREV')); $data->previous->key = 'previous'; if ($this->get('pages.current') > 1) { $page = ($this->get('pages.current') - 2) * $this->limit; $data->start->base = '0'; $data->start->link = JRoute::_($this->url . "{$sepchar}limitstart{$this->id}=0"); $data->previous->base = $page; $data->previous->link = JRoute::_($this->url . "{$sepchar}limitstart{$this->id}=" . $page); $data->start->link = str_replace('resetfilters=1', '', $data->start->link); $data->previous->link = str_replace('resetfilters=1', '', $data->previous->link); $data->start->link = str_replace('clearordering=1', '', $data->start->link); $data->previous->link = str_replace('clearordering=1', '', $data->previous->link); } // Set the next and end data objects $data->next = new JPaginationObject(Text::_('COM_FABRIK_NEXT')); $data->next->key = 'next'; $data->end = new JPaginationObject(Text::_('COM_FABRIK_END')); $data->end->key = 'end'; if ($this->get('pages.current') < $this->get('pages.total')) { $next = $this->get('pages.current') * $this->limit; $end = ($this->get('pages.total') - 1) * $this->limit; $data->next->base = $next; $data->next->link = JRoute::_($this->url . "{$sepchar}limitstart{$this->id}=" . $next); $data->end->base = $end; $data->end->link = JRoute::_($this->url . "{$sepchar}limitstart{$this->id}=" . $end); $data->next->link = str_replace('resetfilters=1', '', $data->next->link); $data->end->link = str_replace('resetfilters=1', '', $data->end->link); $data->next->link = str_replace('clearordering=1', '', $data->next->link); $data->end->link = str_replace('clearordering=1', '', $data->end->link); } $data->pages = array(); $stop = $this->get('pages.stop'); for ($i = $this->get('pages.start'); $i <= $stop; $i++) { $offset = ($i - 1) * $this->limit; $data->pages[$i] = new JPaginationObject($i); $data->pages[$i]->key = $i; if ($i != $this->get('pages.current') || $this->viewAll) { $data->pages[$i]->base = $offset; $data->pages[$i]->link = JRoute::_($this->url . "{$sepchar}limitstart{$this->id}=" . $offset); $data->pages[$i]->link = str_replace('resetfilters=1', '', $data->pages[$i]->link); $data->pages[$i]->link = str_replace('clearordering=1', '', $data->pages[$i]->link); } } return $data; } /** * Create the HTML for a list footer * * @param array $list Pagination list data structure. * * @return string HTML for a list footer */ protected function _list_footer($list) { $limitLabel = $this->showDisplayNum ? Text::_('COM_FABRIK_DISPLAY_NUM') : ''; // Initialize variables $paths = array(); $displayData = new stdClass; $displayData->id = $this->id; $displayData->label = $limitLabel; $displayData->value = $list['limitstart']; $displayData->list = $list['limitfield']; $displayData->pagesCounter = $list['pagescounter']; $displayData->listName = 'limit' . $this->id; $displayData->links = $list['pageslinks']; $displayData->showNav = $this->showNav; $displayData->showTotal = $this->showTotal; $displayData->limit = $this->limit; $layout = $this->getLayout('pagination.fabrik-pagination-footer'); return $layout->render($displayData); } /** * Returns a property of the object or the default value if the property is not set. * Avoids deprecated notices in 3.1 whilst maintaining backwards compat * * @param string $property The name of the property. * @param mixed $default The default value. * * @return mixed The value of the property. * * @since 12.2 * @deprecated 13.3 Access the properties directly. */ public function get($property, $default = null) { $version = new JVersion; if ($version->RELEASE > 2.5) { if (strpos($property, '.')) { $prop = explode('.', $property); $prop[1] = ucfirst($prop[1]); $property = implode($prop); } if (isset($this->$property)) { return $this->$property; } return $default; } elseif (isset($this->$property)) { return $this->$property; } else { return $default; } } /** * Get a pagination JLayout file * * @param string $type form/details/list * @param array $paths Optional paths to add as includes * * @return LayoutFile */ public function getLayout($name, $paths = array(), $options = array()) { $paths[] = JPATH_THEMES . '/' . JFactory::getApplication()->getTemplate() . '/html/layouts/com_fabrik/list_' . $this->id; $paths[] = COM_FABRIK_FRONTEND . '/views/list/tmpl/' . $this->tmpl . '/layouts'; $layout = Html::getLayout($name, $paths, $options); return $layout; } } Helpers/sms_gateways/clickatell.php 0000604 00000003730 15172625755 0013512 0 ustar 00 <?php /** * Clickatell SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Clickatell\Api\ClickatellRest; use Fabrik\Helpers\ArrayHelper; /** * Clickatell SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Clickatell extends JObject { /** * Send SMS * * @param string $message sms message * @param array $opts options * * @return void */ public function process($message = '', $opts) { // Clickatell only uses token, no SID, use whichever param isn't empty $sid = ArrayHelper::getValue($opts, 'sms-username'); $token = ArrayHelper::getValue($opts, 'sms-password'); if (empty($token) && !empty($sid)) { $token = $sid; } // no sms-from setting for Clickatell, just set up 'to' array $smsto = ArrayHelper::getValue($opts, 'sms-to'); $smstos = empty($smsto) ? array() : explode(",", $smsto); // Clickatell is picky about numbers, no spaces or dashes foreach ($smstos as &$smsto) { $smsto = preg_replace("/[^0-9]/","", $smsto); } $client = new ClickatellRest($token); // Clickatell API doesn't throw exceptions, but something else might try { $response = $client->sendMessage( $smstos, $message ); } catch (\Exception $e) { JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error'); return false; } // check the response array foreach ($response as $item) { if ($item->error !== false) { // @TODO add language for this JFactory::getApplication()->enqueueMessage('SMS failed with error code: ' . $item->errorCode, 'error'); return false; } } return true; } } Helpers/sms_gateways/twilio.php 0000604 00000002756 15172625755 0012721 0 ustar 00 <?php /** * Twilio SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\ArrayHelper; use Twilio\Rest\Client; use Twilio\Exceptions\TwilioException; /** * Twilio SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Twilio extends JObject { /** * Send SMS * * @param string $message sms message * @param array $opts options * * @return void */ public function process($message = '', $opts) { $sid = ArrayHelper::getValue($opts, 'sms-username'); $token = ArrayHelper::getValue($opts, 'sms-password'); $smsto = ArrayHelper::getValue($opts, 'sms-to'); // From a valid Twilio number $smsfrom = ArrayHelper::getValue($opts, 'sms-from'); $smstos = empty($smsto) ? array() : explode(",", $smsto); $client = new Twilio\Rest\Client($sid, $token); foreach ($smstos as $smsto) { try { $client->messages->create( trim($smsto), array( 'from' => $smsfrom, 'body' => $message ) ); } catch (TwilioException $e) { JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error'); return false; } } return true; } } Helpers/sms_gateways/kapow.php 0000604 00000002501 15172625755 0012517 0 ustar 00 <?php /** * Send an SMS via the kapow sms gateway * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\ArrayHelper; use Fabrik\Helpers\Sms; /** * Kapow SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Kapow extends JObject { /** * URL To Post SMS to * * @var string */ protected $url = 'http://www.kapow.co.uk/scripts/sendsms.php?username=%s&password=%s&mobile=%s&sms=%s'; /** * Send SMS * * @param string $message sms message * @param array $opts Options * * @return void */ public function process($message, $opts) { $username = ArrayHelper::getValue($opts, 'sms-username'); $password = ArrayHelper::getValue($opts, 'sms-password'); $smsfrom = ArrayHelper::getValue($opts, 'sms-from'); $smsto = ArrayHelper::getValue($opts, 'sms-to'); $smstos = explode(',', $smsto); foreach ($smstos as $smsto) { $url = sprintf($this->url, $username, $password, $smsto, $message); Sms::doRequest('GET', $url, ''); } } } Helpers/sms_gateways/itagg.php 0000604 00000004517 15172625755 0012502 0 ustar 00 <?php /** * Itagg SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\ArrayHelper; use Fabrik\Helpers\Sms; use Fabrik\Helpers\StringHelper; /** * Itagg SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Itagg extends JObject { /** * URL To Post SMS to * * @var string */ protected $url = 'https://secure.itagg.com/smsg/sms.mes'; /** * Send SMS * * @param string $message sms message * @param array $opts Options * * @return void */ public function process($message, $opts) { $username = ArrayHelper::getValue($opts, 'sms-username'); $password = ArrayHelper::getValue($opts, 'sms-password'); $smsfrom = ArrayHelper::getValue($opts, 'sms-from'); $smsto = ArrayHelper::getValue($opts, 'sms-to'); $smstos = explode(",", $smsto); $message = urlencode($message); foreach ($smstos as $smsto) { if (substr($smsto, 0, 1) == '+' && StringHelper::substr($smsto, 1, 2) != '44') { // Global sms $route = 8; } else { // UK (itagg) $route = 7; } $smsto = urlencode($smsto); $url = $this->url; $vars = 'usr=' . $username . '&pwd=' . $password . '&from=rob&to=' . $smsto . '&type=text&route=' . $route . '&txt=' . $message; $itaggapi = "https://secure.itagg.com/smsg/sms.mes"; /* $params="usr=XXX&pwd=YYY&from=steve&to=07712345678,447912345678,3912345678&type=text&rout e=7&txt=hello+via+POST"; */ $ch = curl_init(); if (!$ch) { throw new RuntimeException("cant ini curl session", 500); exit; } curl_setopt($ch, CURLOPT_URL, $itaggapi); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $vars); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); $returned = curl_exec($ch); curl_close($ch); // This will be the OK / error message if ($returned === true) { echo "sent ok"; } $res = Sms::doRequest('POST', $url, $vars); } } } Helpers/sms_gateways/smssza.php 0000604 00000002510 15172625755 0012716 0 ustar 00 <?php /** * Send an SMS via the SMSS (ZA) gateway * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\ArrayHelper; use Fabrik\Helpers\Sms; /** * SMSS (ZA) SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Smssza extends JObject { /** * URL To Post SMS to * * @var string */ protected $url = 'http://148.251.196.36/app/smsapi/index.php?key=%s&type=text&contacts=%s&senderid=%s&msg=%s&time='; /** * Send SMS * * @param string $message sms message * @param array $opts Options * * @return void */ public function process($message, $opts) { $username = ArrayHelper::getValue($opts, 'sms-username'); $password = ArrayHelper::getValue($opts, 'sms-password'); $smsfrom = ArrayHelper::getValue($opts, 'sms-from'); $smsto = ArrayHelper::getValue($opts, 'sms-to'); $url = sprintf($this->url, $username, $smsto, $smsfrom, urlencode($message)); $response = Sms::doRequest('GET', $url, ''); return strstr($response, 'api_') !== false; } } Helpers/sms_gateways/textopoly.php 0000604 00000002516 15172625755 0013453 0 ustar 00 <?php /** * Textopoly SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\ArrayHelper; use Fabrik\Helpers\Sms; /** * Textopoly SMS gateway class * * @package Joomla.Plugin * @subpackage Fabrik.form.sms * @since 3.0 */ class Textopoly extends JObject { /** * URL To Post SMS to * * @var string */ protected $url = 'http://sms.mxtelecom.com/SMSSend?user=%s&pass=%s&smsfrom=%s&smsto=%s&smsmsg=%s'; /** * Send SMS * * @param string $message sms message * @param array $opts Options * * @return void */ public function process($message, $opts) { $username = ArrayHelper::getValue($opts, 'sms-username'); $password = ArrayHelper::getValue($opts, 'sms-password'); $smsfrom = ArrayHelper::getValue($opts, 'sms-from'); $smsto = ArrayHelper::getValue($opts, 'sms-to'); $smstos = explode(',', $smsto); foreach ($smstos as $smsto) { $url = sprintf($this->url, $username, $password, $smsfrom, $smsto, $message); $response = Sms::doRequest('GET', $url, ''); } } } Helpers/Element.php 0000604 00000010676 15172625755 0010275 0 ustar 00 <?php /** * Element Helper class * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use JFactory; /** * Element Helper class * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0.6 */ class Element { /** * For processing repeat elements we need to make its * ID element during the form process * * @param plgFabrik_Element $baseElement repeat element (e.g. db join rendered as checkbox) * * @return plgFabrik_ElementInternalid */ public static function makeIdElement($baseElement) { $pluginManager = Worker::getPluginManager(); $groupModel = $baseElement->getGroupModel(); $elementModel = $pluginManager->getPlugIn('internalid', 'element'); $elementModel->getElement()->name = 'id'; $elementModel->getParams()->set('repeat', $baseElement->isJoin()); $elementModel->getElement()->group_id = $groupModel->getId(); $elementModel->setGroupModel($baseElement->getGroupModel()); $elementModel->_joinModel = $groupModel->getJoinModel(); return $elementModel; } /** * For processing repeat elements we need to make its * parent id element during the form process * * @param plgFabrik_Element $baseElement repeat element (e.g. db join rendered as checkbox) * * @return plgFabrik_ElementField */ public static function makeParentElement($baseElement) { $pluginManager = Worker::getPluginManager(); $groupModel = $baseElement->getGroupModel(); $elementModel = $pluginManager->getPlugIn('field', 'element'); $elementModel->getElement()->name = 'parent_id'; $elementModel->getParams()->set('repeat', $baseElement->isJoin()); $elementModel->getElement()->group_id = $groupModel->getId(); $elementModel->setGroupModel($baseElement->getGroupModel()); $elementModel->_joinModel = $groupModel->getJoinModel(); return $elementModel; } /** * Short cut for getting the element's filter value, or false if no value * * @param int $elementId Element id * @param string $identifier Option listref (like 12_com_content_12) if you need to override default X_com_fabrik_X * * @since 3.0.7 * * @return mixed */ public static function filterValue($elementId, $identifier = '') { $app = \JFactory::getApplication(); $pluginManager = Worker::getPluginManager(); $model = $pluginManager->getElementPlugin($elementId); $listModel = $model->getListModel(); $identifier = empty($identifier) ? $listModel->getRenderContext() : $identifier; $key = 'com_fabrik.list' . $identifier . '.filter'; $filters = ArrayHelper::fromObject($app->getUserState($key)); $elementIds = (array) ArrayHelper::getValue($filters, 'elementid', array()); $index = array_search($elementId, $elementIds); $value = $index === false ? false : ArrayHelper::getValue($filters['value'], $index, false); return $value; } /** * Is the key part of an element join's data. Used in csv import/export * * @param FabrikFEModelForm $model Form model * @param string $key Key - full element name or full element name with _id / ___params appended * * @return boolean */ public static function keyIsElementJoinInfo($model, $key) { $elementModel = self::findElementFromJoinKeys($model, $key); if ($elementModel && $elementModel->isJoin()) { return true; } return false; } /** * Find the element associated with a key. * Loose lookup to find join element from any key related to the join (e.g. _id & __params). * Used in csv import/export * * @param FabrikFEModelForm $model Form model * @param string $key Key - full element name or full element name with _id / ___params appended * * @return PlgFabrik_Element|boolean */ public static function findElementFromJoinKeys($model, $key) { // Search on fullname fullname_id and fullname___params $lookUps = array($key, substr($key, 0, StringHelper::strlen($key) - 3), substr($key, 0, StringHelper::strlen($key) - 9)); foreach ($lookUps as $lookup) { $elementModel = $model->getElement($lookup); if ($elementModel) { return $elementModel; } } return false; } } Helpers/Html.php 0000604 00000265452 15172625755 0007614 0 ustar 00 <?php /** * Fabrik Component HTML Helper * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use JBrowser; use JComponentHelper; use JFactory; use JFile; use JHtml; use JHtmlBootstrap; use JModelLegacy; use JRoute; use JText; use JUri; use JVersion; use stdClass; jimport('joomla.filesystem.file'); if (!defined('COM_FABRIK_FRONTEND')) { throw new RuntimeException(Text::_('COM_FABRIK_SYSTEM_PLUGIN_NOT_ACTIVE'), 400); } /** * Fabrik Component HTML Helper * * @static * @package Joomla * @subpackage Fabrik.helpers * @since 1.5 */ class Html { /** * Is the Fabrik JavaScript framework loaded * * @var bool */ protected static $framework = null; /** * Is the MCL JavaScript library loaded * * @var bool */ protected static $mcl = null; /** * Array of loaded modal window states * * @var array */ protected static $modals = array(); /** * Array of loaded tip states * * @var array */ protected static $tips = array(); /** * Previously loaded js scripts * * @var array */ protected static $scripts = array(); /** * Array of rendered jLayouts for use in JS code. * * @var array */ protected static $jLayoutsJs = array(); /** * Array of paths for requirejs * * @var object */ protected static $allRequirePaths = null; /** * CSS files loaded via AJAX * * @var array */ protected static $ajaxCssFiles = array(); /** * Has the debug JavaScript been loaded * * @var bool */ protected static $debug = null; /** * Has the Facebook API JavaScript file been loaded * * @var bool */ protected static $facebookgraphapi = null; /** * Has the at who js file been loaded * * @var array */ protected static $atWho = array(); /** * Folders to search for media * * @var array */ protected static $helperpaths = array(); /** * Load the modal JavaScript files once * * @var bool */ protected static $modal = null; /** * Form email link URL * * @var string */ protected static $emailURL = null; /** * Form print link URL * * @var string */ protected static $printURL = null; protected static $requireJS = array(); /** * Array containing information for loaded files * * @var array * @since 2.5 */ protected static $loaded = array(); /** * Array of browser request headers. Starts as null. * * @var array */ protected static $requestHeaders = null; /** * Usually gets set to COM_FABRIK_LIVESITE, but can be overridden by a global option * * @var string */ protected static $baseJSAssetURI = null; /** * Load up window code - should be run in ajax loaded pages as well (10/07/2012 but not json views) * might be an issue in that we may be re-observing some links when loading in - need to check * * @param string $selector Element select to auto create windows for - was default = a.modal * @param array $params Window parameters * * @deprecated use windows() instead * * @return void */ public static function mocha($selector = '', $params = array()) { self::windows($selector, $params); } /** * Build a data-toggling dropdown * * @param array $lis Array of links to create dropdown from * @param string $align Should the drop down be left or right aligned - If right then the dropdown content's end * is right aligned to the button * * @return string */ public static function bootStrapDropDown($lis, $align = 'left') { $class = 'btn-group fabrik_action'; if ($align == 'right') { $class .= ' pull-right'; } return '<div class="' . $class . '"><a class="dropdown-toggle btn btn-mini" data-toggle="dropdown" href="#"> <span class="caret"></span> </a> <ul class="dropdown-menu"><li>' . implode('</li>' . "\n" . '<li>', $lis) . '</li></ul></div>'; } /** * Wrap buttons in bootstrap btn-group div * * @param array $items Items * * @return string */ public static function bootStrapButtonGroup($items) { return '<div class="btn-group">' . implode(' ', $items) . '</div>'; } /** * Build an array of the request headers by hand. Replacement for using * apache_request_headers(), which only works in certain configurations. * This solution gets them from the $_SERVER array, and re-munges them back * from HTTP_FOO_BAR format to Foo-Bar format. Stolen from: * http://stackoverflow.com/questions/541430/how-do-i-read-any-request-header-in-php * * @return array request headers assoc */ public static function parseRequestHeaders() { if (isset(self::$requestHeaders)) { return self::$requestHeaders; } self::$requestHeaders = array(); foreach ($_SERVER as $key => $value) { if (substr($key, 0, 5) <> 'HTTP_') { continue; } $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5))))); self::$requestHeaders[$header] = $value; } return self::$requestHeaders; } /** * Load up window code - should be run in ajax loaded pages as well (10/07/2012 but not json views) * might be an issue in that we may be re-observing some links when loading in - need to check * * @param string $selector Element select to auto create windows for - was default = a.modal * @param array $params Window parameters * * @return void */ public static function windows($selector = '', $params = array()) { $app = JFactory::getApplication(); $input = $app->input; $script = ''; // Don't include in an Request.JSON call - for auto-fill form plugin $headers = self::parseRequestHeaders(); if (ArrayHelper::getValue($headers, 'X-Request') === 'JSON') { return; } if ($input->get('format') == 'json') { return; } $sig = md5(serialize(array($selector, $params))); if (isset(self::$modals[$sig]) && (self::$modals[$sig])) { return; } $script .= "window.addEvent('fabrik.loaded', function() {"; if ($selector == '') { return; } // Setup options object $opt['ajaxOptions'] = (isset($params['ajaxOptions']) && (is_array($params['ajaxOptions']))) ? $params['ajaxOptions'] : null; $opt['size'] = (isset($params['size']) && (is_array($params['size']))) ? $params['size'] : 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; // Attach modal behavior to document // Set default values which can be overwritten in <a>'s rel attribute $opts = new stdClass; $opts->id = 'fabwin'; $opts->title = Text::_('COM_FABRIK_ADVANCED_SEARCH'); $opts->loadMethod = 'xhr'; $opts->minimizable = false; $opts->collapsible = true; $opts->width = 500; $opts->height = 150; $opts = json_encode($opts); $script .= <<<EOD $$('$selector').each(function(el, i) { el.addEvent('click', function(e) { var opts = $opts; e.stop(); opts2 = JSON.decode(el.get('rel')); opts = Object.merge(opts, opts2 || {}); opts.contentURL = el.href; if (opts.id === 'fabwin') { opts.id += i; } Fabrik.getWindow(opts); }); }); }); EOD; self::addScriptDeclaration($script); self::$modals[$sig] = true; return; } /** * Show form to allow users to email form to a friend. * Echo's out form HTML. * * @param object $formModel Form model * @param string $template Template * * @return void */ public static function emailForm($formModel, $template = '') { $app = JFactory::getApplication(); $input = $app->input; $layout = self::getLayout('form.fabrik-email-form'); $displayData = new stdClass; $displayData->j3 = Worker::j3(); $displayData->package = $app->getUserState('com_fabrik.package', 'fabrik'); $displayData->referrer = $input->get('referrer', '', 'string'); $document = JFactory::getDocument(); $form = $formModel->getForm(); $document->setTitle($form->label); $document->addStyleSheet('templates/' . $template . '/css/template_css.css'); echo $layout->render($displayData); } /** * Once email has been sent to a friend show this message * * @return void */ public static function emailSent() { $config = JFactory::getConfig(); $document = JFactory::getDocument(); $j3 = Worker::j3(); $document->setTitle($config->get('sitename')); if (!$j3) { ?> <a href='javascript:window.close();'> <span class="small"><?php echo Text::_('COM_FABRIK_CLOSE_WINDOW'); ?> </span> </a> <?php } } /** * Writes a print icon * * @param object $formModel form model * @param object $params parameters * * @return string print html icon/link */ public static function printIcon($formModel, $params) { $layout = self::getLayout('form.fabrik-print-icon'); $displayData = new stdClass; $displayData->status = "status=no,toolbar=no,scrollbars=yes,titlebar=no,menubar=no,resizable=yes,width=400,height=350,directories=no,location=no"; $displayData->link = self::printURL($formModel); $displayData->popup = $params->get('popup', 1); if ($params->get('icons', true)) { $displayData->image = self::image('print.png'); } else { $displayData->image = ' ' . Text::_('COM_FABRIK_PRINT'); } return $layout->render($displayData); } /** * Create print URL * * @param object $formModel form model * * @since 3.0.6 * * @return string */ public static function printURL($formModel) { $app = JFactory::getApplication(); $input = $app->input; $form = $formModel->getForm(); $package = $app->getUserState('com_fabrik.package', 'fabrik'); $table = $formModel->getTable(); if ($app->isAdmin()) { $url = 'index.php?option=com_' . $package . '&task=details.view&tmpl=component&formid=' . $form->id . '&listid=' . $table->id . '&rowid=' . $formModel->getRowId(). '&iframe=1&print=1'; } else { //$this->pdfURL = 'index.php?option=com_' . $this->package . '&view=details&formid=' . $model->getId() . '&rowid=' . $model->getRowId() . '&format=pdf'; $url = COM_FABRIK_LIVESITE . 'index.php?option=com_' . $package . '&view=details&tmpl=component&formid=' . $form->id . '&listid=' . $table->id . '&rowid=' . $formModel->getRowId() . '&iframe=1&print=1'; $url .= '&Itemid=' . Worker::itemId(); } /* $$$ hugh - @TODO - FIXME - if they were using rowid=-1, we don't need this, as rowid has already been transmogrified * to the correct (PK based) rowid. but how to tell if original rowid was -1??? */ if ($input->get('usekey') !== null) { $url .= '&usekey=' . $input->get('usekey'); } $url = JRoute::_($url); // $$$ rob for some reason JRoute wasn't doing this ??? //$url = str_replace('&', '&', $url); self::$printURL = $url; return self::$printURL; } /** * Writes Email icon * * @param object $formModel Form model * @param object $params Parameters * * @return string Email icon/link html */ public static function emailIcon($formModel, $params) { $layout = self::getLayout('form.fabrik-email-icon'); $displayData = new stdClass; $displayData->popup = $params->get('popup', 1); $displayData->status = "status=no,toolbar=no,scrollbars=yes,titlebar=no,menubar=no,resizable=yes,width=400,height=250,directories=no,location=no"; $displayData->link = self::emailURL($formModel); $displayData->icons = $params->get('icons', true); return $layout->render($displayData); } /** * Create URL for form email button * * @param object $formModel form model * * @since 3.0.6 * * @return string */ public static function emailURL($formModel) { $app = JFactory::getApplication(); $input = $app->input; $package = $app->getUserState('com_fabrik.package', 'fabrik'); if ($app->isAdmin()) { $url = 'index.php?option=com_fabrik&task=emailform.display&tmpl=component&formid=' . $formModel->get('id') . '&rowid=' . $formModel->getRowId(); } else { $url = 'index.php?option=com_' . $package . '&view=emailform&tmpl=component&formid=' . $formModel->get('id') . '&rowid=' . $formModel->getRowId(); } if ($input->get('usekey') !== null) { $url .= '&usekey=' . $input->get('usekey'); } $url .= '&referrer=' . urlencode(JURI::getInstance()->toString()); self::$emailURL = JRoute::_($url); return self::$emailURL; } /** * Get a list of condition options - used in advanced search * * @param string $listId list ref * @param string $sel selected value * * @return string html select list */ public static function conditionList($listId, $sel = '') { $conditions = array(); $conditions[] = JHTML::_('select.option', 'AND', Text::_('COM_FABRIK_AND')); $conditions[] = JHTML::_('select.option', 'OR', Text::_('COM_FABRIK_OR')); $name = 'fabrik___filter[list_' . $listId . '][join][]'; return JHTML::_('select.genericlist', $conditions, $name, 'class="inputbox input-mini" size="1" ', 'value', 'text', $sel); } /** * Get a select list of fabrik lists * * @param string $sel selected value * * @return mixed html select list or error */ public static function tableList($sel = '') { $db = Worker::getDbo(true); $query = $db->getQuery(true); $query->select('id, label')->from('#__{package}_lists')->where('published = 1')->order('label'); $db->setQuery($query); $rows = $db->loadObjectList(); return JHTML::_('select.genericlist', $rows, 'fabrik__swaptable', 'class="inputbox" size="1" ', 'id', 'label', $sel); } /** * Load the css and js files once only (using calendar-eightsix) * * @deprecated - behavior.calendar is loaded in framework(); * * @return void */ public static function loadCalendar() { } /** * Fabrik script to load in a style sheet * takes into account if you are viewing the page in raw format * if so sends js code back to web page to inject css file into document head * If not raw format then apply standard J stylesheet * * @param string $file stylesheet URL * @param array $attribs not used * * @return null */ public static function stylesheet($file, $attribs = array()) { // $$$ hugh - moved this to top of function, as we now apply livesite in either usage cases below. if (!strstr($file, COM_FABRIK_LIVESITE)) { $file = COM_FABRIK_LIVESITE . $file; } if (self::cssAsAsset()) { // Send an inline script back which will inject the css file into the doc head // Note your ajax call must have 'evalScripts':true set in its properties if (!in_array($file, self::$ajaxCssFiles)) { if (!strstr($file, 'fabrik.css')) { $opts = new stdClass; echo "<script type=\"text/javascript\"> var v = new Asset.css('" . $file . "', " . json_encode($opts) . "); </script>\n"; self::$ajaxCssFiles[] = $file; } } } else { $document = JFactory::getDocument(); /* $$$ rob 27/04/2011 changed from JHTML::styleSheet as that doesn't work loading * php style sheets with querystrings in them */ $document->addStylesheet($file); } } /** * Will the CSS be loaded as Asset.css() * * @since 3.0.6 * * @return bool */ public static function cssAsAsset() { $app = JFactory::getApplication(); $input = $app->input; $tpl = $input->get('tmpl'); $iFrame = $input->get('iframe'); $print = $input->get('print'); $format = $input->get('format'); return $input->get('format') == 'raw' || ($tpl == 'component' && $iFrame != 1) && $print != 1 && $format !== 'pdf'; } /** * Check for a custom css file and include it if it exists * * @param string $path NOT including JPATH_SITE (so relative too root dir) may include querystring * * @return bool if loaded or not */ public static function stylesheetFromPath($path) { if (strstr($path, '?')) { $file = explode('?', $path); $file = $file[0]; } else { $file = $path; } if (JFile::exists(JPATH_SITE . '/' . $file)) { self::stylesheet($path); return true; } return false; } /** * Generates an HTML radio list * * @param array &$arr An array of objects * @param string $tag_name The value of the HTML name attribute * @param string $tag_attribs Additional HTML attributes for the <select> tag * @param mixed $selected The key that is selected * @param string $key The name of the object variable for the option value * @param string $text The name of the object variable for the option text * @param int $options_per_row number of options to show per row @since 2.0.5 * * @return string HTML for the select list */ public static function radioList(&$arr, $tag_name, $tag_attribs, $selected = null, $key = 'value', $text = 'text', $options_per_row = 0) { return self::aList('radio', $arr, $tag_name, $tag_attribs, $selected, $key, $text, $options_per_row); } /** * Generates an HTML radio OR checkbox list * * @param string $type Radio/checkbox * @param array &$arr An array of objects * @param string $tag_name The value of the HTML name attribute * @param string $tag_attribs Additional HTML attributes for the <select> tag * @param mixed $selected The key that is selected * @param string $key The name of the object variable for the option value * @param string $text The name of the object variable for the option text * @param int $options_per_row Number of options to show per row @since 2.0.5 * @param bool $editable Editable or not * * @return string HTML for the select list */ public static function aList($type, &$arr, $tag_name, $tag_attribs, $selected = null, $key = 'value', $text = 'text', $options_per_row = 0, $editable = true) { reset($arr); $html = array(); if ($options_per_row > 1) { $percentageWidth = floor(floatval(100) / $options_per_row) - 2; $div = "<div class=\"fabrik_subelement\" style=\"float:left;width:" . $percentageWidth . "%\">\n"; } else { $div = '<div class="fabrik_subelement">'; } if ($editable) { $selectText = $type == 'checkbox' ? ' checked="checked"' : ' selected="selected"'; } else { $selectText = ''; } for ($i = 0, $n = count($arr); $i < $n; $i++) { $k = $arr[$i]->$key; $t = $arr[$i]->$text; $id = isset($arr[$i]->id) ? @$arr[$i]->id : null; $extra = ''; $extra .= $id ? ' id="' . $arr[$i]->id . '"' : ''; $found = false; if (is_array($selected)) { foreach ($selected as $obj) { if (is_object($obj)) { $k2 = $obj->$key; if ($k === $k2) { $found = true; $extra .= $selected; break; } } else { if ($k === $obj) { // Checkbox from db join $extra .= $selectText; $found = true; break; } } } } else { $extra .= $k === $selected ? ' checked="checked"' : ''; $found = $k == $selected; } $html[] = $div; if ($editable) { $tmpName = $type === 'checkbox' ? $tag_name . '[' . $i . ']' : $tag_name; $html[] = '<label class="' . $type . '">'; $html[] = '<input type="' . $type . '" value="' . $k . '" name="' . $tmpName . '" class="fabrikinput" ' . $extra . '/>'; } if ($editable || $found) { $html[] = '<span>' . $t . '</span>'; } if ($editable) { $html[] = '</label>'; } $html[] = '</div>'; } $html[] = ""; return implode("\n", $html); } /** * Keep session alive, for example, while editing or creating an article. * * @return void */ public static function keepalive() { // Test since 2.0b3 don't do anything if loading from Fabrik win if (self::inAjaxLoadedPage()) { return; } JHtml::_('behavior.keepalive'); } /** * Load the MCL canvas layer library * * @return array Scripts needed to load MCL */ public static function mcl() { // Cant used compressed version as its not up to date $src = array( 'media/com_fabrik/js/lib/mcl/CANVAS.js', 'media/com_fabrik/js/lib/mcl/CanvasItem.js', 'media/com_fabrik/js/lib/mcl/Cmorph.js', 'media/com_fabrik/js/lib/mcl/Layer.js', 'media/com_fabrik/js/lib/mcl/LayerHash.js', 'media/com_fabrik/js/lib/mcl/Thread.js' ); if (!self::$mcl) { self::script($src); self::$mcl = true; } $src = array( 'lib/mcl/CANVAS', 'lib/mcl/CanvasItem', 'lib/mcl/Cmorph', 'lib/mcl/Layer', 'lib/mcl/LayerHash', 'lib/mcl/Thread' ); return $src; } /** * Append a js file to the main require.js list of files to load. * Will use the -min.js or .js file based on debug settings * * @param array &$srcs Already loaded scripts from framework() * @param string $file JS File path relative to root without .js extension e.g. 'media/com_fabrik/js/list' * * @since 3.0b * * @return void */ public static function addToFrameWork(&$srcs, $file) { $ext = self::isDebug() ? '.js' : '-min.js'; $srcs[] = $file . $ext; } /** * Get the media folder * * @return string media folder */ public static function getMediaFolder() { return self::isDebug() ? 'media/com_fabrik/js' : 'media/com_fabrik/js/dist'; } public static function calendar() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } $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; } /** * 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 2002-2005 / 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 Shift-click 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) . ';'; } /** * Load Fabrik's framework (js and base css file) * * @return array Framework js files */ public static function framework() { if (!self::$framework) { $app = JFactory::getApplication(); $version = new JVersion; Html::modalJLayouts(); $liveSiteSrc = array(); $liveSiteReq = array(); $fbConfig = JComponentHelper::getParams('com_fabrik'); // Only use template test for testing in 2.5 with my temp J bootstrap template. $bootstrapped = in_array($app->getTemplate(), array('bootstrap', 'fabrik4')) || $version->RELEASE > 2.5; //$ext = self::isDebug() ? '.js' : '-min.js'; $mediaFolder = self::getMediaFolder(); $src = array(); JHtml::_('behavior.framework', true); // Ensure bootstrap js is loaded - as J template may not load it. if ($version->RELEASE > 2.5) { JHtml::_('bootstrap.framework'); self::loadBootstrapCSS(); JHtml::_('script', $mediaFolder . '/lib/jquery-ui/jquery-ui.min.js'); } // Require js test - list with no cal loading ajax form with cal if (version_compare(JVERSION, '3.7', '>=')) { self::calendar(); } else { JHTML::_('behavior.calendar'); } $liveSiteReq['Chosen'] = $mediaFolder . '/chosen-loader'; $liveSiteReq['Fabrik'] = $mediaFolder . '/fabrik'; if ($bootstrapped) { $liveSiteReq['FloatingTips'] = $mediaFolder . '/tipsBootStrapMock'; } else { $liveSiteReq['FloatingTips'] = $mediaFolder . '/tips'; } if ($fbConfig->get('advanced_behavior', '0') !== '0') { $chosenOptions = $fbConfig->get('advanced_behavior_options', '{}'); $chosenOptions = json_decode($chosenOptions); if (is_object($chosenOptions) && !isset($chosenOptions->placeholder_text_multiple)) { $chosenOptions->placeholder_text_multiple = JText::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS'); } if (is_object($chosenOptions) && !isset($chosenOptions->placeholder_text_single)) { $chosenOptions->placeholder_text_single = JText::_('JGLOBAL_SELECT_AN_OPTION'); } if (is_object($chosenOptions) && !isset($chosenOptions->no_results_text)) { $chosenOptions->no_results_text = JText::_('JGLOBAL_SELECT_NO_RESULTS_MATCH'); } $chosenOptions = empty($chosenOptions) ? new stdClass : ArrayHelper::fromObject($chosenOptions); JHtml::_('stylesheet', 'jui/chosen.css', false, true); JHtml::_('script', 'jui/chosen.jquery.min.js', false, true, false, false, self::isDebug()); JHtml::_('script', 'jui/ajax-chosen.min', false, true, false, false, self::isDebug()); } if (self::inAjaxLoadedPage() && !$bootstrapped) { // $$$ rob 06/02/2012 recall ant so that Color.detach is available (needed for opening a window from within a window) JHtml::_('script', 'media/com_fabrik/js/lib/art.js'); } if ($fbConfig->get('advanced_behavior', '0') !== '0') { $liveSiteSrc[] = "var chosenInterval = window.setInterval(function () { if (Fabrik.buildChosen) { window.clearInterval(chosenInterval); Fabrik.buildChosen('select.advancedSelect', " . json_encode($chosenOptions) . "); } }, 100);"; } if (!self::inAjaxLoadedPage()) { // Require.js now added in fabrik system plugin onAfterRender() JText::script('COM_FABRIK_LOADING'); $src['Window'] = $mediaFolder . '/window.js'; self::styleSheet(COM_FABRIK_LIVESITE . 'media/com_fabrik/css/fabrik.css'); $liveSiteSrc[] = "\tFabrik.liveSite = '" . COM_FABRIK_LIVESITE . "';"; $liveSiteSrc[] = "\tFabrik.package = '" . $app->getUserState('com_fabrik.package', 'fabrik') . "';"; $liveSiteSrc[] = "\tFabrik.debug = " . (self::isDebug() ? 'true;' : 'false;'); // need to put jLayouts in session data, and add it in the system plugin buildjs(), so just add %%jLayouts%% placeholder //$liveSiteSrc[] = "\tFabrik.jLayouts = " . json_encode(ArrayHelper::toObject(self::$jLayoutsJs)) . ";"; $liveSiteSrc[] = "\tFabrik.jLayouts = %%jLayouts%%;\n"; if ($bootstrapped) { $liveSiteSrc[] = "\tFabrik.bootstrapped = true;"; } else { $liveSiteSrc[] = "\tFabrik.iconGen = new IconGenerator({scale: 0.5});"; $liveSiteSrc[] = "\tFabrik.bootstrapped = false;"; } $liveSiteSrc[] = self::tipInt(); $liveSiteSrc = implode("\n", $liveSiteSrc); } else { if ($bootstrapped) { $liveSiteSrc[] = "\tFabrik.bootstrapped = true;"; } else { $liveSiteSrc[] = "\tFabrik.iconGen = new IconGenerator({scale: 0.5});"; $liveSiteSrc[] = "\tFabrik.bootstrapped = false;"; } $liveSiteSrc[] = "\tif (!Fabrik.jLayouts) { Fabrik.jLayouts = {}; } Fabrik.jLayouts = jQuery.extend(Fabrik.jLayouts, %%jLayouts%%);"; } self::script($liveSiteReq, $liveSiteSrc, '-min.js'); self::$framework = $src; } self::addToSessionJLayouts(); return self::$framework; } /** * Build JS to initiate tips, and observer application state changes, * reloading the tips if needed. * * @return string */ public static function tipInt() { $tipOpts = self::tipOpts(); $tipJs = array(); $tipJs[] = "\tFabrik.tips = new FloatingTips('.fabrikTip', " . json_encode($tipOpts) . ");"; $tipJs[] = "\tFabrik.addEvent('fabrik.list.updaterows', function () {"; $tipJs[] = "\t\t// Reattach new tips after list redraw"; $tipJs[] = "\t\tFabrik.tips.attach('.fabrikTip');"; $tipJs[] = "\t});"; $tipJs[] = "\tFabrik.addEvent('fabrik.plugin.inlineedit.editing', function () {"; $tipJs[] = "\t\tFabrik.tips.hideAll();"; $tipJs[] = "\t});"; $tipJs[] = "\tFabrik.addEvent('fabrik.list.inlineedit.setData', function () {"; $tipJs[] = "\t\tFabrik.tips.attach('.fabrikTip');"; $tipJs[] = "\t});"; // Reload tips if a form is loaded (e.g. a list view with ajax links on which loads a form in a popup) // see: https://github.com/Fabrik/fabrik/issues/1394 $tipJs[] = "\tFabrik.addEvent('fabrik.form.loaded', function () {"; $tipJs[] = "\t\tFabrik.tips.attach('.fabrikTip');"; $tipJs[] = "\t});"; $tipJs[] = "\tFabrik.addEvent('fabrik.list.loaded', function () {"; $tipJs[] = "\t\tFabrik.tips.attach('.fabrikTip');"; $tipJs[] = "\t});"; // Load tips //$tipJs[] = "\tFabrik.tips.attach('.fabrikTip');"; return implode("\n", $tipJs); } /** * Checks the js_base_url global config, to see if admin has set a base URI they want to use to * fetch JS assets from. Allows for putting JS files in a fast CDN like Amazon. If not set, * return COM_FABRIK_LIVESITE. * * @return string */ public static function getJSAssetBaseURI() { if (!isset(static::$baseJSAssetURI)) { $usersConfig = JComponentHelper::getParams('com_fabrik'); $requirejsBaseURI = $usersConfig->get('requirejs_base_uri', COM_FABRIK_LIVESITE); if (empty($requirejsBaseURI)) { $requirejsBaseURI = COM_FABRIK_LIVESITE; } $requirejsBaseURI = rtrim($requirejsBaseURI, '/') . '/'; static::$baseJSAssetURI = $requirejsBaseURI; } return static::$baseJSAssetURI; } /** * Ini the require JS configuration * Stores the shim and config to the session, which Fabrik system plugin * then uses to inject scripts into document. * * @param array $shim Shim js files * @param array $paths Additional require js paths * * @since 3.1 * * @return void */ public static function iniRequireJs($shim = array(), $paths = array()) { $session = JFactory::getSession(); self::$allRequirePaths = (object) array_merge((array) self::requirePaths(), $paths); $framework = array(); $deps = array(); $j3 = Worker::j3(); $requirejsBaseURI = self::getJSAssetBaseURI(); // Load any previously created shim (e.g form which then renders list in outro text) $newShim = $session->get('fabrik.js.shim', array()); foreach ($shim as $k => &$s) { if (is_array($newShim) && array_key_exists($k, $newShim)) { $s->deps = array_unique(array_merge($s->deps, $newShim[$k]->deps)); } $newShim[$k] = $s; } $navigator = JBrowser::getInstance(); if ($navigator->getBrowser() == 'msie' && !$j3) { $deps[] = 'lib/flexiejs/flexie'; } $deps[] = 'fab/utils'; $deps[] = 'jquery'; $deps[] = 'fab/mootools-ext'; $deps[] = 'lib/Event.mock'; if (!$j3) { $deps[] = 'lib/art'; $deps[] = 'fab/tips'; $deps[] = 'fab/icons'; $deps[] = 'fab/icongen'; } self::addRequireJsShim($framework, 'fab/fabrik', $deps, false); self::addRequireJsShim($framework, 'fab/autocomplete-bootstrap', array('fab/fabrik'), false); self::addRequireJsShim($framework, 'jQueryUI', array('jquery'), false); $newShim = array_merge($framework, $newShim); $config = array(); $config[] = "define('jquery', [], function() { return jQuery; });"; // Required for full calendar $config[] = "define('moment', [], function() { return moment; });"; $opts = array( 'baseUrl' => $requirejsBaseURI, 'paths' => self::$allRequirePaths, 'shim' => $newShim, 'waitSeconds' => 30 ); // Force script reloads if in burst is on. if (self::getBurstJs()) { $opts['urlArgs'] = 'bust=' . time(); } $config[] = "requirejs.config("; $config[] = json_encode($opts, self::isDebug() && defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : false); $config[] = ");"; $config[] = "\n"; // Store in session - included in fabrik system plugin $session->set('fabrik.js.shim', $newShim); $session->set('fabrik.js.config', $config); } /** * Should we 'burst' the loading of JS files. If true then loaded * js files will be appended with a random query string ensuring they * are not loaded from cache * * @return boolean * @throws \Exception */ protected static function getBurstJs() { $app = JFactory::getApplication(); $config = JComponentHelper::getParams('com_fabrik'); return (bool) $app->input->get('burst', $config->get('burst_js', 0)); } /** * Helper for create RequireJS shim dependencies * * @param array $framework Array to append the dependency to * @param string $key RequireJs key - the file to load * @param array $dependencies The dependencies to load before the $key file * @param bool $useMin Should we append -min to the $key if we are not in debug mode */ protected static function addRequireJsShim(&$framework, $key, $dependencies, $useMin = true) { $ext = self::isDebug() || !$useMin ? '' : '-min'; $info = new stdClass; $info->deps = $dependencies; $framework[$key . $ext] = $info; } public static function mediaFile($file) { return self::isDebug() ? 'media/com_fabrik/js/' . $file : 'media/com_fabrik/js/dist/' . $file; } /** * Get the js file path map that requireJS uses * * @since 3.1 * * @return stdClass */ protected static function requirePaths() { if (empty(self::$allRequirePaths)) { $r = new stdClass; $r->fab = 'media/com_fabrik/js'; $r->lib = 'media/com_fabrik/js/lib'; $r->element = 'plugins/fabrik_element'; $r->list = 'plugins/fabrik_list'; $r->form = 'plugins/fabrik_form'; $r->cron = 'plugins/fabrik_cron'; $r->viz = 'plugins/fabrik_visualization'; $r->admin = 'administrator/components/com_fabrik/views'; $r->adminfields = 'administrator/components/com_fabrik/models/fields'; $r->jQueryUI = 'media/com_fabrik/js/lib/jquery-ui/jquery-ui'; $r->chosen = 'media/jui/js/chosen.jquery.min'; $r->ajaxChosen = 'media/jui/js/ajax-chosen.min'; // We are now loading compressed js fabrik files from the media/com_fabrik/js/dist folder // This avoids AMD issues where we were loading fab/form or fab/form-min. if (!self::isDebug()) { $r->fab .= '/dist'; } $version = new JVersion; if ($version->RELEASE >= 3.2 && $version->DEV_LEVEL > 1) { $r->punycode = 'media/system/js/punycode'; } self::$allRequirePaths = $r; } return self::$allRequirePaths; } /** * Load mootools lib * * @deprecated use ::framework instead * * @return void */ public static function mootools() { self::framework(); } /** * Load J!'s bootstrap CSS if requested. Special case for iframes in non J! pages loading us. * * @return void */ public static function loadBootstrapCSS($force = false) { $app = JFactory::getApplication(); if ($force || $app->input->get('loadbootstrapcss', '') !== '') { $doc = JFactory::getDocument(); JHtmlBootstrap::loadCss(true, $doc->direction); } } /** * Get tip options to control its fx - set in Fabrik global configuration * * @return stdClass */ public static function tipOpts() { $usersConfig = JComponentHelper::getParams('com_fabrik'); $opts = new stdClass; $opts->tipfx = 'Fx.Transitions.' . $usersConfig->get('tipfx', 'Linear'); if ($usersConfig->get('tipfx', 'Linear') !== 'Linear') { $opts->tipfx .= '.' . $usersConfig->get('tipfx_ease', 'easeIn'); } $opts->duration = $usersConfig->get('tipfx_duration', '500'); $opts->distance = (int) $usersConfig->get('tipfx_distance', '20'); $opts->fadein = (bool) $usersConfig->get('tipfx_fadein', false); return $opts; } /** * Add a script declaration to the session. Inserted into doc via system plugin * * @param string $script Js code to add * * @return null */ public static function addScriptDeclaration($script) { self::addToSessionScripts($script); } /** * Add a rendered JLayout to the Fabrik.jLayouts object * * @param string $name Reference to layout, used in JavaScript * @param string $layoutName Dot syntax path to layout file * @param stdClass $data Template data * @param array $paths Additional layout paths * @param array $options Options */ public static function jLayoutJs($name, $layoutName, stdClass $data = null, $paths = array(), $options = array()) { if (!array_key_exists($name, self::$jLayoutsJs)) { $layout = self::getLayout($layoutName, $paths, $options); self::$jLayoutsJs[$name] = $layout->render($data); } } /** * Add a CSS style declaration, either to the head or inline if format=raw * * @param string $style CSS * * @return void */ public static function addStyleDeclaration($style) { $app = JFactory::getApplication(); if ($app->input->get('format') == 'raw') { echo '<style type="text/css">' . $style . '</style>'; } else { JFactory::getDocument()->addStyleDeclaration($style); } } /** * Sometimes you want to load a page in an iframe and want to use tmpl=component - in this case * append iframe=1 to the url to ensure that we don't try to add the scripts via FBAsset() * * @return bool */ public static function inAjaxLoadedPage() { $app = JFactory::getApplication(); $package = $app->getUserState('com_fabrik.package', 'fabrik'); // Are we in fabrik or a content view, if not return false (things like com_config need to load in Mootools) $app = JFactory::getApplication(); $input = $app->input; $option = $input->get('option'); if ($option !== 'com_' . $package && $option !== 'com_content') { return false; } if (class_exists('JSite')) { $app = JFactory::getApplication(); $menus = $app->getMenu(); $menu = $menus->getActive(); if (is_object($menu) && ($menu->browserNav == 2)) { return false; } } return $input->get('format') == 'raw' || ($input->get('tmpl') == 'component' && $input->get('iframe') != 1 && $input->get('format') !== 'pdf'); } /** * Returns true if either J! or Fabrik debug is enabled * Use this for things like choosing whether to include compressed or uncompressed JS, etc. * Do NOT use for actual debug output. * * @param bool $enabled Set to true if Fabrik debug global option must be set to true * * @return bool */ public static function isDebug($enabled = false) { static $debug = null; if (!isset($debug)) { $app = JFactory::getApplication(); $config = JComponentHelper::getParams('com_fabrik'); if ($enabled && $config->get('use_fabrikdebug') == 0) { $debug = false; return false; } if ($config->get('use_fabrikdebug') == 2) { $debug = true; return true; } $config = JFactory::getConfig(); $debug = (int) $config->get('debug') || $app->input->get('fabrikdebug', 0) == 1; } return $debug; } /** * Returns true if either J! system debug is true, and &fabrikdebug=2, * will then bypass ALL redirects, so we can see J! profile info. * * @return bool */ public static function isDebugSubmit() { $app = JFactory::getApplication(); $config = JComponentHelper::getParams('com_fabrik'); if ($config->get('use_fabrikdebug') == 0) { return false; } $jConfig = JFactory::getConfig(); $debug = (int) $jConfig->get('debug'); return $debug === 1 && $app->input->get('fabrikdebug', 0) == 2; } /** * Wrapper for JHTML::Script() loading with require.js * If not debugging will replace file names .js => -min.js * * @param mixed $file String or array of files to load, relative path to root for local files * e.g. 'administrator/components/com_fabrik/models/fields/tables.js' * @param string $onLoad Optional js to run once the Js file has been loaded * @param string $minSuffix The minimised file suffix to use, replaces '.js' * * @return void */ public static function script($file, $onLoad = '', $minSuffix = '-min.js') { if (empty($file)) { return; } if (is_array($onLoad)) { $onLoad = implode("\n", $onLoad); } $ext = self::isDebug() ? '.js' : $minSuffix; $paths = self::requirePaths(); $files = (array) $file; // Replace with minified files if found foreach ($files as &$file) { if (!(StringHelper::stristr($file, 'http://') || StringHelper::stristr($file, 'https://'))) { /** * Fix for new media compressed JS paths, which we switched from ./js/foo-mins.js to ./js/dist/foo.js. * Some code feeds us the new dist path, but some still uses just media/com_fabrik/js. So, if we're * not in debug mode, and the path is media/com_fabrik/js and doesn't have /dist, add it. **/ if (!self::isDebug()) { if (strpos($file, 'media/com_fabrik/js/') !== false) { if (strpos($file, 'media/com_fabrik/js/lib/') === false) { if (strpos($file, 'media/com_fabrik/js/dist/') === false) { $file = str_replace('media/com_fabrik/js/', 'media/com_fabrik/js/dist/', $file); } } } } if (JFile::exists(COM_FABRIK_BASE . $file)) { $compressedFile = str_replace('.js', $ext, $file); if (JFile::exists(COM_FABRIK_BASE . $compressedFile) || JFile::exists($compressedFile)) { $file = $compressedFile; } } } // Set file name based on requirejs basePath $pathMatched = false; foreach ($paths as $requireKey => $path) { if (strstr($file, $path)) { $file = str_replace($path, '', $file); $file = str_replace('.js', '', $file); $file = $requireKey . $file; $pathMatched = true; } } if (!$pathMatched) { if (!(StringHelper::stristr($file, 'http://') || StringHelper::stristr($file, 'https://'))) { $file = COM_FABRIK_LIVESITE . $file; } } } // Need to load element for ajax popup forms in IE. $needed = array(); if (!Worker::j3()) { $needed[] = self::isDebug() ? 'fab/icongen' : 'fab/icongen-min'; $needed[] = self::isDebug() ? 'fab/icons' : 'fab/icons-min'; } foreach ($needed as $need) { if (!in_array($need, $files)) { array_unshift($files, $need); } } $files = array_unique($files); // Set names from $files keys if assoc array. In general it is for require js files $names = array_keys($files) !== range(0, count($files) - 1) ? array_keys($files) : array(); $files = "['" . implode("', '", $files) . "']"; $require[] = 'requirejs(' . ($files) . ', function (' . implode(", ", $names) . ') {'; $require[] = $onLoad; $require[] = '});'; $require[] = "\n"; $require = implode("\n", $require); self::addToSessionScripts($require); } /** * Add jLayouts to session - will then be added via Fabrik System plugin * * @return void */ protected static function addToSessionJLayouts() { $key = 'fabrik.js.jlayouts'; $session = JFactory::getSession(); /* * No need to figure out what's already there, unlike addToSessionScripts, * we're just updating the whole thing each time framework is added. */ $session->set($key, self::$jLayoutsJs); } /** * Add script to session - will then be added via Fabrik System plugin * * @param string $js JS code * * @return void */ protected static function addToSessionScripts($js) { $key = 'fabrik.js.scripts'; $session = JFactory::getSession(); if ($session->has($key)) { $scripts = $session->get($key); } else { $scripts = array(); } $scripts[] = $js; $session->set($key, $scripts); } /** * Add script to session - will then be added (in head) via Fabrik System plugin * * @param string $js JS code * * @return void */ protected static function addToSessionHeadScripts($js) { $key = 'fabrik.js.head.scripts'; $session = JFactory::getSession(); if ($session->has($key)) { $scripts = $session->get($key); } else { $scripts = array(); } $scripts[] = $js; $session->set($key, $scripts); } /** * Add jLayouts to session - will then be added via Fabrik System plugin * * @return void */ public static function addToSessionCacheIds($id) { $key = 'fabrik.js.cacheids'; $session = JFactory::getSession(); if ($session->has($key)) { $cacheIds = $session->get($key); } else { $cacheIds = array(); } $cacheIds[] = $id; $session->set($key, array_values(array_unique($cacheIds))); } /** * Load the slimbox / media box css and js files * * @return void */ public static function slimbox() { $input = JFactory::getApplication()->input; if ($input->get('format') === 'raw') { return; } if (!self::$modal) { $fbConfig = JComponentHelper::getParams('com_fabrik'); if ($fbConfig->get('include_lightbox_js', 1) == 0) { return; } if ($fbConfig->get('use_mediabox', false)) { $folder = 'components/com_fabrik/libs/mediabox-advanced/'; $mbStyle = $fbConfig->get('mediabox_style', 'Dark'); JHTML::stylesheet($folder . 'mediabox-' . $mbStyle . '.css'); self::script($folder . 'mediaboxAdv.js'); } else { if (Worker::j3()) { JHTML::stylesheet('components/com_fabrik/libs/slimbox2/css/slimbox2.css'); self::script('components/com_fabrik/libs/slimbox2/js/slimbox2.js'); } else { JHTML::stylesheet('components/com_fabrik/libs/slimbox1.64/css/slimbox.css'); self::script('components/com_fabrik/libs/slimbox1.64/js/slimbox.js'); } } self::$modal = true; } } /** * Load the slide-show css and js files * * @return void */ public static function slideshow() { /* * switched from cycle2, to bootstrap, so for now don't need anything */ /* $folder = 'components/com_fabrik/libs/cycle2/'; $ext = self::isDebug() ? '.js' : '.min.js'; self::script($folder . 'jquery.cycle2' . $ext); */ } /** * Attach tooltips to document * * @param string $selector String class name of tips * @param array $params Array parameters * @param string $selectorPrefix Limit the tips selection to those contained within an id * * @return void */ public static function tips($selector = '.hasTip', $params = array(), $selectorPrefix = 'document') { $sig = md5(serialize(array($selector, $params))); if (isset(self::$tips[$sig]) && (self::$tips[$sig])) { return; } // Setup options object $opt['maxTitleChars'] = (isset($params['maxTitleChars']) && ($params['maxTitleChars'])) ? (int) $params['maxTitleChars'] : 50; $opt['offsets'] = (isset($params['offsets'])) ? (int) $params['offsets'] : 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'])) ? '\\true' : '\\false'; $opt['onShow'] = (isset($params['onShow'])) ? '\\' . $params['onShow'] : null; $opt['onHide'] = (isset($params['onHide'])) ? '\\' . $params['onHide'] : null; $options = json_encode($opt); // Attach tooltips to document // Force the zindex to 9999 so that it appears above the popup window. $tooltipInit = 'window.addEvent("fabrik.load", function() {if(typeOf(' . $selectorPrefix . ') !== \'null\' && ' . $selectorPrefix . '.getElements(\'' . $selector . '\').length !== 0) {window.JTooltips = new Tips(' . $selectorPrefix . '.getElements(\'' . $selector . '\'), ' . $options . ');$$(".tool-tip").setStyle("z-index", 999999);}});'; /* self::addScriptDeclaration($tooltipInit); */ self::$tips[$sig] = true; } /** * Add a debug out put section * * @param mixed $content String/object * @param string $title Debug title * * @return void */ public static function debug($content, $title = 'output:') { $config = JComponentHelper::getParams('com_fabrik'); $app = JFactory::getApplication(); $input = $app->input; if ($config->get('use_fabrikdebug') == 0) { return; } if ($input->getBool('fabrikdebug', 0, 'request') != 1) { return; } if ($input->get('format') == 'raw') { return; } $jconfig = JFactory::getConfig(); $secret = $jconfig->get('secret'); echo '<div class="fabrikDebugOutputTitle">' . $title . '</div>'; echo '<div class="fabrikDebugOutput fabrikDebugHidden">'; if (is_object($content) || is_array($content)) { $content = print_r($content, true); $content = str_replace($secret, 'xxxxxxxxx', $content); echo '<pre>' . htmlspecialchars($content) . '</pre>'; } else { $content = str_replace($secret, 'xxxxxxxxx', $content); // Remove any <pre> tags provided by e.g. JQuery::dump $content = preg_replace('/(^\s*<pre( .*)?>)|(<\/pre>\s*$)/i', '', $content); echo '<pre>' . htmlspecialchars($content) . '</pre>'; } echo '</div>'; if (!isset(self::$debug)) { self::$debug = true; $style = ".fabrikDebugOutputTitle{padding:5px;background:#efefef;color:#333;border:1px solid #999;cursor:pointer}"; $style .= ".fabrikDebugOutput{padding:5px;background:#efefef;color:#999;}"; $style .= ".fabrikDebugOutput pre{padding:5px;background:#efefef;color:#999;}"; $style .= ".fabrikDebugHidden{display:none}"; self::addStyleDeclaration($style); $script = "window.addEvent('domready', function() { document.getElements('.fabrikDebugOutputTitle').each(function (title) { title.addEvent('click', function (e) { title.getNext().toggleClass('fabrikDebugHidden'); }); }); })"; self::addScriptDeclaration($script); } } /** * Create html for ajax folder browser (used by file-upload and image elements) * * @param array $folders array of folders to show * @param string $path start path * @param string $tpl view template * * @return string html snippet */ public static function folderAjaxSelect($folders, $path = '', $tpl = '') { $str = array(); $str[] = '<a href="#" class="btn btn-default toggle" title="' . Text::_('COM_FABRIK_BROWSE_FOLDERS') . '">'; $str[] = self::image('orderneutral.png', 'form', $tpl, array('alt' => Text::_('COM_FABRIK_BROWSE_FOLDERS'), 'icon-class' => 'icon-menu-2')); $str[] = '</a>'; $str[] = '<div class="folderselect-container">'; $str[] = '<span class="breadcrumbs"><a href="#">' . Text::_('HOME') . '</a><span> / </span>'; $i = 1; $path = explode("/", $path); foreach ($path as $p) { if (!empty($p)) { $str[] = '<a href="#" class="crumb' . $i . '">' . $p . '</a><span> / </span>'; $i++; } } $str[] = '</span>'; $str[] = '<ul class="folderselect">'; settype($folders, 'array'); foreach ($folders as $folder) { if (trim($folder) != '') { $str[] = '<li class="fileupload_folder"><a href="#">' . $folder . '</a></li>'; } } // For html validation if (empty($folder)) { $str[] = '<li></li>'; } $str[] = '</ul></div>'; return implode("\n", $str); } /** * Add auto-complete JS code to head * * @param string $htmlId Of element to turn into autocomplete * @param int $elementId Element id * @param int $formId Form id * @param string $plugin Plugin name * @param array $opts * onSelection - function to run when option selected * * max - max number of items to show in selection list * * @return void */ public static function autoComplete($htmlId, $elementId, $formId, $plugin = 'field', $opts = array()) { /* $input = JFactory::getApplication()->input; if ($input->get('format') === 'raw') { return; } */ $json = self::autoCompleteOptions($htmlId, $elementId, $formId, $plugin, $opts); $str = json_encode($json); JText::script('COM_FABRIK_NO_RECORDS'); JText::script('COM_FABRIK_AUTOCOMPLETE_AJAX_ERROR'); $jsFile = 'autocomplete'; $className = 'AutoComplete'; if (Worker::j3()) { $jsFile = $plugin === 'cascadingdropdown' ? 'autocomplete-bootstrap-cdd' : 'autocomplete-bootstrap'; $className = $plugin === 'cascadingdropdown' ? 'FabCddAutocomplete' : 'AutoComplete'; } $needed = array(); $needed[] = 'fab/' . $jsFile; $needed[] = 'lib/Event.mock'; $needed = implode("', '", $needed); self::addScriptDeclaration( "require(['$needed'], function ($className) { new $className('$htmlId', $str); });" ); } /** * Gets auto complete js options (needed separate from autoComplete as db js class needs these values for repeat * group duplication) * * @param string $htmlId Element to turn into autocomplete * @param int $elementId Element id * @param int $formId Form id * @param string $plugin Plugin type * @param array $opts * onSelection - function to run when option selected * * max - max number of items to show in selection list * * @return array Autocomplete options (needed for elements so when duplicated we can create a new * FabAutocomplete object */ public static function autoCompleteOptions($htmlId, $elementId, $formId, $plugin = 'field', $opts = array()) { $json = new stdClass; if (!array_key_exists('minTriggerChars', $opts)) { $usersConfig = JComponentHelper::getParams('com_fabrik'); $json->minTriggerChars = (int) $usersConfig->get('autocomplete_min_trigger_chars', '3'); } if (!array_key_exists('max', $opts)) { $usersConfig = JComponentHelper::getParams('com_fabrik'); $json->max = (int) $usersConfig->get('autocomplete_max_rows', '10'); } if (!array_key_exists('autoLoadSingleResult', $opts)) { $usersConfig = JComponentHelper::getParams('com_fabrik'); $json->autoLoadSingleResult = (int) $usersConfig->get('autocomplete_autoload_single', '0'); } $app = JFactory::getApplication(); $package = $app->getUserState('com_fabrik.package', 'fabrik'); $json->url = COM_FABRIK_LIVESITE . 'index.php?option=com_' . $package . '&format=raw'; $json->url .= $app->isAdmin() ? '&task=plugin.pluginAjax' : '&view=plugin&task=pluginAjax'; $json->url .= '&g=element&element_id=' . $elementId . '&formid=' . $formId . '&plugin=' . $plugin . '&method=autocomplete_options&package=' . $package; $c = ArrayHelper::getValue($opts, 'onSelection'); if ($c != '') { $json->onSelections = $c; } foreach ($opts as $k => $v) { $json->$k = $v; } $json->formRef = ArrayHelper::getValue($opts, 'formRef', 'form_' . $formId); $json->container = ArrayHelper::getValue($opts, 'container', 'fabrikElementContainer'); $json->menuclass = ArrayHelper::getValue($opts, 'menuclass', 'auto-complete-container'); return $json; } /** * Load the auto-complete script once * * @deprecated since 3.1b * * @return void */ public static function autoCompleteScript() { } public static function atWho($selector, $placeHolders = array()) { array_filter($placeHolders); $key = $selector . implode('.', $placeHolders); if (!array_key_exists($key, self::$atWho)) { $replacements = Worker::globalReplacements(); $replacements = array_keys($replacements); $replacements = array_map(function ($v) { $v = str_replace(array('{', '}'), array('', ''), $v); return $v; }, $replacements); $placeHolders = array_merge($placeHolders, $replacements); $placeHolders = json_encode($placeHolders); $script[] = "jQuery('$selector').atwho({ 'at': '{', 'insertTpl' : '\${atwho-at}\${name}}', data: $placeHolders, limit: 5, });"; self::$atWho[$key] = true; $css = self::isDebug() ? 'jquery.atwho.css' : 'jquery.atwho.min.css'; Html::stylesheet('media/com_fabrik/js/lib/at/' . $css); $needed[] = self::isDebug() ? '\'lib/caret/caret\'' : '\'lib/caret/caret-min\''; $needed[] = self::isDebug() ? '\'lib/at/atwho\'' : '\'lib/at/atwho-min\''; $needed = implode(", ", $needed); $script = implode("\n", $script); self::addScriptDeclaration( "requirejs([$needed], function (j, f) { $script });" ); } } /** * Load the Facebook Graph API * * @param string $appId Application id * @param string $locale locale e.g 'en_US' * @param array $meta meta tags to add * * @return string */ public static function facebookGraphAPI($appId, $locale = 'en_US', $meta = array()) { if (!isset(self::$facebookgraphapi)) { self::$facebookgraphapi = true; $document = JFactory::getDocument(); $data = array('custom' => array()); $typeFound = false; foreach ($meta as $k => $v) { if (is_array($v)) { $v = implode(',', $v); } $v = strip_tags($v); // $$$ rob og:type required if ($k == 'og:type') { $typeFound = true; if ($v == '') { $v = 'article'; } } $data['custom'][] = '<meta property="' . $k . '" content="' . $v . '"/>'; } if (!$typeFound) { $data['custom'][] = '<meta property="og:type" content="article"/>'; } $document->setHeadData($data); } $retStr = <<<EOT <!-- Load Facebook SDK for JavaScript --> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = 'https://connect.facebook.net/$locale/sdk.js#xfbml=1&version=v3.0&appId=$appId&autoLogAppEvents=1'; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> EOT; return $retStr; } /** * Add path for image() function * * @param string $path to add to list of folders to search * @param string $type of path set to load (currently only image is used) * @param string $view are we looking at loading form or list images? * @param bool $highPriority should the added $path take precedence over previously added paths (default true) * * @since 3.0 * * @return array paths */ public static function addPath($path = '', $type = 'image', $view = 'form', $highPriority = true) { if (!array_key_exists($type, self::$helperpaths)) { self::$helperpaths[$type] = array(); $app = JFactory::getApplication(); $template = $app->getTemplate(); switch ($type) { case 'image': if ($app->isAdmin()) { self::$helperpaths[$type][] = JPATH_SITE . DIRECTORY_SEPARATOR . 'administrator/templates/' . $template . '/images/'; } self::$helperpaths[$type][] = COM_FABRIK_BASE . 'templates/' . $template . '/html/com_fabrik/' . $view . '/%s/images/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'templates/' . $template . '/html/com_fabrik/' . $view . '/images/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'templates/' . $template . '/html/com_fabrik/images/'; self::$helperpaths[$type][] = COM_FABRIK_FRONTEND . '/views/' . $view . '/tmpl/%s/images/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'media/com_fabrik/images/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'images/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'images/stories/'; self::$helperpaths[$type][] = COM_FABRIK_BASE . 'media/system/images/'; break; } } if (!array_key_exists($path, self::$helperpaths[$type]) && $path !== '') { $highPriority ? array_unshift(self::$helperpaths[$type], $path) : self::$helperpaths[$type][] = $path; } return self::$helperpaths[$type]; } /** * Search various folder locations for an image * * @param string $file file name * @param string $type type e.g. form/list/element * @param string $tmpl template folder name * * @return string full path name if found, original filename if not found */ public static function getImagePath($file, $type = 'form', $tmpl = '') { $file = StringHelper::ltrim($file, DIRECTORY_SEPARATOR); $paths = self::addPath('', 'image', $type, true); foreach ($paths as $path) { $path = sprintf($path, $tmpl); $src = $path . $file; if (JFile::exists($src)) { return $src; } } return ''; } /** * Search various folder locations for a template image * * @param string $file File name * @param string $type Type e.g. form/list/element * @param string $tmpl Template folder name * @param array|string $properties Assoc list of properties or string (if you just want to set the image alt tag) * @param bool $srcOnly Src only (default false) * @param array $opts Additional render options: * forceImage: regardless of in J3 site - render an <img> if set to true * (bypasses bootstrap icon loading) * * @since 3.0 * * @return string image */ public static function image($file, $type = 'form', $tmpl = '', $properties = array(), $srcOnly = false, $opts = array()) { if (is_string($properties)) { $properties = array('alt' => $properties); } // if there's a file of this name in our paths, or forceImage is set, don't use icon, use image $src = self::getImagePath($file, $type, $tmpl); $forceImage = ArrayHelper::getValue($opts, 'forceImage', false) || !empty($src); if (Worker::j3() && $forceImage !== true) { unset($properties['alt']); $class = ArrayHelper::getValue($properties, 'icon-class', ''); $class = 'icon-' . JFile::stripExt($file) . ($class ? ' ' . $class : ''); unset($properties['icon-class']); $class .= ' ' . ArrayHelper::getValue($properties, 'class', ''); unset($properties['class']); $p = self::propertiesFromArray($properties); if (!$srcOnly) { return Html::icon($class, '', $p); } else { return $class; } } $src = str_replace(COM_FABRIK_BASE, COM_FABRIK_LIVESITE, $src); $src = str_replace("\\", "/", $src); if ($srcOnly) { return $src; } if (isset($properties['class'])) { $properties['class'] .= ' fabrikImg'; } else { $properties['class'] = 'fabrikImg'; } $p = self::propertiesFromArray($properties); return $src == '' ? '' : '<img src="' . $src . '" ' . $p . '/>'; } /** * Build HTML properties from an associated array * * @param array $properties Properties * * @return string */ public static function propertiesFromArray($properties) { $bits = array(); $p = ''; foreach ($properties as $key => $val) { if ($key === 'title') { $val = htmlspecialchars($val, ENT_QUOTES); } $bits[$key] = $val; } foreach ($bits as $key => $val) { if (!\FabrikWorker::isJSON($val)) { $val = str_replace('"', "'", $val); $p .= $key . '="' . $val . '" '; } } return $p; } /** * Build array of items for use in grid() * * @param array $values Option values * @param array $labels Option labels * @param array $selected Selected options * @param string $name Input name * @param string $type Checkbox/radio etc * @param bool $elementBeforeLabel Element before or after the label - deprecated - not used in Joomla 3 * @param array $classes Label classes * @param bool $buttonGroup Should it be rendered as a bootstrap button group (radio only) * @param array $inputDataAttributes Input data attributes e.g. array('data-foo="bar") * * @return array Grid items */ public static function gridItems($values, $labels, $selected, $name, $type = 'checkbox', $elementBeforeLabel = true, $classes = array(), $buttonGroup = false, $inputDataAttributes = array()) { $items = array(); $layout = self::getLayout('fabrik-grid-item'); $displayData = new stdClass; $displayData->type = $type; $displayData->name = $name; $displayData->classes = $classes; $displayData->inputDataAttributes = implode(' ', $inputDataAttributes); $displayData->selected = $selected; $displayData->elementBeforeLabel = $elementBeforeLabel; $displayData->buttonGroup = $buttonGroup; for ($i = 0; $i < count($values); $i++) { $displayData->i = $i; $displayData->label = $labels[$i]; // For values like '1"' $displayData->value = htmlspecialchars($values[$i], ENT_QUOTES); $items[] = $layout->render($displayData); } return $items; } /** * Make a grid of items * * @param array $values Option values * @param array $labels Option labels * @param array $selected Selected options * @param string $name Input name * @param string $type Checkbox/radio etc. * @param bool $elementBeforeLabel Element before or after the label - deprecated - not used in Joomla 3 * @param int $optionsPerRow Number of suboptions to show per row * @param array $classes Array of arrays, for 'label' and 'container' classes * @param bool $buttonGroup Should it be rendered as a bootstrap button group (radio only) * @param array $dataAttributes Additional array('data-foo="bar"), like YesNo needs data-toggle="button" * @param array $inputDataAttributes Input data attributes e.g. array('data-foo="bar") * * @return string grid */ public static function grid($values, $labels, $selected, $name, $type = 'checkbox', $elementBeforeLabel = true, $optionsPerRow = 4, $classes = array(), $buttonGroup = false, $dataAttributes = array(), $inputDataAttributes = array()) { if (Worker::j3()) { $elementBeforeLabel = true; } $containerClasses = array_key_exists('container', $classes) ? implode(' ', $classes['container']) : ''; $dataAttributes = implode(' ', $dataAttributes); $items = self::gridItems($values, $labels, $selected, $name, $type, $elementBeforeLabel, $classes, $buttonGroup, $inputDataAttributes); $grid = array(); $optionsPerRow = empty($optionsPerRow) ? 4 : $optionsPerRow; $w = floor(100 / $optionsPerRow); if ($buttonGroup && $type == 'radio') { $grid[] = '<fieldset class="' . $type . ' ' . $containerClasses . ' btn-group" ' . $dataAttributes . '>'; foreach ($items as $i => $s) { $grid[] = $s; } $grid[] = '</fieldset>'; } else { if (Worker::j3()) { $grid = self::bootstrapGrid($items, $optionsPerRow, 'fabrikgrid_' . $type); } else { $grid[] = '<ul>'; foreach ($items as $i => $s) { $clear = ($i % $optionsPerRow == 0) ? 'clear:left;' : ''; $grid[] = '<li style="' . $clear . 'float:left;width:' . $w . '%;padding:0;margin:0;">' . $s . '</li>'; } $grid[] = '</ul>'; } } return $grid; } /** * Wrap items in bootstrap grid markup * * @param array $items Content to wrap * @param int $columns Number of columns in the grid * @param string $spanClass Additional class to add to cells * @param bool $explode Should the results be exploded to a string or returned as an array * * @return mixed string/array based on $explode parameter */ public static function bootstrapGrid($items, $columns, $spanClass = '', $explode = false, $spanId = null) { $layout = self::getLayout('fabrik-bootstrap-grid'); $displayData = new stdClass; $displayData->items = $items; $displayData->columns = $columns; $displayData->spanClass = $spanClass; $displayData->spanId = $spanId; $displayData->explode = $explode; $grid = $layout->render($displayData); return $explode ? $grid : explode("\n", $grid); } /** * Does the browser support Canvas elements * * @since 3.0.9 * * @return boolean */ public static function canvasSupport() { $navigator = JBrowser::getInstance(); return !($navigator->getBrowser() == 'msie' && $navigator->getMajor() < 9); } /** * Run Joomla content plugins over text * * @param string &$text Content * @param bool $cloak Cloak emails * * @return void * * @since 3.0.7 */ public static function runContentPlugins(&$text, $cloak = false) { $app = JFactory::getApplication(); $input = $app->input; $opt = $input->get('option'); $view = $input->get('view'); $format = $input->get('format'); $input->set('option', 'com_content'); $input->set('view', 'article'); $input->set('format', 'html'); jimport('joomla.html.html.content'); /** * J!'s email cloaking will cloak email addresses in form inputs, which is a Bad Thing<tm>. * What we really need to do is work out a way to prevent ONLY cloaking of emails in form inputs, * but that's not going to be trivial. So band-aid is to turn it off in form and list views, so * addresses only get cloaked in details view. * In addition, if we are in a details PDF view we should not run the email cloak plugin. */ if (!$cloak) { $text .= '{emailcloak=off}'; } $text = JHTML::_('content.prepare', $text); if (!$cloak) { $text = StringHelper::rtrimword($text, '{emailcloak=off}'); } $input->set('option', $opt); $input->set('view', $view); $input->set('format', $format); } /** * Run text through J!'s email cloaking * @param $text * * @return mixed */ public static function cloakEmails($text) { $text = JHtml::_('email.cloak',$text); return $text; } /** * Get content item template * * @param int $contentTemplate Joomla article id * @param string $part which part, intro, full, or both * @param bool $runPlugins run content plugins on the text * * @since 3.0.7 * * @return string content item html */ public static function getContentTemplate($contentTemplate, $part = 'both', $runPlugins = false) { $app = JFactory::getApplication(); if ($app->isAdmin()) { $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select('introtext, ' . $db->quoteName('fulltext'))->from('#__content')->where('id = ' . (int) $contentTemplate); $db->setQuery($query); $res = $db->loadObject(); } else { JModelLegacy::addIncludePath(COM_FABRIK_BASE . 'components/com_content/models'); $articleModel = JModelLegacy::getInstance('Article', 'ContentModel'); $res = $articleModel->getItem($contentTemplate); } if ($part == 'intro') { $res = $res->introtext; } else { if ($part == 'full') { $res = $res->fulltext; } else { $res = $res->introtext . ' ' . $res->fulltext; } } if ($runPlugins === true) { self::runContentPlugins($res, false); } return $res; } /** * Read a template file * * @param string $templateFile Path to template * * @return string template content */ public static function getTemplateFile($templateFile) { return file_get_contents($templateFile); } /** * Run a PHP template as a require. Return buffered output, or false if require returns false. * * @param string $tmpl Path to template * @param array $data Optional element data in standard format, for eval'd code to use * @param object $model Optional model object, depending on context, for eval'd code to use * * @return mixed email message or false */ public static function getPHPTemplate($tmpl, $data = array(), $model = null) { // Start capturing output into a buffer ob_start(); $result = require $tmpl; $message = ob_get_contents(); ob_end_clean(); if ($result === false) { return false; } else { return $message; } } /** * Get base tag url * * @param string $fullName Full name (key value to remove from querystring) * @param string $rootUrl Optional root to use rather than REQUEST_URI * * @return string */ public static function tagBaseUrl($fullName, $rootUrl = null) { $url = filter_var(ArrayHelper::getValue($_SERVER, 'REQUEST_URI', 'index.php'), FILTER_SANITIZE_URL); $bits = explode('?', $url); $root = isset($rootUrl) ? $rootUrl : ArrayHelper::getValue($bits, 0, '', 'string'); $bits = ArrayHelper::getValue($bits, 1, '', 'string'); $bits = explode("&", $bits); $rootBits = array(); if (isset($rootUrl)) { $rootBits = explode('?', $rootUrl); $rootBits = ArrayHelper::getValue($rootBits, 1, '', 'string'); $rootBits = explode("&", $rootBits); } for ($b = count($bits) - 1; $b >= 0; $b--) { if (in_array($bits[$b], $rootBits)) { unset($bits[$b]); continue; } $parts = explode("=", $bits[$b]); if (count($parts) > 1) { $key = StringHelper::ltrimword(StringHelper::safeColNameToArrayKey($parts[0]), '&'); if ($key == $fullName) { unset($bits[$b]); } if ($key == $fullName . '[value]') { unset($bits[$b]); } if ($key == $fullName . '[condition]') { unset($bits[$b]); } } } $url = empty($bits) ? $root : $root . StringHelper::qsSepChar($root) . implode('&', $bits); return $url; } /** * Tagify a string * * @param array $data Data to tagify * @param string $baseUrl Base Href url * @param string $name Key name for querystring * @param string $icon HTML bootstrap icon * * @return string tagified string */ public static function tagify($data, $baseUrl = '', $name = '', $icon = '') { $url = $baseUrl; $tags = array(); if (!is_array($data)) { return $tags; } if ($url == '') { $url = self::tagBaseUrl(); } // Remove duplicates from tags $data = array_unique($data); foreach ($data as $key => $d) { $d = trim($d); if ($d != '') { if (trim($baseUrl) == '') { $qs = strstr($url, '?'); if (substr($url, -1) === '?') { $thisUrl = $url . $name . '[value]=' . $d; } else { $thisUrl = strstr($url, '?') ? $url . '&' . $name . '[value]=' . urlencode($d) : $url . '?' . $name . '[value]=' . urlencode($d); } $thisUrl .= '&' . $name . '[condition]=CONTAINS'; $thisUrl .= '&resetfilters=1'; } else { $thisUrl = str_replace('{tag}', urlencode($d), $url); $thisUrl = str_replace('{key}', urlencode($key), $url); } $tags[] = '<a href="' . $thisUrl . '" class="fabrikTag">' . $icon . $d . '</a>'; } } return $tags; } /** * Return a set of attributes for an <a> tag * * @param string $title Title to use for popup image * @param string $group Grouping tag for next/prev, if applicable * * @return string */ public static function getLightboxAttributes($title = "", $group = "") { $fbConfig = JComponentHelper::getParams('com_fabrik'); $lightBoxScript = $fbConfig->get('use_mediabox', '0'); $attributes = array(); switch ($lightBoxScript) { case 0: case 1: default: $attributes[] = 'rel="lightbox[' . $group . ']"'; break; case 2: $attributes[] = "data-rokbox"; if (!empty($title)) { $attributes[] = 'data-rockbox-caption="' . addslashes($title) . '"'; } if (!empty($group)) { $attributes[] = 'data-rokbox-album="' . addslashes($group) . '"'; } break; } return implode(' ', $attributes); } /** * Make an <a> tag * * @param string $href URL * @param string $lbl Link text * @param array $opts Link properties key = value * @param bool $normalize if true, tweak scheme to match J! URI * * @since 3.1 * * @return string <a> tag or empty string if not $href */ public static function a($href, $lbl = '', $opts = array(), $normalize = false) { if (empty($href) || StringHelper::strtolower($href) == 'http://' || StringHelper::strtolower($href) == 'https://') { return ''; } if (Worker::isEmail($href)) { return '<a href="mailto:' . $href . '">' . $lbl . '</a>'; } if ($normalize) { $parsedUrl = parse_url(JUri::root()); if ($parsedUrl['scheme'] === 'https') { $href = str_ireplace('http://', 'https://', $href); } } if (empty($lbl)) { // If label is empty, set as a copy of the link $lbl = $href; } $smart_link = ArrayHelper::getValue($opts, 'smart_link', false); $target = ArrayHelper::getValue($opts, 'target', false); if ($smart_link || $target == 'mediabox') { $smarts = self::getSmartLinkType($href); // Not sure that the type option is now needed. $opts['rel'] = 'lightbox[' . $smarts['type'] . ' ' . $smarts['width'] . ' ' . $smarts['height'] . ']'; } unset($opts['smart_link']); $a[] = '<a href="' . $href . '"'; foreach ($opts as $key => $value) { $a[] = ' ' . $key . '="' . trim($value) . '"'; } $a[] = '>' . $lbl . '</a>'; return implode('', $a); } /** * Get an array containing info about the media link * * @param string $link to examine * * @return array width, height, type of link */ public static function getSmartLinkType($link) { /* $$$ hugh - not really sure how much of this is necessary, like setting different widths * and heights for different social video sites. I copied the numbers from the examples page * for mediabox: http://iaian7.com/webcode/mediaboxAdvanced */ $ret = array('width' => '800', 'height' => '600', 'type' => 'mediabox'); if (preg_match('#^http://([\w\.]+)/#', $link, $matches)) { $site = $matches[1]; /* * @TODO should probably make this a little more intelligent, like optional www, * and check for site specific spoor in the URL (like '/videoplay' for google, * '/photos' for flicker, etc). */ switch ($site) { case 'www.flickr.com': $ret['width'] = '400'; $ret['height'] = '300'; $ret['type'] = 'social'; break; case 'video.google.com': $ret['width'] = '640'; $ret['height'] = '400'; $ret['type'] = 'social'; break; case 'www.metacafe.com': $ret['width'] = '400'; $ret['height'] = '350'; $ret['type'] = 'social'; break; case 'vids.myspace.com': $ret['width'] = '430'; $ret['height'] = '346'; $ret['type'] = 'social'; break; case 'myspacetv.com': $ret['width'] = '430'; $ret['height'] = '346'; $ret['type'] = 'social'; break; case 'www.revver.com': $ret['width'] = '480'; $ret['height'] = '392'; $ret['type'] = 'social'; break; case 'www.seesmic.com': $ret['width'] = '425'; $ret['height'] = '353'; $ret['type'] = 'social'; break; case 'www.youtube.com': $ret['width'] = '480'; $ret['height'] = '380'; $ret['type'] = 'social'; break; case 'www.veoh.com': $ret['width'] = '540'; $ret['height'] = '438'; $ret['type'] = 'social'; break; case 'www.viddler.com': $ret['width'] = '437'; $ret['height'] = '370'; $ret['type'] = 'social'; break; case 'vimeo.com': $ret['width'] = '400'; $ret['height'] = '302'; $ret['type'] = 'social'; break; case '12seconds.tv': $ret['width'] = '431'; $ret['height'] = '359'; $ret['type'] = 'social'; break; } if ($ret['type'] == 'mediabox') { $ext = StringHelper::strtolower(JFile::getExt($link)); switch ($ext) { case 'swf': case 'flv': case 'mp4': $ret['width'] = '640'; $ret['height'] = '360'; $ret['type'] = 'flash'; break; case 'mp3': $ret['width'] = '400'; $ret['height'] = '20'; $ret['type'] = 'audio'; break; } } } return $ret; } public static function formvalidation() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Add validate.js language strings JText::script('JLIB_FORM_FIELD_INVALID'); // Include MooTools More framework static::framework('more'); $debug = JFactory::getConfig()->get('debug'); $version = new JVersion; if ($version->RELEASE >= 3.2 && $version->DEV_LEVEL > 1) { $file = $debug ? 'punycode-uncompressed' : 'punycode'; $path = JURI::root() . 'media/system/js/' . $file; $js = array(); $js[] = "requirejs({"; $js[] = " 'paths': {"; $js[] = " 'punycode': '" . $path . "'"; $js[] = " }"; $js[] = " },"; $js[] = "['punycode'], function (p) {"; $js[] = " window.punycode = p;"; $js[] = "});"; self::addToSessionHeadScripts(implode("\n", $js)); } JHtml::_('script', 'system/validate.js', false, true); static::$loaded[__METHOD__] = true; } /** * Get the element's JLayout file * Its actually an instance of LayoutFile which inverses the ordering added include paths. * In LayoutFile the addedPath takes precedence over the default paths, which makes more sense! * * @param string $name Layout file name (eg. fabrik-element-label) * @param array $paths Optional paths to add as includes * @param array $options Layout options * * @return LayoutFile */ public static function getLayout($name, $paths = array(), $options = array()) { $defaultOptions = array( 'debug' => false, 'component' => 'com_fabrik', 'client' => 'site' ); $options = array_merge($defaultOptions, $options); $basePath = COM_FABRIK_FRONTEND . '/layouts'; $layout = new LayoutFile($name, $basePath, $options); $layout->addIncludePaths(JPATH_SITE . '/layouts'); $layout->addIncludePaths(JPATH_THEMES . '/' . JFactory::getApplication()->getTemplate() . '/html/layouts'); $layout->addIncludePaths(JPATH_THEMES . '/' . JFactory::getApplication()->getTemplate() . '/html/layouts/com_fabrik'); foreach ($paths as $path) { $layout->addIncludePath($path); } return $layout; } /** * Render an icon using JLayouts * * @param string $icon Icon class name * @param string $label Label * @param string $properties Additional html properties * @param bool $nameOnly Return just the icon name * * @return string */ public static function icon($icon, $label = '', $properties = '', $nameOnly = false) { $icon = Html::getLayout('fabrik-icon') ->render((object) array( 'icon' => $icon, 'properties' => $properties, 'nameOnly' => $nameOnly )); if ($label != '' && !$nameOnly) { $icon .= ' ' . $label; } return $icon; } /** * Add the js jLayout objects for rendering the modal * * @return void */ public static function modalJLayouts() { Html::jLayoutJs('modal-close', 'modal.fabrik-close'); Html::jLayoutJs('icon-expand', 'fabrik-icon', (object) array('icon' => 'icon-expand')); Html::jLayoutJs('icon-full-screen', 'fabrik-icon', (object) array('icon' => 'icon-out-2 icon-fullscreen')); } /** * Get framework specific grid span class * * @param string $spanSize numeric span size * * @return void */ public static function getGridSpan($size, $viewport = 'medium') { static $spans; $size = (int)$size; if (!is_array($spans)) { $spans = array(); } if (!array_key_exists($viewport, $spans)) { $spans[$viewport] = array(); } if (!array_key_exists($size, $spans[$viewport])) { $layout = self::getLayout('fabrik-grid-span'); $displayData = new stdClass; $displayData->spanSize = $size; $displayData->viewport = $viewport; $spans[$viewport][$size] = $layout->render($displayData); } return $spans[$viewport][$size]; } /** * Load markup into DOMDocument, checking for entities. * * The loadXML() chokes if data has & in it. But we can't htmlspecialchar() it, as that removes * the HTML markup we're looking for. So we need to ONLY change &'s which aren't already part of * any HTML entities which may be in the data. So use a negative lookahead regex, which finds & followed * by anything except non-space the ;. * * It also chokes if the data already contains any HTML entities which XML doesn't like, like é, * so first we need to do an html_entity_decode() to get rid of those! * * @param string $html HTML to load * * @return \DOMDocument */ public static function loadDOMDocument($html) { // libxml_use_internal_errors won't supress the empty string warning, so ... if (empty($html)) { $html = '<span></span>'; } // suppress output of warnings about DOM structure $previous = libxml_use_internal_errors(true); $doc = new \DOMDocument; $html = html_entity_decode($html); $html = preg_replace('/&(?!\S+;)/', '&', $html); $doc->loadXML($html); libxml_clear_errors(); libxml_use_internal_errors($previous); return $doc; } } Helpers/Pdf.php 0000604 00000016324 15172625755 0007411 0 ustar 00 <?php /** * PDF Set up helper * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); jimport('joomla.filesystem.file'); use Dompdf\Dompdf; use Dompdf\Options; /** * PDF Set up helper * * @package Joomla * @subpackage Fabrik.helpers * @since 3.1rc3 */ class Pdf { /** * @param $html * @param string $size * @param string $orientation */ public static function renderPdf($html, $size = 'A4', $orientation = 'portrait') { $config = \JComponentHelper::getParams('com_fabrik'); if ($config->get('fabrik_pdf_lib', 'dompdf') === 'dompdf') { $pdf = self::renderDomPdf($html, $size, $orientation); } else { $pdf = self::renderMPdf($html, $size, $orientation); } return $pdf; } public static function renderMPdf($html, $size = 'A4', $orientation = 'portrait') { $size = ucfirst($size); switch ($orientation) { case 'landscape': $orientation = 'L'; $size .= '-' . $orientation; break; case 'portrait': default: $orientation = 'P'; break; } Pdf::fullPaths($html); $html = mb_convert_encoding($html,'HTML-ENTITIES','UTF-8'); try { $mpdf = new \Mpdf\Mpdf( [ 'tempDir' => \JFactory::getConfig()->get('tmp_path', JPATH_ROOT . '/tmp'), 'mode' => 'utf-8', 'format' => $size, 'orientation' => $orientation ] ); $mpdf->WriteHTML($html); return $mpdf->Output('', \Mpdf\Output\Destination::STRING_RETURN); } catch (\Mpdf\MpdfException $e) { // mmmphh return ''; } } public static function renderDomPdf($html, $size = 'A4', $orientation = 'portrait') { Pdf::fullPaths($html); $html = mb_convert_encoding($html,'HTML-ENTITIES','UTF-8'); $domPdf = self::iniDomPdf(true); $domPdf->set_paper($size, $orientation); $domPdf->load_html($html); $domPdf->render(); return $domPdf->output(); } /** * Init selected PDF */ public static function iniPdf() { $config = \JComponentHelper::getParams('com_fabrik'); if ($config->get('fabrik_pdf_lib', 'dompdf') === 'dompdf') { return self::iniDomPdf(false); } } /** * Set up DomPDF engine * * @param bool $puke throw exception if not installed (true) or just return false * * @return bool */ public static function iniDomPdf($puke = false) { if (!Worker::canPdf($puke)) { return false; } $config = \JFactory::getConfig(); $options = new Options(); $options->set('isRemoteEnabled', true); /** * need to enable HTML5 parser to work around a bug in DOMPDF: * https://github.com/dompdf/dompdf/issues/1494#issuecomment-332116978 */ $options->setIsHtml5ParserEnabled(true); $options->set('fontCache', $config->get('tmp_path')); $options->set('tempDir', $config->get('tmp_path')); return new Dompdf($options); } /** * Parse relative images a hrefs and style sheets to full paths * * @param string &$data data * * @return void */ public static function fullPaths(&$data) { $data = str_replace('xmlns=', 'ns=', $data); libxml_use_internal_errors(true); $base_root = COM_FABRIK_LIVESITE_ROOT . '/'; // scheme, host, port, without trailing /,add it $subdir = str_replace(COM_FABRIK_LIVESITE_ROOT,'',COM_FABRIK_LIVESITE); // subdir /xx/ $subdir = ltrim($subdir,'/'); $schemeString = '://'; //if no schemeString found assume path is relative try { $doc = new \DOMDocument(); $doc->strictErrorChecking = FALSE; // prepend encoding, otherwise UTF-8 will get munged into special chars $data = '<?xml version="1.0" encoding="UTF-8"?>' . $data; // load the document $doc->loadHTML($data); // img tags $imgs = $doc->getElementsByTagName('img'); foreach ($imgs as $img) { $src = $img->getAttribute('src'); if (!strstr($src, $schemeString)) { $base = empty($subdir) || strstr($src, $subdir) ? $base_root : $base_root . $subdir; $src = $base . ltrim($src,'/'); $img->setAttribute('src', $src); } } // a tags $as = $doc->getElementsByTagName('a'); foreach ($as as $a) { $href = $a->getAttribute('href'); if (!strstr($href, $schemeString) && !strstr($href, 'mailto:')) { $base = empty($subdir) || strstr($href, $subdir) ? $base_root : $base_root . $subdir; $href = $base . ltrim($href,'/'); $a->setAttribute('href', $href); } } // link tags $links = $doc->getElementsByTagName('link'); foreach ($links as $link) { $rel = $link->getAttribute('rel'); $href = $link->getAttribute('href'); if ($rel == 'stylesheet' && !strstr($href, $schemeString)) { $base = empty($subdir) || strstr($href, $subdir) ? $base_root : $base_root . $subdir; $href = $base . ltrim($href,'/'); $link->setAttribute('href', $href); } } $data = $doc->saveHTML(); /* $ok = simplexml_import_dom($doc); //$ok = new \SimpleXMLElement($data); if ($ok) { $imgs = $ok->xpath('//img'); foreach ($imgs as &$img) { if (!strstr($img['src'], $schemeString)) { $base = empty($subdir) || strstr($img['src'], $subdir) ? $base_root : $base_root . $subdir; $img['src'] = $base . ltrim($img['src'],'/'); } } // Links $as = $ok->xpath('//a'); foreach ($as as &$a) { if (!strstr($a['href'], $schemeString) && !strstr($a['href'], 'mailto:')) { $base = empty($subdir) || strstr($a['href'], $subdir) ? $base_root : $base_root . $subdir; $a['href'] = $base . ltrim($a['href'],'/'); } } // CSS files. $links = $ok->xpath('//link'); foreach ($links as &$link) { if ($link['rel'] == 'stylesheet' && !strstr($link['href'], $schemeString)) { $base = empty($subdir) || strstr($link['href'], $subdir) ? $base_root : $base_root . $subdir; $link['href'] = $base . ltrim($link['href'],'/'); } } $data = $ok->asXML(); } */ } catch (\Exception $err) { // Oho malformed html - if we are debugging the site then show the errors // otherwise continue, but it may mean that images/css/links are incorrect $errors = libxml_get_errors(); $config = \JComponentHelper::getParams('com_fabrik'); // Don't show the errors if we want to debug the actual pdf html if (JDEBUG && $config->get('pdf_debug', false) === true) { echo "<pre>"; print_r($errors); echo "</pre>"; exit; } //Create the full path via general str_replace //todo: relative URL starting without / else { $base = $base_root . $subdir; $data = str_replace('href="/', 'href="' . $base, $data); $data = str_replace('src="/', 'src="' . $base, $data); $data = str_replace("href='/", "href='" . $base, $data); $data = str_replace("src='/", "src='" . $base, $data); } } } } Helpers/Text.php 0000604 00000004054 15172625755 0007621 0 ustar 00 <?php /** * String helpers * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2015 fabrikar.com - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); /** * * $$$ hugh JText::_() does funky stuff to strings with commas in them, like * truncating everything after the first comma, if what follows the first comma * is all "upper case". But it tests for that using non MB safe code, so any non * ASCII strings (like Greek text) with a comma in them get truncated at the comma. * Corner case or what! But we need to work round this behavior. * * So ... here's a wrapper for JText::_(). */ class Text extends \JText { /** * Translates a string into the current language. * * Examples: * <script>alert(Joomla.JText._('<?php echo Text::_("JDEFAULT", array("script"=>true));?>'));</script> * will generate an alert message containing 'Default' * <?php echo Text::_("JDEFAULT");?> it will generate a 'Default' string * * @param string $string The string to translate. * @param mixed $jsSafe Boolean: Make the result javascript safe. * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation) * @param boolean $script To indicate that the string will be push in the javascript language store * * @return string The translated string or the key is $script is true * * @since 11.1 */ public static function _($string, $jsSafe = false, $interpretBackSlashes = true, $script = false) { /** * This function is now kind of redundant, as it uses to guard against some behavior of JText_() which no * longer happens (as of 3.7). But we'll keep it around as a wrapper in case we ever need to Do Fabrikm Stuff * to translatable strings. */ return parent::_($string, $jsSafe, $interpretBackSlashes, $script); } } Helpers/Worker.php 0000604 00000226706 15172625755 0010160 0 ustar 00 <?php /** * Generic tools that all models use * * @package Joomla * @subpackage Fabrik * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use Fabrik\Helpers\FCipher; use DateTime; use Exception; use FabTable; use JAccess; use JCache; use JComponentHelper; use JCrypt; use JCryptCipherSimple; use JCryptKey; use JDatabaseDriver; use JFactory; use JFile; use JFilterInput; use JForm; use JHtml; use JLanguageHelper; use JLanguageMultilang; use JLog; use JMail; use JMailHelper; use JModelLegacy; use Joomla\CMS\Application\CMSApplication; use JPath; use JSession; use JTable; use JUri; use JVersion; use RuntimeException; /** * Generic tools that all models use * This code used to be in models/parent.php * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class Worker { /** * Fabrik database objects * * @var JDatabaseDriver[] */ public static $database = null; /** * Fabrik db connections * * @var array */ public static $connection = null; /** * Plugin manager * * @var object */ public static $pluginManager = null; /** * Strtotime final date format * * @var string */ static protected $finalFormat = null; /** * Add slashes in parse message * * @var bool */ protected $parseAddSlashes = false; /** * Search data to replace placeholders * * @var array */ protected $_searchData = array(); /** * Get array of valid view types * * @return array */ public static function getViewTypes() { return array( 'article', 'cron', 'csv', 'details', 'element', 'form', 'list', 'package', 'visualization' ); } /** * Returns true if $view is a valid view type * * @param string $view View type * * @return bool */ public static function isViewType($view) { $view = strtolower(trim($view)); $viewTypes = self::getViewTypes(); return in_array($view, $viewTypes); } /** * Returns true if $file has an image extension type * * @param string $file Filename * * @return bool */ public static function isImageExtension($file) { $path_parts = pathinfo($file); if (array_key_exists('extension', $path_parts)) { $image_extensions_eregi = 'bmp|gif|jpg|jpeg|png'; return preg_match('/' . $image_extensions_eregi . '/i', $path_parts['extension']) > 0; } return false; } /** * Get audio mime type array, keyed by file extension * * @return array */ public static function getAudioMimeTypes() { return array( 'mp3' => 'audio/x-mpeg', 'm4a' => 'audio/x-m4a' ); } /** * Get audio mime type array, keyed by file extension * * @return array */ public static function getImageMimeTypes() { return array( 'png' => 'image/png', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'bmp' => 'image/bmp', 'webp' => 'image/webp' ); } /** * Get document mime type array, keyed by file extension * * @return array */ public static function getDocMimeTypes() { return array( 'pdf' => 'application/pdf', 'epub' => 'document/x-epub' ); } /** * Get video mime type array, keyed by file extension * * @return array */ public static function getVideoMimeTypes() { return array( 'mp4' => 'video/mp4', 'm4v' => 'video/x-m4v', 'mov' => 'video/quicktime' ); } /** * Get Audio Mime type * * @param string $file Filename * * @return bool|string */ public static function getAudioMimeType($file) { $path_parts = pathinfo($file); $types = self::getAudioMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Audio Mime type * * @param string $file Filename * * @return bool|string */ public static function getImageMimeType($file) { $path_parts = pathinfo($file); $types = self::getImageMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Video Mime type * * @param string $file Filename * * @return bool|string */ public static function getVideoMimeType($file) { $path_parts = pathinfo($file); $types = self::getVideoMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Doc Mime type * * @param string $file Filename * * @return bool|string */ public static function getDocMimeType($file) { $path_parts = pathinfo($file); $types = self::getDocMimeTypes(); return ArrayHelper::getValue( $types, ArrayHelper::getValue($path_parts, 'extension', ''), false ); } /** * Get Podcast Mime type * * @param string $file Filename * * @return bool|string */ public static function getPodcastMimeType($file) { $mime_type = false; if ($mime_type = self::getVideoMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getAudioMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getDocMimeType($file)) { return $mime_type; } elseif ($mime_type = self::getImageMimeType($file)) { return $mime_type; } return $mime_type; } /** * Format a string to datetime * * http://fr.php.net/strftime * (use as strftime) * * @param string $date String date to format * @param string $format Date format strftime format * * @return array|void date info */ public static function strToDateTime($date, $format) { $weekdays = array('Sun' => '0', 'Mon' => '1', 'Tue' => '2', 'Wed' => '3', 'Thu' => '4', 'Fri' => '5', 'Sat' => '6'); if (!($date = self::str2Time($date, $format))) { return; } $shortMonths = array(Text::_('Jan'), Text::_('Feb'), Text::_('Mar'), Text::_('Apr'), Text::_('May'), Text::_('Jun'), Text::_('Jul'), Text::_('Aug'), Text::_('Sept'), Text::_('Oct'), Text::_('Nov'), Text::_('Dec')); /*$$ rob set day default to 1, so that if you have a date format string of %m-%Y the day is set to the first day of the month * and not the last day of the previous month (which is what a 0 here would represent) */ $dateTime = array('sec' => 0, 'min' => 0, 'hour' => 0, 'day' => 1, 'mon' => 0, 'year' => 0, 'timestamp' => 0); foreach ($date as $key => $val) { switch ($key) { case 'd': case 'e': case 'j': $dateTime['day'] = intval($val); break; case 'D': $dateTime['day'] = intval($weekdays[$val]); break; case 'm': case 'n': $dateTime['mon'] = intval($val); break; case 'b': $dateTime['mon'] = $shortMonths[$val] + 1; break; case 'Y': $dateTime['year'] = intval($val); break; case 'y': $dateTime['year'] = intval($val) + 2000; break; case 'G': case 'g': case 'H': case 'h': $dateTime['hour'] = intval($val); break; case 'M': $dateTime['min'] = intval($val); break; case 'i': $dateTime['min'] = intval($val); break; case 's': case 'S': $dateTime['sec'] = intval($val); break; } } $dateTime['timestamp'] = mktime($dateTime['hour'], $dateTime['min'], $dateTime['sec'], $dateTime['mon'], $dateTime['day'], $dateTime['year']); return $dateTime; } /** * Check for, and convert, any 'special' formats for strtotime, like 'yesterday', etc. * * @param string $date Date to check * @param bool $gmt Set date to universal time? * * @return string date */ public static function specialStrToMySQL($date, $gmt = true) { /** * $$$ hugh - if date is empty, just return today's date */ if (empty($date)) { $d = JFactory::getDate(); $date = $d->toSql(!$gmt); return $date; } /** * lets check if we have some special text as per : * http://php.net/strtotime - this means we can use "+2 week" as a url filter * do this before we urldecode the date otherwise the + is replaced with ' '; */ $matches = array(); $matches2 = array(); $matches3 = array(); // E.g. now preg_match("/(now|ago|midnight|yesterday|today)/i", $date, $matches); // E.g. +2 Week preg_match("/[+|-][0-9]* (week\b|year\b|day\b|month\b)/i", $date, $matches2); // E.g. next Wednesday preg_match("/[next|last]* (monday\b|tuesday\b|wednesday\b|thursday\b|friday\b|saturday\b|sunday\b)/i", $date, $matches3); $matches = array_merge($matches, $matches2, $matches3); if (!empty($matches)) { $d = JFactory::getDate($date); $date = $d->toSql(!$gmt); } return $date; } /** * String to time * * @param string $date Date representation * @param string $format Date format * * @return array date bits keyed on date representations e.g. m/d/Y */ public static function str2Time($date, $format) { /** * lets check if we have some special text as per : * http://php.net/strtotime - this means we can use "+2 week" as a url filter * do this before we urldecode the date otherwise the + is replaced with ' '; */ $matches = array(); $matches2 = array(); $matches3 = array(); // E.g. now preg_match("/[now|ago|midnight|yesterday|today]/i", $date, $matches); // E.g. +2 Week preg_match("/[+|-][0-9]* (week\b|year\b|day\b|month\b)/i", $date, $matches2); // E.g. next Wednesday preg_match("/[next|last]* (monday\b|tuesday\b|wednesday\b|thursday\b|friday\b|saturday\b|sunday\b)/i", $date, $matches3); $matches = array_merge($matches, $matches2, $matches3); if (!empty($matches)) { $d = JFactory::getDate($date); $date = $d->format($format); } /* $$$ - hugh : urldecode (useful when ajax calls, may need better fix) * as per http://fabrikar.com/forums/showthread.php?p=43314#post43314 */ $date = urldecode($date); // Strip any textual date representations from the string $days = array('%A', '%a'); foreach ($days as $day) { if (strstr($format, $day)) { $format = str_replace($day, '', $format); $date = self::stripDay($date, $day == '%a' ? true : false); } } $months = array('%B', '%b', '%h'); foreach ($months as $month) { if (strstr($format, $month)) { $format = str_replace($month, '%m', $format); $date = self::monthToInt($date, $month == '%B' ? false : true); } } // @TODO: some of these aren't right for strftime self::$finalFormat = $format; $search = array('%d', '%e', '%D', '%j', '%m', '%b', '%Y', '%y', '%g', '%H', '%h', '%i', '%s', '%S', '%M'); $replace = array('(\d{2})', '(\d{1,2})', '(\w{3})', '(\d{1,2})', '(\d{2})', '(\w{3})', '(\d{4})', '(\d{2})', '(\d{1,2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})', '(\d{2})'); $pattern = str_replace($search, $replace, $format); if (!preg_match("#$pattern#", $date, $matches)) { // Lets allow for partial date formats - e.g. just the date and ignore the time $format = explode('%', $format); if (empty($format)) { // No format left to test so return false return false; } array_pop($format); $format = trim(implode('%', $format)); self::$finalFormat = $format; return self::str2Time($date, $format); } $dp = $matches; if (!preg_match_all('#%(\w)#', $format, $matches)) { return false; } $id = $matches['1']; if (count($dp) != count($id) + 1) { return false; } $ret = array(); for ($i = 0, $j = count($id); $i < $j; $i++) { $ret[$id[$i]] = $dp[$i + 1]; } return $ret; } /** * Removed day of week name from string * * @param string $date The string date * @param bool $abrv Abbreviated day? * * @return string date */ public static function stripDay($date, $abrv = false) { if ($abrv) { $date = str_replace(Text::_('SUN'), '', $date); $date = str_replace(Text::_('MON'), '', $date); $date = str_replace(Text::_('TUE'), '', $date); $date = str_replace(Text::_('WED'), '', $date); $date = str_replace(Text::_('THU'), '', $date); $date = str_replace(Text::_('FRI'), '', $date); $date = str_replace(Text::_('SAT'), '', $date); } else { $date = str_replace(Text::_('SUNDAY'), '', $date); $date = str_replace(Text::_('MONDAY'), '', $date); $date = str_replace(Text::_('TUESDAY'), '', $date); $date = str_replace(Text::_('WEDNESDAY'), '', $date); $date = str_replace(Text::_('THURSDAY'), '', $date); $date = str_replace(Text::_('FRIDAY'), '', $date); $date = str_replace(Text::_('SATURDAY'), '', $date); } return $date; } /** * Convert a month (could be in any language) into the month number (1 = jan) * * @param string $date Data to convert * @param bool $abrv Is the month is a short or full name version * * @return string */ public static function monthToInt($date, $abrv = false) { if ($abrv) { $date = str_replace(Text::_('JANUARY_SHORT'), '01', $date); $date = str_replace(Text::_('FEBRUARY_SHORT'), '02', $date); $date = str_replace(Text::_('MARCH_SHORT'), '03', $date); $date = str_replace(Text::_('APRIL_SHORT'), '04', $date); $date = str_replace(Text::_('MAY_SHORT'), '05', $date); $date = str_replace(Text::_('JUNE_SHORT'), '06', $date); $date = str_replace(Text::_('JULY_SHORT'), '07', $date); $date = str_replace(Text::_('AUGUST_SHORT'), '08', $date); $date = str_replace(Text::_('SEPTEMBER_SHORT'), '09', $date); $date = str_replace(Text::_('OCTOBER_SHORT'), 10, $date); $date = str_replace(Text::_('NOVEMBER_SHORT'), 11, $date); $date = str_replace(Text::_('DECEMBER_SHORT'), 12, $date); } else { $date = str_replace(Text::_('JANUARY'), '01', $date); $date = str_replace(Text::_('FEBRUARY'), '02', $date); $date = str_replace(Text::_('MARCH'), '03', $date); $date = str_replace(Text::_('APRIL'), '04', $date); $date = str_replace(Text::_('MAY'), '05', $date); $date = str_replace(Text::_('JUNE'), '06', $date); $date = str_replace(Text::_('JULY'), '07', $date); $date = str_replace(Text::_('AUGUST'), '08', $date); $date = str_replace(Text::_('SEPTEMBER'), '09', $date); $date = str_replace(Text::_('OCTOBER'), 10, $date); $date = str_replace(Text::_('NOVEMBER'), 11, $date); $date = str_replace(Text::_('DECEMBER'), 12, $date); } return $date; } /** * Check a string is not reserved by Fabrik * * @param string $str To check * @param bool $strict Include things like rowid, listid in the reserved words, defaults to true * * @return bool */ public static function isReserved($str, $strict = true) { $reservedWords = array("task", "view", "layout", "option", "formid", "submit", "ul_max_file_size" , "ul_file_types", "ul_directory", 'adddropdownvalue', 'adddropdownlabel', 'ul_end_dir'); /* * $$$ hugh - a little arbitrary, but need to be able to exclude these so people can create lists from things like * log files, which include field names like rowid and itemid. So when saving an element, we now set strict mode * to false if it's not a new element. */ $strictWords = array("listid", 'rowid', 'itemid'); if ($strict) { $reservedWords = array_merge($reservedWords, $strictWords); } if (in_array(StringHelper::strtolower($str), $reservedWords)) { return true; } return false; } /** * Check a string is valid to use as an element name * * @param string $str To check * @param bool $strict Include things like rowid, listid in the reserved words, defaults to true * * @return bool */ public static function validElementName($str, $strict = true) { // check if it's a Fabrik reserved word if (self::isReserved($str, $strict)) { return false; } // check valid MySQL - start with letter or _, then only alphanumeric or underscore if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $str)) { return false; } // check for various other gotchas, like ending in _raw, starting with more than one _, etc. if (preg_match('/^submit|^__|_raw$/', $str)) { return false; } return true; } /** * Get the crypt object * * @param string type type of encryption (aes, crypt or simple) * * @since 3.1 * * @return Fabrik\Helpers\FCipher */ public static function getCrypt($type = 'simple') { return new FCipher($type); } /** * Special case placeholder handling for repeat data. When something (usually an element plugin) is doing * replacements for elements which are in the "same" repeat group, almost always they will want * the value for the same repeat instance, not a comma seperated list of all the values. So (say) * the upload element is creating a file path, for an upload element in a repeat group, of ... * '/uploads/{repeat_table___userid}/', and there are 4 repeat instance, it doesn't want a path of ... * '/uploads/34,45,94,103/', it just wants the one value from the same repeat count as the upload * element. Or a calc element doing "return '{repeat_table___first_name} {repeat_table___last_name}';". Etc. * * Rather than make this a part of parseMessageForPlaceHolder, for now I'm making it a sperate function, * which just handles this one very specific data replacement. Will look at merging it in with the main * parsing once we have a better understanding of where / when / how to do it. * * @param string $msg Text to parse * @param array $searchData Data to search for placeholders * @param object $el Element model of the element which is doing the replacing * @param int $repeatCounter Repeat instance * * @return string parsed message */ public function parseMessageForRepeats($msg, $searchData, $el, $repeatCounter) { if (strstr($msg, '{') && !empty($searchData)) { $groupModel = $el->getGroupModel(); if ($groupModel->canRepeat()) { $elementModels = $groupModel->getPublishedElements(); $formModel = $el->getFormModel(); foreach ($elementModels as $elementModel) { $repeatElName = $elementModel->getFullName(true, false); foreach (array($repeatElName, $repeatElName . '_raw') as $tmpElName) { if (strstr($msg, '{' . $tmpElName . '}')) { if (array_key_exists($tmpElName, $searchData) && is_array($searchData[$tmpElName]) && array_key_exists($repeatCounter, $searchData[$tmpElName])) { $tmpVal = $searchData[$tmpElName][$repeatCounter]; if (is_array($tmpVal)) { $tmpVal = implode(',', $tmpVal); } $msg = str_replace('{' . $tmpElName . '}', $tmpVal, $msg); } } } } } } return $msg; } /** * Iterates through string to replace every * {placeholder} with posted data * * @param mixed $msg Text|Array to parse * @param array $searchData Data to search for placeholders (default $_REQUEST) * @param bool $keepPlaceholders If no data found for the place holder do we keep the {...} string in the * message * @param bool $addSlashes Add slashed to the text? * @param object $theirUser User to use in replaceWithUserData (defaults to logged in user) * @param bool $unsafe If true (default) will not replace certain placeholders like $jConfig_secret * must not be shown to users * * @return string parsed message */ public function parseMessageForPlaceHolder($msg, $searchData = null, $keepPlaceholders = true, $addSlashes = false, $theirUser = null, $unsafe = true) { $returnType = is_array($msg) ? 'array' : 'string'; $messages = (array) $msg; foreach ($messages as &$msg) { $this->parseAddSlashes = $addSlashes; if (!($msg == '' || is_array($msg) || StringHelper::strpos($msg, '{') === false)) { $msg = str_replace(array('%7B', '%7D'), array('{', '}'), $msg); if (is_object($searchData)) { $searchData = ArrayHelper::fromObject($searchData); } // Merge in request and specified search data $f = JFilterInput::getInstance(); $post = $f->clean($_REQUEST, 'array'); $this->_searchData = is_null($searchData) ? $post : array_merge($post, $searchData); // Enable users to use placeholder to insert session token $this->_searchData['JSession::getFormToken'] = JSession::getFormToken(); // Replace with the user's data $msg = self::replaceWithUserData($msg); if (!is_null($theirUser)) { // Replace with a specified user's data $msg = self::replaceWithUserData($msg, $theirUser, 'your'); } $msg = self::replaceWithGlobals($msg); if (!$unsafe) { $msg = self::replaceWithUnsafe($msg); $msg = self::replaceWithSession($msg); } $msg = preg_replace("/{}/", "", $msg); // Replace {element name} with form data $msg = preg_replace_callback("/{([^}\s]+(\|\|[\w|\s]+)*)}/i", array($this, 'replaceWithFormData'), $msg); if (!$keepPlaceholders) { $msg = preg_replace("/{[^}\s]+}/i", '', $msg); } } } return $returnType === 'array' ? $messages : ArrayHelper::getValue($messages, 0, ''); } /** * Replace {varname} with request data (called from J content plugin) * * @param string &$msg String to parse * * @return void */ public function replaceRequest(&$msg) { static $request; if (!is_array($request)) { $request = array(); $f = JFilterInput::getInstance(); foreach ($_REQUEST as $k => $v) { if (is_string($v)) { $request[$k] = $f->clean($v, 'CMD'); } } } foreach ($request as $key => $val) { if (is_string($val)) { // $$$ hugh - escape the key so preg_replace won't puke if key contains / $key = str_replace('/', '\/', $key); $msg = preg_replace("/\{$key\}/", $val, $msg); } } } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with user ($my) data * AND * {$their->var->email} placeholders * * @param string $msg Message to parse * @param object $user Joomla user object * @param string $prefix Search string to look for e.g. 'my' to look for {$my->id} * * @return string parsed message */ public static function replaceWithUserData($msg, $user = null, $prefix = 'my') { $app = JFactory::getApplication(); if (is_null($user)) { $user = JFactory::getUser(); } $user->levels = $user->getAuthorisedViewLevels(); if (is_object($user)) { foreach ($user as $key => $val) { if (substr($key, 0, 1) != '_') { if (!is_object($val) && !is_array($val)) { $msg = str_replace('{$' . $prefix . '->' . $key . '}', $val, $msg); $msg = str_replace('{$' . $prefix . '->' . $key . '}', $val, $msg); } elseif (is_array($val)) { $msg = str_replace('{$' . $prefix . '->' . $key . '}', implode(',', $val), $msg); $msg = str_replace('{$' . $prefix . '->' . $key . '}', implode(',', $val), $msg); } } } } /* * $$$rob parse another users data into the string: * format: is {$their->var->email} where var is the $app->input var to search for * e.g url - index.php?owner=62 with placeholder {$their->owner->id} * var should be an integer corresponding to the user id to load */ $matches = array(); preg_match('/{\$their-\>(.*?)}/', $msg, $matches); foreach ($matches as $match) { $bits = explode('->', str_replace(array('{', '}'), '', $match)); if (count($bits) !== 3) { continue; } $userId = $app->input->getInt(ArrayHelper::getValue($bits, 1)); // things like user elements might be single entry arrays if (is_array($userId)) { $userId = array_pop($userId); } if (!empty($userId)) { $user = JFactory::getUser($userId); $val = $user->get(ArrayHelper::getValue($bits, 2)); $msg = str_replace($match, $val, $msg); } } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with global data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithGlobals($msg) { $replacements = self::globalReplacements(); foreach ($replacements as $key => $value) { $msg = str_replace($key, $value, $msg); } return $msg; } /** * Utility function for replacing language tags. * {lang} - Joomla code for user's selected language, like en-GB * {langtag} - as {lang} with with _ instead of - * {shortlang} - first two letters of {lang}, like en * {multilang} - multilang URL code * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithLanguageTags($msg) { $replacements = self::langReplacements(); foreach ($replacements as $key => $value) { $msg = str_replace($key, $value, $msg); } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with unsafe data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithUnsafe($msg) { $replacements = self::unsafeReplacements(); foreach ($replacements as $key => $value) { $msg = str_replace($key, $value, $msg); } return $msg; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with session data * * @param string $msg Message to parse * * @return string parsed message */ public static function replaceWithSession($msg) { if (strstr($msg, '{$session->')) { $session = JFactory::getSession(); $sessionData = array( 'id' => $session->getId(), 'token' => $session->get('session.token'), 'formtoken' => JSession::getFormToken() ); foreach ($sessionData as $key => $value) { $msg = str_replace('{$session->' . $key . '}', $value, $msg); } $msg = preg_replace_callback( '/{\$session-\>(.*?)}/', function($matches) use ($session) { $bits = explode(':', $matches[1]); if (count($bits) > 1) { $sessionKey = $bits[1]; $nameSpace = $bits[0]; } else { $sessionKey = $bits[0]; $nameSpace = 'default'; } $val = $session->get($sessionKey, '', $nameSpace); if (is_string($val)) { return $val; } else if (is_numeric($val)) { return (string) $val; } return ''; }, $msg ); } return $msg; } /** * Get an associative array of replacements for 'unsafe' value, like $jConfig_secret, which we * only want to use for stricty internal use that won't ever get shown to the user * * @return array * @throws \Exception */ public static function unsafeReplacements() { $config = JFactory::getConfig(); $replacements = array( '{$jConfig_absolute_path}' => JPATH_SITE, '{$jConfig_secret}' => $config->get('secret') ); return $replacements; } /** * Get an associative array of replacements strings and values * * @return array * @throws \Exception */ public static function globalReplacements() { $app = JFactory::getApplication(); $itemId = self::itemId(); $config = JFactory::getConfig(); $session = JFactory::getSession(); $token = $session->get('session.token'); $replacements = array( '{$jConfig_live_site}' => COM_FABRIK_LIVESITE, '{$jConfig_offset}' => $config->get('offset'), '{$Itemid}' => $itemId, '{$jConfig_sitename}' => $config->get('sitename'), '{$jConfig_mailfrom}' => $config->get('mailfrom'), '{where_i_came_from}' => $app->input->server->get('HTTP_REFERER', '', 'string'), '{date}' => date('Ymd'), '{year}' => date('Y'), '{mysql_date}' => date('Y-m-d H:i:s'), '{session.token}' => $token ); foreach ($_SERVER as $key => $val) { if (!is_object($val) && !is_array($val)) { $replacements['{$_SERVER->' . $key . '}'] = $val; $replacements['{$_SERVER->' . $key . '}'] = $val; } } if ($app->isClient('administrator')) { $replacements['{formview}'] = 'task=form.view'; $replacements['{listview}'] = 'task=list.view'; $replacements['{detailsview}'] = 'task=details.view'; } else { $replacements['{formview}'] = 'view=form'; $replacements['{listview}'] = 'view=list'; $replacements['{detailsview}'] = 'view=details'; } return array_merge($replacements, self::langReplacements()); } /** * Returns array of language tag replacements * * @return array */ public static function langReplacements() { $langtag = JFactory::getLanguage()->getTag(); $lang = str_replace('-', '_', $langtag); $shortlang = explode('_', $lang); $shortlang = $shortlang[0]; $multilang = Worker::getMultiLangURLCode(); $replacements = array( '{lang}' => $lang, '{langtag}' => $langtag, '{multilang}' => $multilang, '{shortlang}' => $shortlang, ); return $replacements; } /** * Called from parseMessageForPlaceHolder to iterate through string to replace * {placeholder} with posted data * * @param string $matches Placeholder e.g. {placeholder} * * @return string posted data that corresponds with placeholder */ protected function replaceWithFormData($matches) { // Merge any join data key val pairs down into the main data array $joins = ArrayHelper::getValue($this->_searchData, 'join', array()); foreach ($joins as $k => $data) { foreach ($data as $k => $v) { /* * Only replace if we haven't explicitly set the key in _searchData. * Otherwise, calc element in repeat group uses all repeating groups values rather than the * current one that the plugin sets when it fire its Ajax request. */ if (!array_key_exists($k, $this->_searchData)) { $this->_searchData[$k] = $v; } } } $match = $matches[0]; $orig = $match; // Strip the {} $match = StringHelper::substr($match, 1, StringHelper::strlen($match) - 2); /* $$$ hugh - added dbprefix substitution * Not 100% if we should do this on $match before copying to $orig, but for now doing it * after, so we don't potentially disclose dbprefix if no substitution found. */ $config = JFactory::getConfig(); $prefix = $config->get('dbprefix'); $match = str_replace('#__', $prefix, $match); // $$$ rob test this format searchvalue||defaultsearchvalue $bits = explode('||', $match); if (count($bits) == 2) { $match = self::parseMessageForPlaceHolder('{' . $bits[0] . '}', $this->_searchData, false); if (in_array($match, array('', '<ul></ul>', '<ul><li></li></ul>'))) { return $bits[1] !== '' ? $bits[1] : $orig; } else { return $match !== '' ? $match : $orig; } } $match = preg_replace("/ /", "_", $match); if (!strstr($match, '.')) { // For some reason array_key_exists wasn't working for nested arrays?? $aKeys = array_keys($this->_searchData); // Remove the table prefix from the post key $aPrefixFields = array(); for ($i = 0; $i < count($aKeys); $i++) { $aKeyParts = explode('___', $aKeys[$i]); if (count($aKeyParts) == 2) { $tablePrefix = array_shift($aKeyParts); $field = array_pop($aKeyParts); $aPrefixFields[$field] = $tablePrefix; } } if (array_key_exists($match, $aPrefixFields)) { $match = $aPrefixFields[$match] . '___' . $match; } // Test to see if the made match is in the post key arrays $found = in_array($match, $aKeys, true); if ($found) { // Get the post data $match = $this->_searchData[$match]; if (is_array($match)) { $newMatch = ''; // Deal with radio boxes etc. inside repeat groups foreach ($match as $m) { if (is_array($m)) { $newMatch .= ',' . implode(',', $m); } else { $newMatch .= ',' . $m; } } $match = StringHelper::ltrim($newMatch, ','); } } else { $match = ''; } } else { // Could be looking for URL field type e.g. for $_POST[url][link] the match text will be url.link $aMatch = explode('.', $match); $aPost = $this->_searchData; foreach ($aMatch as $sPossibleArrayKey) { if (is_array($aPost)) { if (!isset($aPost[$sPossibleArrayKey])) { return $orig; } else { $aPost = $aPost[$sPossibleArrayKey]; } } } $match = $aPost; $found = true; } if ($this->parseAddSlashes) { $match = htmlspecialchars($match, ENT_QUOTES, 'UTF-8'); } return $found ? $match : $orig; } /** * Internal function to recursive scan directories * * @param string $imagePath Image path * @param string $folderPath Path to scan * @param string &$folders Root path of this folder * @param array &$images Value array of all existing folders * @param array $aFolderFilter Value array of all existing images * @param bool $makeOptions Make options out for the results * * @return void */ public static function readImages($imagePath, $folderPath, &$folders, &$images, $aFolderFilter, $makeOptions = true) { $imgFiles = self::fabrikReadDirectory($imagePath, '.', false, false, $aFolderFilter); foreach ($imgFiles as $file) { $ff_ = $folderPath . $file . '/'; $ff = $folderPath . $file; $i_f = $imagePath . '/' . $file; if (is_dir($i_f) && $file != 'CVS' && $file != '.svn') { if (!in_array($file, $aFolderFilter)) { $folders[] = JHTML::_('select.option', $ff_); self::readImages($i_f, $ff_, $folders, $images, $aFolderFilter); } } elseif (preg_match('/bmp|gif|jpg|png/i', $file) && is_file($i_f)) { // Leading / we don't need $imageFile = StringHelper::substr($ff, 1); $images[$folderPath][] = $makeOptions ? JHTML::_('select.option', $imageFile, $file) : $file; } } } /** * Utility function to read the files in a directory * * @param string $path The file system path * @param string $filter A filter for the names * @param bool $recurse Recurse search into sub-directories * @param bool $fullPath True if to prepend the full path to the file name * @param array $aFolderFilter Folder names not to recurse into * @param bool $foldersOnly Return a list of folders only (true) * * @return array of file/folder names */ public static function fabrikReadDirectory($path, $filter = '.', $recurse = false, $fullPath = false, $aFolderFilter = array(), $foldersOnly = false) { $arr = array(); if (!@is_dir($path)) { return $arr; } $handle = opendir($path); while ($file = readdir($handle)) { $dir = JPath::clean($path . '/' . $file); $isDir = is_dir($dir); if ($file != "." && $file != "..") { if (preg_match("/$filter/", $file)) { if (($isDir && $foldersOnly) || !$foldersOnly) { if ($fullPath) { $arr[] = trim(JPath::clean($path . '/' . $file)); } else { $arr[] = trim($file); } } } $goDown = true; if ($recurse && $isDir) { foreach ($aFolderFilter as $sFolderFilter) { if (strstr($dir, $sFolderFilter)) { $goDown = false; } } if ($goDown) { $arr2 = self::fabrikReadDirectory($dir, $filter, $recurse, $fullPath, $aFolderFilter, $foldersOnly); $arrDiff = array_diff($arr, $arr2); $arr = array_merge($arrDiff); } } } } closedir($handle); asort($arr); return $arr; } /** * Joomfish translations don't seem to work when you do an ajax call * it seems to load the geographical location language rather than the selected lang * so for ajax calls that need to use jf translated text we need to get the current lang and * send it to the js code which will then append the lang=XX to the ajax querystring * * Renamed to getShortLang as we don't support Joomfish any more * * @since 2.0.5 * * @return string first two letters of lang code - e.g. nl from 'nl-NL' */ public static function getShortLang() { $lang = JFactory::getLanguage(); $lang = explode('-', $lang->getTag()); return array_shift($lang); } /** * If J! multiple languages is enabled, return the URL language code for the currently selected language, which is * set by the admin in the 'content languages'. If not multi lang, return false; * * @return boolean || string */ public static function getMultiLangURLCode() { $multiLang = false; if (JLanguageMultilang::isEnabled()) { $lang = JFactory::getLanguage()->getTag(); $languages = JLanguageHelper::getLanguages(); foreach ($languages as $language) { if ($language->lang_code === $lang) { $multiLang = $language->sef; break; } } } return $multiLang; } /** * Get the content filter used both in form and admin pages for content filter * takes values from J content filtering options * * @return array (bool should the filter be used, object the filter to use) */ public static function getContentFilter() { $filter = false; // Filter settings jimport('joomla.application.component.helper'); // Get Config and Filters in Joomla 2.5 $config = JComponentHelper::getParams('com_config'); $filters = $config->get('filters'); // If no filter data found, get from com_content (Joomla 1.6/1.7 sites) if (empty($filters)) { $contentParams = JComponentHelper::getParams('com_content'); $filters = $contentParams->get('filters'); } $user = JFactory::getUser(); $userGroups = JAccess::getGroupsByUser($user->get('id')); $blackListTags = array(); $blackListAttributes = array(); $whiteListTags = array(); $whiteListAttributes = array(); $whiteList = false; $blackList = false; $unfiltered = false; // Cycle through each of the user groups the user is in. // Remember they are include in the Public group as well. foreach ($userGroups AS $groupId) { // May have added a group by not saved the filters. if (!isset($filters->$groupId)) { continue; } // Each group the user is in could have different filtering properties. $filterData = $filters->$groupId; $filterType = StringHelper::strtoupper($filterData->filter_type); if ($filterType == 'NH') { // Maximum HTML filtering. } elseif ($filterType == 'NONE') { // No HTML filtering. $unfiltered = true; } else { // Black or white list. // Pre-process the tags and attributes. $tags = explode(',', $filterData->filter_tags); $attributes = explode(',', $filterData->filter_attributes); $tempTags = array(); $tempAttributes = array(); foreach ($tags as $tag) { $tag = trim($tag); if ($tag) { $tempTags[] = $tag; } } foreach ($attributes as $attribute) { $attribute = trim($attribute); if ($attribute) { $tempAttributes[] = $attribute; } } // Collect the black or white list tags and attributes. // Each list is cumulative. if ($filterType == 'BL') { $blackList = true; $blackListTags = array_merge($blackListTags, $tempTags); $blackListAttributes = array_merge($blackListAttributes, $tempAttributes); } elseif ($filterType == 'WL') { $whiteList = true; $whiteListTags = array_merge($whiteListTags, $tempTags); $whiteListAttributes = array_merge($whiteListAttributes, $tempAttributes); } } } // Remove duplicates before processing (because the black list uses both sets of arrays). $blackListTags = array_unique($blackListTags); $blackListAttributes = array_unique($blackListAttributes); $whiteListTags = array_unique($whiteListTags); $whiteListAttributes = array_unique($whiteListAttributes); // Unfiltered assumes first priority. if ($unfiltered) { $doFilter = false; // Don't apply filtering. } else { $doFilter = true; // Black lists take second precedence. if ($blackList) { // Remove the white-listed attributes from the black-list. $tags = array_diff($blackListTags, $whiteListTags); $filter = JFilterInput::getInstance($tags, array_diff($blackListAttributes, $whiteListAttributes), 1, 1); } // White lists take third precedence. elseif ($whiteList) { // Turn off xss auto clean $filter = JFilterInput::getInstance($whiteListTags, $whiteListAttributes, 0, 0, 0); } // No HTML takes last place. else { $filter = JFilterInput::getInstance(); } } return array($doFilter, $filter); } /** * Clear PHP errors prior to running eval'd code * * @return void */ public static function clearEval() { /** * "Clear" PHP's errors. NOTE that error_get_last() will still return non-null after this * if there were any errors, but $error['message'] will be empty. See comment in logEval() * below for details. */ @trigger_error(""); } /** * Raise a J Error notice if the eval'd result is false and there is a error * * @param mixed $val Evaluated result * @param string $msg Error message, should contain %s as we sprintf in the error_get_last()'s message property * * @return void */ public static function logEval($val, $msg) { if ($val !== false) { return; } $error = error_get_last(); /** * $$$ hugh - added check for 'message' being empty, so we can do .. * @trigger_error(''); * ... prior to eval'ing code if we want to "clear" anything pitched prior * to the eval. For instance, in the PHP validation plugin. If we don't "clear" * the errors before running the eval'd validation code, we end up reporting any * warnings or notices pitched in our code prior to the validation running, which * can be REALLY confusing. After a trigger_error(), error_get_last() won't return null, * but 'message' will be empty. */ if (is_null($error) || empty($error['message'])) { // No error set (eval could have actually returned false as a correct value) return; } $enqMsgType = 'error'; $indentHTML = '<br/> Debug: '; $errString = Text::_('COM_FABRIK_EVAL_ERROR_USER_WARNING'); // Give a technical error message to the developer if (version_compare(phpversion(), '5.2.0', '>=') && $error && is_array($error)) { $errString .= $indentHTML . sprintf($msg, $error['message']); } else { $errString .= $indentHTML . sprintf($msg, "unknown error - php version < 5.2.0"); } self::logError($errString, $enqMsgType); } /** * Raise a J Error notice if in dev mode or log a J error otherwise * * @param string $errString Message to display / log * @param string $msgType Joomla enqueueMessage message type e.g. 'error', 'warning' etc. * * @return void */ public static function logError($errString, $msgType) { if (Html::isDebug()) { $app = JFactory::getApplication(); $app->enqueueMessage($errString, $msgType); } else { switch ($msgType) { case 'message': $priority = JLog::INFO; break; case 'warning': $priority = JLog::WARNING; break; case 'error': default: $priority = JLog::ERROR; break; } JLog::add($errString, $priority, 'com_fabrik'); } } /** * Log to table jos_fabrik_logs * * @param string $type E.g. 'fabrik.fileupload.download' * @param mixed $msg Array/object/string * @param bool $jsonEncode Should we json encode the message? * * @return void */ public static function log($type, $msg, $jsonEncode = true) { if ($jsonEncode) { $msg = json_encode($msg); } $log = FabTable::getInstance('log', 'FabrikTable'); $log->message_type = $type; $log->message = $msg; $log->store(); } /** * Get a database object * * Returns the global {@link JDatabase} object, only creating it * if it doesn't already exist. * * @param bool $loadJoomlaDb Force (if true) the loading of the main J database, * needed in admin to connect to J db whilst still using fab db drivers "{package}" * replacement text * * @param mixed $cnnId If null then loads the fabrik default connection, if an int then loads the * specified connection by its id * * @return JDatabaseDriver object */ public static function getDbo($loadJoomlaDb = false, $cnnId = null) { $sig = (int) $loadJoomlaDb . '.' . $cnnId; if (!self::$database) { self::$database = array(); self::$database = array(); } if (!array_key_exists($sig, self::$database)) { JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_fabrik/tables'); $conf = JFactory::getConfig(); if (!$loadJoomlaDb) { $cnModel = JModelLegacy::getInstance('Connection', 'FabrikFEModel'); $cn = $cnModel->getConnection($cnnId); $host = $cn->host; $user = $cn->user; $password = $cn->password; $database = $cn->database; } else { $host = $conf->get('host'); $user = $conf->get('user'); $password = $conf->get('password'); $database = $conf->get('db'); } $dbPrefix = $conf->get('dbprefix'); $driver = $conf->get('dbtype'); // Test for swapping db table names $driver .= '_fab'; $options = array('driver' => $driver, 'host' => $host, 'user' => $user, 'password' => $password, 'database' => $database, 'prefix' => $dbPrefix); $version = new JVersion; self::$database[$sig] = $version->RELEASE > 2.5 ? JDatabaseDriver::getInstance($options) : JDatabase::getInstance($options); Worker::bigSelects(self::$database[$sig]); } return self::$database[$sig]; } /** * $$$ hugh - testing doing bigSelects stuff here * Reason being, some folk on shared hosting plans with very restrictive MySQL * setups are hitting the 'big selects' problem on Fabrik internal queries, not * just on their List specific queries. So we need to apply 'big selects' to our * default connection as well, essentially enabling it for ALL queries we do. * * @param JDatabaseDriver $fabrikDb * * @return void */ public static function bigSelects($fabrikDb) { $fbConfig = JComponentHelper::getParams('com_fabrik'); if ($fbConfig->get('enable_big_selects', 0) == '1') { /** * Use of OPTION in SET deprecated from MySQL 5.1. onward * http://www.fabrikar.com/forums/index.php?threads/enable-big-selects-error.39463/#post-198293 * NOTE - technically, using verison_compare on MySQL version could fail, if it's a "gamma" * release, which PHP desn't grok! */ if (version_compare($fabrikDb->getVersion(), '5.1.0', '>=')) { $fabrikDb->setQuery("SET SQL_BIG_SELECTS=1, GROUP_CONCAT_MAX_LEN=10240"); } else { $fabrikDb->setQuery("SET OPTION SQL_BIG_SELECTS=1, GROUP_CONCAT_MAX_LEN=10240"); } try { $fabrikDb->execute(); } catch (\Exception $e) { // Fail silently } } } /** * Helper function get get a connection * * @param mixed $item A list table or connection id * * @since 3.0b * * @return FabrikFEModelConnection connection */ public static function getConnection($item = null) { $app = JFactory::getApplication(); $input = $app->input; $jForm = $input->get('jform', array(), 'array'); if (is_object($item)) { $item = is_null($item->connection_id) ? ArrayHelper::getValue($jForm, 'connection_id', -1) : $item->connection_id; } $connId = (int) $item; if (!self::$connection) { self::$connection = array(); } if (!array_key_exists($connId, self::$connection)) { $connectionModel = JModelLegacy::getInstance('connection', 'FabrikFEModel'); $connectionModel->setId($connId); if ($connId === -1) { // -1 for creating new table $connectionModel->loadDefaultConnection(); $connectionModel->setId($connectionModel->getConnection()->id); } $connectionModel->getConnection(); self::$connection[$connId] = $connectionModel; } return self::$connection[$connId]; } /** * Get the plugin manager * * @since 3.0b * * @return FabrikFEModelPluginmanager Plugin manager */ public static function getPluginManager() { if (!self::$pluginManager) { self::$pluginManager = JModelLegacy::getInstance('Pluginmanager', 'FabrikFEModel'); } return self::$pluginManager; } /** * Takes a string which may or may not be json and returns either string/array/object * will also turn valGROUPSPLITTERval2 to array * * @param string $data Json encoded string * @param bool $toArray Force data to be an array * @param bool $emptyish Set to false to return an empty array if $data is an empty string, instead of an * emptyish (one empty string entry) array * * @return mixed data */ public static function JSONtoData($data, $toArray = false, $emptyish = true) { if (is_string($data)) { if (!strstr($data, '{')) { // Was messing up date rendering @ http://www.podion.eu/dev2/index.php/2011-12-19-10-33-59/actueel // return $toArray ? (array) $data : $data; } // Repeat elements are concatenated with the GROUPSPLITTER - convert to json string before continuing. if (strstr($data, GROUPSPLITTER)) { $data = json_encode(explode(GROUPSPLITTER, $data)); } /* half hearted attempt to see if string is actually json or not. * issue was that if you try to decode '000123' its turned into '123' */ if (strstr($data, '{') || strstr($data, '[')) { $json = json_decode($data); // Only works in PHP5.3 // $data = (json_last_error() == JSON_ERROR_NONE) ? $json : $data; if (is_null($json)) { /* * if coming back from a failed validation - the json string may have been htmlspecialchars_encoded in * the form model getGroupView method */ $json = json_decode(stripslashes(htmlspecialchars_decode($data, ENT_QUOTES))); } $data = is_null($json) ? $data : $json; } // If $data was an empty string and "emptyish" is not set, we want an empty array, not an array with one empty string if ($toArray && !$emptyish && $data === '') { $data = array(); } } $data = $toArray ? (array) $data : $data; return $data; } /** * Test if a string is a compatible date * * @param string $d Date to test * @param bool $notNull don't allow null / empty dates * * @return bool */ public static function isNullDate($d) { $db = self::getDbo(); $aNullDates = array('0000-00-000000-00-00', '0000-00-00 00:00:00', '0000-00-00', '', $db->getNullDate()); return in_array($d, $aNullDates); } /** * Test if a string is a compatible date * * @param string $d Date to test * @param bool $notNull don't allow null / empty dates * * @return bool */ public static function isDate($d, $notNull = true) { // Catch for ',' if (strlen($d) < 2) { return false; } if ($notNull && self::isNullDate($d)) { return false; } try { $dt = new DateTime($d); } catch (\Exception $e) { return false; } return true; } public static function addMonthsInterval($months, DateTime $date) { $next = new DateTime($date->format('d-m-Y H:i:s')); $next->modify('last day of +' . $months . ' month'); if ($date->format('d') > $next->format('d')) { return $date->diff($next); } else { return new DateInterval('P' . $months . 'M'); } } public static function addMonths($months, DateTime $date) { return $date->add(self::addMonthsInterval($months, $date)); } /** * Get a user's TZ offset in MySql format, suitable for CONVERT_TZ * * @param int userId userid or null (use logged on user if null) * * @return string symbolic timezone name (America/Chicago) */ public static function getUserTzOffsetMySql($userId = null) { $tz = self::getUserTzName($userId); $tz = new \DateTimeZone($tz); $date = new \DateTime("now", $tz); $offset = $tz->getOffset($date) . ' seconds'; $dateOffset = clone $date; $dateOffset->sub(\DateInterval::createFromDateString($offset)); $interval = $dateOffset->diff($date); return $interval->format('%R%H:%I'); } /** * Get a user's TZ offset in seconds * * @param int userId userid or null (use logged on user if null) * * @return int seconds offset */ public static function getUserTzOffset($userId = null) { $tz = self::getUserTzName($userId); $tz = new \DateTimeZone($tz); $date = new \DateTime("now", $tz); return $tz->getOffset($date); } /** * Get a user's TZ name * * @param int userId userid or null (use logged on user if null) * * @return string symbolic timezone name (America/Chicago) */ public static function getUserTzName($userId = null) { if (empty($userId)) { $user = JFactory::getUser(); } else { $user = JFactory::getUser($userId); } $config = JFactory::getConfig(); $tz = $user->getParam('timezone', $config->get('offset')); return $tz; } /** * See if data is JSON or not. * * @param mixed $data Date to test * @params bool $quotedString should we treat a single quoted string as JSON * @since 3.0.6 * * @return bool */ public static function isJSON($data, $quotedString = true) { if (!is_string($data)) { return false; } if (is_numeric($data)) { return false; } if (!$quotedString) { $data = trim($data, '"'); } return json_decode($data) !== null; } /** * Is the email really an email (more strict than JMailHelper::isEmailAddress()) * * @param string $email Email address * @param bool $sms test for SMS phone number instead of email, default false * * @since 3.0.4 * * @return bool */ public static function isEmail($email, $sms = false) { if ($sms) { return self::isSMS($email); } $conf = JFactory::getConfig(); $mailer = $conf->get('mailer'); if ($mailer === 'mail') { // Sendmail and Joomla isEmailAddress don't use the same conditions return (JMailHelper::isEmailAddress($email) && JMail::ValidateAddress($email)); } return JMailHelper::isEmailAddress($email); } /** * Is valid SMS number format * This is just a stub which return true for now! * * @param string $sms SMS number * * @since 3.4.0 * * @return bool */ public static function isSMS($sms) { return true; } /** * Function to send an email * * @param string $from From email address * @param string $fromName From name * @param mixed $recipient Recipient email address(es) * @param string $subject email subject * @param string $body Message body * @param boolean $mode false = plain text, true = HTML * @param mixed $cc CC email address(es) * @param mixed $bcc BCC email address(es) * @param mixed $attachment Attachment file name(s) * @param mixed $replyTo Reply to email address(es) * @param mixed $replyToName Reply to name(s) * @param array $headers Optional custom headers, assoc array keyed by header name * * @return boolean True on success * * @since 11.1 */ public static function sendMail($from, $fromName, $recipient, $subject, $body, $mode = false, $cc = null, $bcc = null, $attachment = null, $replyTo = null, $replyToName = null, $headers = array()) { // do a couple of tweaks to improve spam scores // Get a JMail instance $mailer = JFactory::getMailer(); // If html, make sure there's an <html> tag if ($mode) { if (!stristr($body, '<html>')) { $body = '<html>' . $body . '</html>'; } } /** * if simple single email recipient with no name part, fake out name part to avoid TO_NO_BKRT hit in spam filters * (don't do it for sendmail, as sendmail only groks simple emails in To header!) */ $recipientName = ''; if ($mailer->Mailer !== 'sendmail' && is_string($recipient) && !strstr($recipient, '<')) { $recipientName = $recipient; } $mailer->setSubject($subject); $mailer->setBody($body); $mailer->Encoding = 'base64'; // Are we sending the email as HTML? $mailer->isHtml($mode); try { $mailer->addRecipient($recipient, $recipientName); } catch (\Exception $e) { return false; } try { $mailer->addCc($cc); } catch (\Exception $e) { // not sure if we should bail if Cc is bad, for now just soldier on } try { $mailer->addBcc($bcc); } catch (\Exception $e) { // not sure if we should bail if Bcc is bad, for now just soldier on } if (!empty($attachment)) { try { $mailer->addAttachment($attachment); } catch (\Exception $e) { // most likely file didn't exist, ignore } } $autoReplyTo = false; // Take care of reply email addresses if (is_array($replyTo)) { $numReplyTo = count($replyTo); for ($i = 0; $i < $numReplyTo; $i++) { try { $mailer->addReplyTo($replyTo[$i], $replyToName[$i]); } catch (\Exception $e) { // carry on } } } elseif (isset($replyTo)) { try { $mailer->addReplyTo($replyTo, $replyToName); } catch (\Exception $e) { // carry on } } else { $autoReplyTo = true; } try { $mailer->setSender(array($from, $fromName, $autoReplyTo)); } catch (\Exception $e) { return false; } /** * Set the plain text AltBody, which forces the PHP mailer class to make this * a multipart MIME type, with an alt body for plain text. If we don't do this, * the default behavior is to send it as just text/html, which causes spam filters * to downgrade it. * @@@trob: insert \n before <br to keep newlines(strip_tag may then strip <br> or <br /> etc, decode html */ if ($mode) { $body = str_ireplace(array("<br />","<br>","<br/>"), "\n<br />", $body); $body = html_entity_decode($body); $mailer->AltBody = JMailHelper::cleanText(strip_tags($body)); } foreach ($headers as $headerName => $headerValue) { $mailer->addCustomHeader($headerName, $headerValue); } $config = JComponentHelper::getParams('com_fabrik'); if ($config->get('verify_peer', '1') === '0') { $mailer->SMTPOptions = array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ) ); } try { $ret = $mailer->Send(); } catch (\Exception $e) { return false; } return $ret; } /** * Get a JS go back action e.g 'onclick="history.back()" * * @return string */ public static function goBackAction() { jimport('joomla.environment.browser'); $uri = JUri::getInstance(); $url = filter_var(ArrayHelper::getValue($_SERVER, 'HTTP_REFERER'), FILTER_SANITIZE_URL); if ($uri->getScheme() === 'https') { $goBackAction = 'onclick="parent.location=\'' . $url . '\'"'; } else { $goBackAction = 'onclick="parent.location=\'' . $url . '\'"'; } return $goBackAction; } /** * Attempt to find the active menu item id - Only for front end * * - First checked $listId for menu items * - Then checks if itemId in $input * - Finally checked active menu item * * @param int $listId List id to attempt to get the menu item id for the list. * * @return mixed NULL if nothing found, int if menu item found */ public static function itemId($listId = null) { static $listIds = array(); $app = JFactory::getApplication(); if (!$app->isAdmin()) { // Attempt to get Itemid from possible list menu item. if (!is_null($listId)) { if (!array_key_exists($listId, $listIds)) { $db = JFactory::getDbo(); $myLanguage = JFactory::getLanguage(); $myTag = $myLanguage->getTag(); $qLanguage = !empty($myTag) ? ' AND ' . $db->q($myTag) . ' = ' . $db->qn('m.language') : ''; $query = $db->getQuery(true); $query->select('m.id AS itemId')->from('#__extensions AS e') ->leftJoin('#__menu AS m ON m.component_id = e.extension_id') ->where('e.name = "com_fabrik" and e.type = "component" and m.link LIKE "%listid=' . $listId . '"' . $qLanguage); $db->setQuery($query); if ($itemId = $db->loadResult()) { $listIds[$listId] = $itemId; } else{ $listIds[$listId] = false; } } else{ if ($listIds[$listId] !== false) { return $listIds[$listId]; } } } $itemId = (int) $app->input->getInt('itemId'); if ($itemId !== 0) { return $itemId; } $menus = $app->getMenu(); $menu = $menus->getActive(); if (is_object($menu)) { return $menu->id; } } return null; } /** * Attempt to get a variable first from the menu params (if they exists) if not from request * * @param string $name Param name * @param mixed $val Default * @param bool $mambot If set to true menu params ignored * @param string $priority Defaults that menu priorities override request - set to 'request' * to inverse this priority * @param array $opts Options 'listid' -> if priority = menu then the menu list id must * match this value to use the menu param. * * @return string */ public static function getMenuOrRequestVar($name, $val = '', $mambot = false, $priority = 'menu', $opts = array()) { $app = JFactory::getApplication(); $input = $app->input; if ($priority === 'menu') { $val = $input->get($name, $val, 'string'); if (!$app->isAdmin()) { if (!$mambot) { $menus = $app->getMenu(); $menu = $menus->getActive(); if (is_object($menu)) { $match = true; if (array_key_exists('listid', $opts)) { $menuListId = ArrayHelper::getValue($menu->query, 'listid', ''); $checkListId = ArrayHelper::getValue($opts, 'listid', $menuListId); $match = (int) $menuListId === (int) $checkListId; } else if (array_key_exists('formid', $opts)) { $menuFormId = ArrayHelper::getValue($menu->query, 'formid', ''); $checkFormId = ArrayHelper::getValue($opts, 'formid', $menuFormId); $match = (int) $menuFormId === (int) $checkFormId; } if ($match) { $val = $menu->params->get($name, $val); } } } } } else { if (!$app->isAdmin()) { $menus = $app->getMenu(); $menu = $menus->getActive(); // If there is a menu item available AND the view is not rendered in a content plugin if (is_object($menu) && !$mambot) { $match = true; if (array_key_exists('listid', $opts)) { $menuListId = ArrayHelper::getValue($menu->query, 'listid', ''); $checkListId = ArrayHelper::getValue($opts, 'listid', $menuListId); $match = (int) $menuListId === (int) $checkListId; } else if (array_key_exists('formid', $opts)) { $menuFormId = ArrayHelper::getValue($menu->query, 'formid', ''); $checkFormId = ArrayHelper::getValue($opts, 'formid', $menuFormId); $match = (int) $menuFormId === (int) $checkFormId; } if ($match) { $val = $menu->params->get($name, $val); } } } $val = $input->get($name, $val, 'string'); } return $val; } /** * Access control function for determining if the user can perform * a designated function on a specific row * * @param object $params Item params to test * @param object $row Data * @param string $col Access control setting to compare against * * @return mixed - if ACL setting defined here return bool, otherwise return -1 to continue with default acl * setting */ public static function canUserDo($params, $row, $col) { if (!is_null($row)) { $app = JFactory::getApplication(); $input = $app->input; $user = JFactory::getUser(); $userCol = $params->get($col, ''); if ($userCol != '') { $userCol = StringHelper::safeColNameToArrayKey($userCol); if (!array_key_exists($userCol, $row)) { return false; } else { if (array_key_exists($userCol . '_raw', $row)) { $userCol .= '_raw'; } $myId = $user->get('id'); // -1 for menu items that link to their own records $userColVal = is_array($row) ? $row[$userCol] : $row->$userCol; // User element stores as object if (is_object($userColVal)) { $userColVal = ArrayHelper::fromObject($userColVal); } // Could be coming back from a failed validation in which case val might be an array if (is_array($userColVal)) { $userColVal = array_shift($userColVal); } if (empty($userColVal) && empty($myId)) { return false; } if (intVal($userColVal) === intVal($myId) || $input->get('rowid') == -1) { return true; } } } } return -1; } /** * Can Fabrik render PDF - required the DOMPDF library to be installed in Joomla libraries folder * * @param bool $puke throw an exception if can't * * @throws RuntimeException * * @return bool */ public static function canPdf($puke = true) { $config = \JComponentHelper::getParams('com_fabrik'); if ($config->get('fabrik_pdf_lib', 'dompdf') === 'dompdf') { $file = COM_FABRIK_LIBRARY . '/vendor/dompdf/dompdf/composer.json'; } else { $file = COM_FABRIK_LIBRARY . '/vendor/mpdf/mpdf/composer.json'; } if (!JFile::exists($file)) { if ($puke) { throw new RuntimeException(Text::_('COM_FABRIK_NOTICE_DOMPDF_NOT_FOUND')); } else { return false; } } return true; } /** * Get a cache handler * $$$ hugh - added $listModel arg, needed so we can see if they have set "Disable Caching" on the List * * @param object $listModel List Model * @param string $group group name (will default to package) * @param int $ttl time to live in minutes, defaults to J! config * * @since 3.0.7 * * @return JCache */ public static function getCache($listModel = null, $group = null, $ttl = null) { $config = JFactory::getConfig(); $app = JFactory::getApplication(); $package = isset($group) ? $group : $app->getUserState('com_fabrik.package', 'fabrik'); $time = isset($ttl) ? (int) $ttl : (int) $config->get('cachetime'); $base = JPATH_BASE . '/cache/'; $opts = array('defaultgroup' => 'com_' . $package, 'cachebase' => $base, 'lifetime' => $time, 'language' => 'en-GB', 'storage' => 'file'); $cache = JCache::getInstance('callback', $opts); $doCache = $config->get('caching', 0) > 0 ? true : false; if ($doCache && $listModel !== null) { $doCache = $listModel->getParams()->get('list_disable_caching', '0') == '0'; } $cache->setCaching($doCache); return $cache; } /** * Is caching enabled * * @param object $listModel List Model * @param bool $noGuest disable caching for guests * * @since 3.8 * * @return JCache */ public static function useCache($listModel = null, $noGuest = true, $excludedFormats = null) { $config = JFactory::getConfig(); $app = JFactory::getApplication(); if (!isset($excludedFormats)) { $excludedFormats = array('raw', 'csv', 'pdf', 'json', 'fabrikfeed', 'feed'); } // check global J! system cache setting $doCache = $config->get('caching', 0) > 0 ? true : false; // if enabled, see if any other settingg disables it if ($doCache) { // Check the Fabrik global option $fabrikConfig = JComponentHelper::getParams('com_fabrik'); if ($fabrikConfig->get('disable_caching', '0') === '1') { return false; } // If a list model has been specified, see if caching is disabled for this list if ($listModel !== null) { if ($listModel->getParams()->get('list_disable_caching', '0') === '1') { return false; } } // Check if caching is disabled for guests if ($noGuest) { if (JFactory::getUser()->get('id') === '0') { return false; } } if (in_array($app->input->get('format'), $excludedFormats)) { return false; } } return true; } /** * Get the default values for a given JForm * * @param string $form Form name e.g. list, form etc. * * @since 3.0.7 * * @return array key field name, value default value */ public static function formDefaults($form) { JForm::addFormPath(JPATH_COMPONENT . '/models/forms'); JForm::addFieldPath(JPATH_COMPONENT . '/models/fields'); $form = JForm::getInstance('com_fabrik.' . $form, $form, array('control' => '', 'load_data' => true)); $fs = $form->getFieldset(); $json = array('params' => array()); foreach ($fs as $name => $field) { if (substr($name, 0, 7) === 'params_') { $name = str_replace('params_', '', $name); $json['params'][$name] = $field->value; } else { $json[$name] = $field->value; } } return $json; } /** * Are we in J3 or using a bootstrap tmpl * * @since 3.1 * * @return bool */ public static function j3() { $app = JFactory::getApplication(); $version = new JVersion; // Only use template test for testing in 2.5 with my temp J bootstrap template. $tpl = $app->getTemplate(); return ($tpl === 'bootstrap' || $tpl === 'fabrik4' || $version->RELEASE > 2.5); } /** * Are we in a form process task * * @since 3.2 * * @return bool */ public static function inFormProcess() { $app = JFactory::getApplication(); return $app->input->get('task') == 'form.process' || ($app->isAdmin() && $app->input->get('task') == 'process'); } /** * Remove messages from JApplicationCMS * * @param CMSApplication $app Application to kill messages from * @param string $type Message type e.g. 'warning', 'error' * * @return array Remaining messages. */ public static function killMessage(CMSApplication $app, $type) { $appReflection = new \ReflectionClass(get_class($app)); $_messageQueue = $appReflection->getProperty('_messageQueue'); $_messageQueue->setAccessible(true); $messages = $_messageQueue->getValue($app); foreach ($messages as $key => $message) { if ($message['type'] == $type) { unset($messages[$key]); } } $_messageQueue->setValue($app, $messages); return $messages; } /** * Loose casing to boolean * * @param mixed $var Var to test * @param boolean $default if neither a truish or falsy match are found * * @return bool - Set to false if false is found. */ public static function toBoolean($var, $default) { if ($var === 'false' || $var === 0 || $var === false) { return false; } if ($var === 'true' || $var === 1 || $var === true) { return true; } return $default; } /** * Get a getID3 instance - check if library installed, if not, toss an exception * * @return object|bool - getid3 object or false if lib not installed */ public static function getID3Instance() { $getID3 = false; if (JFile::exists(COM_FABRIK_LIBRARY . '/libs/getid3/getid3/getid3.php')) { ini_set('display_errors', true); require_once COM_FABRIK_LIBRARY . '/libs/getid3/getid3/getid3.php'; require_once COM_FABRIK_LIBRARY . '/libs/getid3/getid3/getid3.lib.php'; \getid3_lib::IncludeDependency(COM_FABRIK_LIBRARY . '/libs/getid3/getid3/extension.cache.mysqli.php', __FILE__, true); $config = JFactory::getConfig(); $host = $config->get('host'); $database = $config->get('db'); $username = $config->get('user'); $password = $config->get('password'); $getID3 = new \getID3_cached_mysqli($host, $database, $username, $password); } return $getID3; } public static function getMemoryLimit($symbolic = false) { $memory = trim(ini_get('memory_limit')); $memory = trim($memory); if ($symbolic) { return $memory; } $last = strtolower($memory[strlen($memory)-1]); $val = substr($memory, 0, -1); switch($last) { case 'g': $val *= 1024; case 'm': $val *= 1024; case 'k': $val *= 1024; } return $val; } } Helpers/Sms.php 0000604 00000003570 15172625755 0007441 0 ustar 00 <?php /** * Send sms's * * @package Joomla * @subpackage Fabrik.helpers * @copyright Copyright (C) 2005-2016 Media A-Team, Inc. - All rights reserved. * @license GNU/GPL http://www.gnu.org/copyleft/gpl.html */ namespace Fabrik\Helpers; // No direct access defined('_JEXEC') or die('Restricted access'); use \JFactory; use \RuntimeException; /** * Send sms's * * @package Joomla * @subpackage Fabrik.helpers * @since 3.0 */ class Sms { /** * Send sms * * @param string $method post/get * @param string $url url to request * @param string $vars querystring vars to post * @param string $auth auth * @param string $callback method * * @return mixed data or curl error */ public static function doRequest($method, $url, $vars, $auth = '', $callback = false) { $app = JFactory::getApplication(); if (!function_exists('curl_init')) { throw new RuntimeException(Text::_('COM_FABRIK_ERR_CURL_NOT_INSTALLED')); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_USERAGENT, $app->input->server->getString('HTTP_USER_AGENT')); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt'); if ($method == 'POST') { curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $vars); } if (!empty($auth)) { curl_setopt($ch, CURLOPT_USERPWD, $auth); } $data = curl_exec($ch); curl_close($ch); if ($data) { if ($callback) { return call_user_func($callback, $data); } else { return $data; } } else { return curl_error($ch); } } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings