Spade

Mini Shell

Directory:~$ /proc/self/root/home/lmsyaran/public_html/css/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ //proc/self/root/home/lmsyaran/public_html/css/fabrik.tar

fabrik/fabrik/Controllers/Controller.php000064400000002746151165341560014422
0ustar00<?php
/**
 * Created by PhpStorm.
 * User: rob
 * Date: 24/05/2016
 * Time: 09:56
 */

namespace Fabrik\Controllers;

defined('_JEXEC') or die;

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Document\Document;
use Joomla\CMS\Session\Session;
use Joomla\CMS\User\User;
use Joomla\CMS\Factory;
use Joomla\Utilities\ArrayHelper;

class Controller extends BaseController
{
	/**
	 * @var JApplicationCMS
	 */
	protected $app;

	/**
	 * @var User
	 */
	protected $user;

	/**
	 * @var string
	 */
	protected $package;

	/**
	 * @var Session
	 */
	protected $session;

	/**
	 * @var Document
	 */
	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',
Factory::getApplication());
		$this->user    = ArrayHelper::getValue($config, 'user',
Factory::getUser());
		$this->package =
$this->app->getUserState('com_fabrik.package',
'fabrik');
		$this->session = ArrayHelper::getValue($config, 'session',
Factory::getSession());
		$this->doc     = ArrayHelper::getValue($config, 'doc',
Factory::getDocument());
		$this->db      = ArrayHelper::getValue($config, 'db',
Factory::getDbo());
		$this->config  = ArrayHelper::getValue($config, 'config',
Factory::getApplication()->getConfig());
		parent::__construct($config);
	}
}fabrik/fabrik/Document/PartialDocument.php000064400000055721151165341570014644
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Language\Text;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Utility\Utility;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Uri\Uri;
use Joomla\Registry\Registry;
use Joomla\CMS\Factory;
use Joomla\CMS\Document\Document;
use Fabrik\Helpers\Php;

jimport('joomla.utilities.utility');

/**
 * HtmlDocument class, provides an easy interface to parse and display a
HTML document
 *
 * @since  11.1
 */
class PartialDocument extends Document
{
    /**
     * Array of Header `<link>` tags
     *
     * @var    array
     * @since  11.1
     */
    public $_links = array();

    /**
     * Array of custom tags
     *
     * @var    array
     * @since  11.1
     */
    public $_custom = array();

    /**
     * Name of the template
     *
     * @var    string
     * @since  11.1
     */
    public $template = null;

    /**
     * Base url
     *
     * @var    string
     * @since  11.1
     */
    public $baseurl = null;

    /**
     * Array of template parameters
     *
     * @var    array
     * @since  11.1
     */
    public $params = null;

    /**
     * File name
     *
     * @var    array
     * @since  11.1
     */
    public $_file = null;

    /**
     * String holding parsed template
     *
     * @var    string
     * @since  11.1
     */
    protected $_template = '';

    /**
     * Array of parsed template JDoc tags
     *
     * @var    array
     * @since  11.1
     */
    protected $_template_tags = array();

    /**
     * Integer with caching setting
     *
     * @var    integer
     * @since  11.1
     */
    protected $_caching = null;

    /**
     * Set to true when the document should be output as HTML5
     *
     * @var    boolean
     * @since  12.1
     *
     * @note  4.0  Will be replaced by $html5 and the default value will be
true.
     */
    private $_html5 = null;

    /**
     * Class constructor
     *
     * @param   array  $options  Associative array of options
     *
     * @since   11.1
     */
    public function __construct($options = array())
    {
        parent::__construct($options);

        // Set document type
        $this->_type = 'partial';

        // Set default mime type and document metadata (meta data syncs
with mime type by default)
        $this->setMimeEncoding('text/html');
    }

    /**
     * Get the HTML document head data
     *
     * @return  array  The document head data in array form
     *
     * @since   11.1
     */
    public function getHeadData()
    {
        $data = array();
        $data['title']       = $this->title;
        $data['description'] = $this->description;
        $data['link']        = $this->link;
        $data['metaTags']    = $this->_metaTags;
        $data['links']       = $this->_links;
        $data['styleSheets'] = $this->_styleSheets;
        $data['style']       = $this->_style;
        $data['scripts']     = $this->_scripts;
        $data['script']      = $this->_script;
        $data['custom']      = $this->_custom;
        $data['scriptText']  = Text::script();

        return $data;
    }

    /**
     * Reset the HTML document head data
     *
     * @param   mixed  $types  type or types of the heads elements to reset
     *
     * @return  PartialDocument  instance of $this to allow chaining
     *
     * @since   3.7.0
     */
    public function resetHeadData($types = null)
    {
        if (is_null($types))
        {
            $this->title        = '';
            $this->description  = '';
            $this->link         = '';
            $this->_metaTags    = array();
            $this->_links       = array();
            $this->_styleSheets = array();
            $this->_style       = array();
            $this->_scripts     = array();
            $this->_script      = array();
            $this->_custom      = array();
        }

        if (is_array($types))
        {
            foreach ($types as $type)
            {
                $this->resetHeadDatum($type);
            }
        }

        if (is_string($types))
        {
            $this->resetHeadDatum($types);
        }

        return $this;
    }

    /**
     * Reset a part the HTML document head data
     *
     * @param   string  $type  type of the heads elements to reset
     *
     * @return  void
     *
     * @since   3.7.0
     */
    private function resetHeadDatum($type)
    {
        switch ($type)
        {
            case 'title':
            case 'description':
            case 'link':
                $this->{$type} = '';
                break;

            case 'metaTags':
            case 'links':
            case 'styleSheets':
            case 'style':
            case 'scripts':
            case 'script':
            case 'custom':
                $realType = '_' . $type;
                $this->{$realType} = array();
                break;
        }
    }

    /**
     * Set the HTML document head data
     *
     * @param   array  $data  The document head data in array form
     *
     * @return  HtmlDocument|null instance of $this to allow chaining or
null for empty input data
     *
     * @since   11.1
     */
    public function setHeadData($data)
    {
        if (empty($data) || !is_array($data))
        {
            return;
        }

        $this->title        = (isset($data['title'])
&& !empty($data['title'])) ? $data['title'] :
$this->title;
        $this->description  = (isset($data['description'])
&& !empty($data['description'])) ?
$data['description'] : $this->description;
        $this->link         = (isset($data['link']) &&
!empty($data['link'])) ? $data['link'] :
$this->link;
        $this->_metaTags    = (isset($data['metaTags'])
&& !empty($data['metaTags'])) ?
$data['metaTags'] : $this->_metaTags;
        $this->_links       = (isset($data['links'])
&& !empty($data['links'])) ? $data['links'] :
$this->_links;
        $this->_styleSheets = (isset($data['styleSheets'])
&& !empty($data['styleSheets'])) ?
$data['styleSheets'] : $this->_styleSheets;
        $this->_style       = (isset($data['style'])
&& !empty($data['style'])) ? $data['style'] :
$this->_style;
        $this->_scripts     = (isset($data['scripts'])
&& !empty($data['scripts'])) ? $data['scripts']
: $this->_scripts;
        $this->_script      = (isset($data['script'])
&& !empty($data['script'])) ? $data['script'] :
$this->_script;
        $this->_custom      = (isset($data['custom'])
&& !empty($data['custom'])) ? $data['custom'] :
$this->_custom;

        if (isset($data['scriptText']) &&
!empty($data['scriptText']))
        {
            foreach ($data['scriptText'] as $key => $string)
            {
                Text::script($key, $string);
            }
        }

        return $this;
    }

    /**
     * Merge the HTML document head data
     *
     * @param   array  $data  The document head data in array form
     *
     * @return  PartialDocument|null instance of $this to allow chaining or
null for empty input data
     *
     * @since   11.1
     */
    public function mergeHeadData($data)
    {
        if (empty($data) || !is_array($data))
        {
            return;
        }

        $this->title = (isset($data['title']) &&
!empty($data['title']) && !stristr($this->title,
$data['title']))
            ? $this->title . $data['title']
            : $this->title;
        $this->description = (isset($data['description'])
&& !empty($data['description']) &&
!stristr($this->description, $data['description']))
            ? $this->description . $data['description']
            : $this->description;
        $this->link = (isset($data['link'])) ?
$data['link'] : $this->link;

        if (isset($data['metaTags']))
        {
            foreach ($data['metaTags'] as $type1 => $data1)
            {
                $booldog = $type1 == 'http-equiv' ? true : false;

                foreach ($data1 as $name2 => $data2)
                {
                    $this->setMetaData($name2, $data2, $booldog);
                }
            }
        }

        $this->_links = (isset($data['links']) &&
!empty($data['links']) &&
is_array($data['links']))
            ? array_unique(array_merge($this->_links,
$data['links']), SORT_REGULAR)
            : $this->_links;
        $this->_styleSheets = (isset($data['styleSheets'])
&& !empty($data['styleSheets']) &&
is_array($data['styleSheets']))
            ? array_merge($this->_styleSheets,
$data['styleSheets'])
            : $this->_styleSheets;

        if (isset($data['style']))
        {
            foreach ($data['style'] as $type => $stdata)
            {
                if (!isset($this->_style[strtolower($type)]) ||
!stristr($this->_style[strtolower($type)], $stdata))
                {
                    $this->addStyleDeclaration($stdata, $type);
                }
            }
        }

        $this->_scripts = (isset($data['scripts']) &&
!empty($data['scripts']) &&
is_array($data['scripts']))
            ? array_merge($this->_scripts, $data['scripts'])
            : $this->_scripts;

        if (isset($data['script']))
        {
            foreach ($data['script'] as $type => $sdata)
            {
                if (!isset($this->_script[strtolower($type)]) ||
!stristr($this->_script[strtolower($type)], $sdata))
                {
                    $this->addScriptDeclaration($sdata, $type);
                }
            }
        }

        $this->_custom = (isset($data['custom']) &&
!empty($data['custom']) &&
is_array($data['custom']))
            ? array_unique(array_merge($this->_custom,
$data['custom']))
            : $this->_custom;

        return $this;
    }

    /**
     * Adds `<link>` tags to the head of the document
     *
     * $relType defaults to 'rel' as it is the most common
relation type used.
     * ('rev' refers to reverse relation, 'rel'
indicates normal, forward relation.)
     * Typical tag: `<link href="index.php"
rel="Start">`
     *
     * @param   string  $href      The link that is being related.
     * @param   string  $relation  Relation of link.
     * @param   string  $relType   Relation type attribute.  Either rel or
rev (default: 'rel').
     * @param   array   $attribs   Associative array of remaining
attributes.
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    public function addHeadLink($href, $relation, $relType =
'rel', $attribs = array())
    {
        $this->_links[$href]['relation'] = $relation;
        $this->_links[$href]['relType'] = $relType;
        $this->_links[$href]['attribs'] = $attribs;

        return $this;
    }

    /**
     * Adds a shortcut icon (favicon)
     *
     * This adds a link to the icon shown in the favorites list or on
     * the left of the url in the address bar. Some browsers display
     * it on the tab, as well.
     *
     * @param   string  $href      The link that is being related.
     * @param   string  $type      File type
     * @param   string  $relation  Relation of link
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    public function addFavicon($href, $type =
'image/vnd.microsoft.icon', $relation = 'shortcut
icon')
    {
        $href = str_replace('\\', '/', $href);
        $this->addHeadLink($href, $relation, 'rel',
array('type' => $type));

        return $this;
    }

    /**
     * Adds a custom HTML string to the head block
     *
     * @param   string  $html  The HTML to add to the head
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    public function addCustomTag($html)
    {
        $this->_custom[] = trim($html);

        return $this;
    }

    /**
     * Returns whether the document is set up to be output as HTML5
     *
     * @return  boolean true when HTML5 is used
     *
     * @since   12.1
     */
    public function isHtml5()
    {
        return $this->_html5;
    }

    /**
     * Sets whether the document should be output as HTML5
     *
     * @param   bool  $state  True when HTML5 should be output
     *
     * @return  void
     *
     * @since   12.1
     */
    public function setHtml5($state)
    {
        if (is_bool($state))
        {
            $this->_html5 = $state;
        }
    }

    /**
     * Get the contents of a document include
     *
     * @param   string  $type     The type of renderer
     * @param   string  $name     The name of the element to render
     * @param   array   $attribs  Associative array of remaining
attributes.
     *
     * @return  mixed|string The output of the renderer
     *
     * @since   11.1
     */
    public function getBuffer($type = null, $name = null, $attribs =
array())
    {
        // If no type is specified, return the whole buffer
        if ($type === null)
        {
            return parent::$_buffer;
        }

        $title = (isset($attribs['title'])) ?
$attribs['title'] : null;

        if (isset(parent::$_buffer[$type][$name][$title]))
        {
            return parent::$_buffer[$type][$name][$title];
        }

        $renderer = $this->loadRenderer($type);

        if ($this->_caching == true && $type ==
'modules')
        {
            $cache = Factory::getCache('com_modules',
'');
            $hash = md5(serialize(array($name, $attribs, null,
$renderer)));
            $cbuffer = $cache->get('cbuffer_' . $type);

            if (isset($cbuffer[$hash]))
            {
                return Cache::getWorkarounds($cbuffer[$hash],
array('mergehead' => 1));
            }
            else
            {
                $options = array();
                $options['nopathway'] = 1;
                $options['nomodules'] = 1;
                $options['modulemode'] = 1;

                $this->setBuffer($renderer->render($name, $attribs,
null), $type, $name);
                $data = parent::$_buffer[$type][$name][$title];

                $tmpdata = Cache::setWorkarounds($data, $options);

                $cbuffer[$hash] = $tmpdata;

                $cache->store($cbuffer, 'cbuffer_' . $type);
            }
        }
        else
        {
            $this->setBuffer($renderer->render($name, $attribs,
null), $type, $name, $title);
        }

        return parent::$_buffer[$type][$name][$title];
    }

    /**
     * Set the contents a document includes
     *
     * @param   string  $content  The content to be set in the buffer.
     * @param   array   $options  Array of optional elements.
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    public function setBuffer($content, $options = array())
    {
        // The following code is just for backward compatibility.
        if (\func_num_args() > 1 && !\is_array($options)) {
            $args             = \func_get_args();
            $options          = [];
            $options['type']  = $args[1];
            $options['name']  = $args[2] ?? null;
            $options['title'] = $args[3] ?? null;
        }

        $type  = $options['type'] ?? '';
        $name  = $options['name'] ?? '';
        $title = $options['title'] ?? '';

        parent::$_buffer[$type][$name][$title] = $content;

        return $this;
    }

    /**
     * Parses the template and populates the buffer
     *
     * @param   array  $params  Parameters for fetching the template
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    public function parse($params = array())
    {
        return $this->_fetchTemplate($params)->_parseTemplate();
    }

    /**
     * Outputs the template to the browser.
     *
     * @param   boolean  $caching  If true, cache the output
     * @param   array    $params   Associative array of attributes
     *
     * @return  string The rendered data
     *
     * @since   11.1
     */
    public function render($caching = false, $params = array())
    {
        $this->_caching = $caching;

        if (empty($this->_template))
        {
            $this->parse($params);
        }

        $data = $this->_renderTemplate();
        parent::render();

        return $data;
    }

    /**
     * Count the modules based on the given condition
     *
     * @param   string  $condition  The condition to use
     *
     * @return  integer  Number of modules found
     *
     * @since   11.1
     */
    public function countModules($condition)
    {
        $operators =
'(\+|\-|\*|\/|==|\!=|\<\>|\<|\>|\<=|\>=|and|or|xor)';
        $words = preg_split('# ' . $operators . ' #',
$condition, null, PREG_SPLIT_DELIM_CAPTURE);

        if (count($words) === 1)
        {
            $name = strtolower($words[0]);
            $result =
((isset(parent::$_buffer['modules'][$name])) &&
(parent::$_buffer['modules'][$name] === false))
                ? 0 : count(ModuleHelper::getModules($name));

            return $result;
        }

        Log::add('Using an expression in
PartialDocument::countModules() is deprecated.', Log::WARNING,
'deprecated');

        for ($i = 0, $n = count($words); $i < $n; $i += 2)
        {
            // Odd parts (modules)
            $name = strtolower($words[$i]);
            $words[$i] =
((isset(parent::$_buffer['modules'][$name])) &&
(parent::$_buffer['modules'][$name] === false))
                ? 0
                : count(ModuleHelper::getModules($name));
        }

        $str = 'return ' . implode(' ', $words) .
';';

        return Php::Eval(['code' => $str]);
    }

    /**
     * Count the number of child menu items of the current active menu item
     *
     * @return  integer  Number of child menu items
     *
     * @since   11.1
     */
    public function countMenuChildren()
    {
        static $children;

        if (!isset($children))
        {
            $db = Factory::getDbo();
            $app = Factory::getApplication();
            $menu = $app->getMenu();
            $active = $menu->getActive();
            $children = 0;

            if ($active)
            {
                $query = $db->getQuery(true)
                    ->select('COUNT(*)')
                    ->from('#__menu')
                    ->where('parent_id = ' . $active->id)
                    ->where('published = 1');
                $db->setQuery($query);
                $children = $db->loadResult();
            }
        }

        return $children;
    }

    /**
     * Load a template file
     *
     * @param   string  $directory  The name of the template
     * @param   string  $filename   The actual filename
     *
     * @return  string  The contents of the template
     *
     * @since   11.1
     */
    protected function _loadTemplate($directory, $filename)
    {
        $contents = '';

        // Check to see if we have a valid template file
        if (file_exists($directory . '/' . $filename))
        {
            // Store the file path
            $this->_file = $directory . '/' . $filename;

            // Get the file content
            ob_start();
            require $directory . '/' . $filename;
            $contents = ob_get_contents();
            ob_end_clean();
        }

        // Try to find a favicon by checking the template and root folder
        $icon = '/favicon.ico';

        foreach (array($directory, JPATH_BASE) as $dir)
        {
            if (file_exists($dir . $icon))
            {
                $path = str_replace(JPATH_BASE, '', $dir);
                $path = str_replace('\\', '/', $path);
                $this->addFavicon(Uri::base(true) . $path . $icon);
                break;
            }
        }

        return $contents;
    }

    /**
     * Fetch the template, and initialise the params
     *
     * @param   array  $params  Parameters to determine the template
     *
     * @return  PartialDocument instance of $this to allow chaining
     *
     * @since   11.1
     */
    protected function _fetchTemplate($params = array())
    {
        // Check
        $directory = isset($params['directory']) ?
$params['directory'] : 'templates';
        $filter = InputFilter::getInstance();
        $template = $filter->clean($params['template'],
'cmd');
        $file = $filter->clean($params['file'],
'cmd');

        if (!file_exists($directory . '/' . $template .
'/' . $file))
        {
            $template = 'system';
        }

        if (!file_exists($directory . '/' . $template .
'/' . $file))
        {
            $file = 'index.php';
        }

        // Load the language file for the template
        $lang = Factory::getApplication()->getLanguage();

        // 1.5 or core then 1.6
        $lang->load('tpl_' . $template, JPATH_BASE, null,
false, true)
        || $lang->load('tpl_' . $template, $directory .
'/' . $template, null, false, true);

        // Assign the variables
        $this->template = $template;
        $this->baseurl = Uri::base(true);
        $this->params = isset($params['params']) ?
$params['params'] : new Registry;

        // Load
        $this->_template = $this->_loadTemplate($directory .
'/' . $template, $file);

        return $this;
    }

    /**
     * Parse a document template
     *
     * @return  PartialDocument  instance of $this to allow chaining
     *
     * @since   11.1
     */
    protected function _parseTemplate()
    {
        $matches = array();

        if (preg_match_all('#<jdoc:include\
type="([^"]+)"(.*)\/>#iU', $this->_template,
$matches))
        {
            $template_tags_first = array();
            $template_tags_last = array();

            // Step through the jdocs in reverse order.
            for ($i = count($matches[0]) - 1; $i >= 0; $i--)
            {
                $type = $matches[1][$i];
                $attribs = empty($matches[2][$i]) ? array() :
Utility::parseAttributes($matches[2][$i]);
                $name = isset($attribs['name']) ?
$attribs['name'] : null;

                // Separate buffers to be executed first and last
                if ($type == 'module' || $type ==
'modules')
                {
                    $template_tags_first[$matches[0][$i]] =
array('type' => $type, 'name' => $name,
'attribs' => $attribs);
                }
                else
                {
                    $template_tags_last[$matches[0][$i]] =
array('type' => $type, 'name' => $name,
'attribs' => $attribs);
                }
            }
            // Reverse the last array so the jdocs are in forward order.
            $template_tags_last = array_reverse($template_tags_last);

            $this->_template_tags = $template_tags_first +
$template_tags_last;
        }

        return $this;
    }

    /**
     * Render pre-parsed template
     *
     * @return string rendered template
     *
     * @since   11.1
     */
    protected function _renderTemplate()
    {
        $replace = array();
        $with = array();

        foreach ($this->_template_tags as $jdoc => $args)
        {
            $replace[] = $jdoc;
            $with[] = $this->getBuffer($args['type'],
$args['name'], $args['attribs']);
        }

        return str_replace($replace, $with, $this->_template);
    }
}

fabrik/fabrik/Document/PdfDocument.php000064400000015034151165341570013752
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document;

defined('JPATH_PLATFORM') or die;

//require_once JPATH_SITE .
'/components/com_fabrik/helpers/pdf.php';

use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Fabrik\Helpers\Pdf;
use Joomla\CMS\Document\HtmlDocument;

jimport('joomla.utilities.utility');

/**
 * PdfDocument class, provides an easy interface to parse and display a PDF
document
 *
 * @since  11.1
 */
class PdfDocument extends HtmlDocument
{
	/**
	 * Array of Header `<link>` tags
	 *
	 * @var    array
	 * @since  11.1
	 */
	public $_links = array();

	/**
	 * Array of custom tags
	 *
	 * @var    array
	 * @since  11.1
	 */
	public $_custom = array();

	/**
	 * Name of the template
	 *
	 * @var    string
	 * @since  11.1
	 */
	public $template = null;

	/**
	 * Base url
	 *
	 * @var    string
	 * @since  11.1
	 */
	public $baseurl = null;

	/**
	 * Array of template parameters
	 *
	 * @var    array
	 * @since  11.1
	 */
	public $params = null;

	/**
	 * File name
	 *
	 * @var    array
	 * @since  11.1
	 */
	public $_file = null;

	/**
	 * String holding parsed template
	 *
	 * @var    string
	 * @since  11.1
	 */
	protected $_template = '';

	/**
	 * Array of parsed template JDoc tags
	 *
	 * @var    array
	 * @since  11.1
	 */
	protected $_template_tags = array();

	/**
	 * Integer with caching setting
	 *
	 * @var    integer
	 * @since  11.1
	 */
	protected $_caching = null;

	/**
	 * Set to true when the document should be output as HTML5
	 *
	 * @var    boolean
	 * @since  12.1
	 *
	 * @note  4.0  Will be replaced by $html5 and the default value will be
true.
	 */
	private $_html5 = null;

	/**
	 * Fabrik config
	 *
	 * @var null
	 */
	protected $config = null;

	/**
	 * Orientation
	 *
	 * @var  string
	 */
	private $orientation = 'P';

	/**
	 * Paper size
	 *
	 * @var  string
	 */
	private $size = 'A4';

	/**
	 * Class constructor
	 *
	 * @param   array  $options  Associative array of options
	 *
	 * @since   11.1
	 */
	public function __construct($options = array())
	{
		parent::__construct($options);

		$this->config = ComponentHelper::getParams('com_fabrik');
		if ($this->config->get('pdf_debug', false))
		{
			$this->setMimeEncoding('text/html');
			$this->_type = 'pdf';
		}
		else
		{
			// Set mime type
			$this->_mime = 'application/pdf';

			// Set document type
			$this->_type = 'pdf';
		}

		$this->iniPdf();
	}

	/**
	 * Init selected PDF
	 */
	protected function iniPdf()
	{
		if ($this->config->get('fabrik_pdf_lib',
'dompdf') === 'dompdf')
		{
			if (!$this->iniDomPdf())
			{
				throw new
RuntimeException(Text::_('COM_FABRIK_NOTICE_DOMPDF_NOT_FOUND'));
			}
		}
	}

	/**
	 * Set up DomPDF engine
	 *
	 * @return  bool
	 */
	protected function iniDomPdf()
	{
		$this->engine = Pdf::iniDomPdf(true);

		return $this->engine;
	}

	/**
	 * Set the paper size and orientation
	 * Note if too small for content then the pdf renderer will bomb out in an
infinite loop
	 * Legal seems to be more lenient than a4 for example
	 * If doing landscape set large paper size
	 *
	 * @param   string  $size         Paper size E.g A4,legal
	 * @param   string  $orientation  Paper orientation landscape|portrait
	 *
	 * @since 3.0.7
	 *
	 * @return  void
	 */
	public function setPaper($size = 'A4', $orientation =
'landscape')
	{
		if ($this->config->get('fabrik_pdf_lib',
'dompdf') === 'dompdf')
		{
			$size = strtoupper($size);
			$this->engine->set_paper($size, $orientation);
		}
		else
		{
			$this->size = ucfirst($size);

			switch ($orientation)
			{
				case 'landscape':
					$this->orientation = 'L';
					$this->size .= '-' . $this->orientation;
					break;
				case 'portrait':
				default:
					$this->orientation = 'P';
					break;
			}
		}
	}

	/**
	 * Sets the document name
	 *
	 * @param   string  $name  Document name
	 *
	 * @return  void
	 */
	public function setName($name = 'joomla')
	{
		$this->name = $name;
	}

	/**
	 * Returns the document name
	 *
	 * @return	string
	 */
	public function getName()
	{
		return $this->name;
	}

    /**
     * Render the document.
     *
     * @param   boolean  $cache   If true, cache the output
     * @param   array    $params  Associative array of attributes
     *
     * @return	string
     */
	public function render($cache = false, $params = array())
	{
		// mb_encoding foo when content-type had been set to text/html; uft-8;
		$this->_metaTags['http-equiv'] = array();
		$this->_metaTags['http-equiv']['content-type'] =
'text/html';

		// Testing using futural font.
		// $this->addStyleDeclaration('body: { font-family: futural
!important; }');

		$data = parent::render();

		Pdf::fullPaths($data);

		/**
		 * I think we need this to handle some HTML entities when rendering
otherlanguages (like Polish),
		 * but haven't tested it much
		 */
		$data =
mb_convert_encoding($data,'HTML-ENTITIES','UTF-8');
		$config = ComponentHelper::getParams('com_fabrik');

		if ($this->config->get('fabrik_pdf_lib',
'dompdf') === 'dompdf')
		{
			$this->engine->load_html($data);

			if ($config->get('pdf_debug', false))
			{
				return $this->engine->output_html();
			}
			else
			{
				$this->engine->render();
				$this->engine->stream($this->getName() . '.pdf');
			}
		}
		else
		{
			if ($config->get('pdf_debug', false))
			{
				return $data;
			}
			else
			{
				try
				{
					$mpdf = new \Mpdf\Mpdf(
						[
							'tempDir'     =>
Factory::getApplication()->getConfig()->get('tmp_path',
JPATH_ROOT . '/tmp'),
							'mode'        => 'utf-8',
							'format'      => $this->size,
							'orientation' => $this->orientation
						]
					);
					//$mpdf->shrink_tables_to_fit = 1;
					$mpdf->use_kwt = true;
					$mpdf->WriteHTML($data);
					$mpdf->Output($this->getName() . '.pdf',
\Mpdf\Output\Destination::INLINE);
				}
				catch (\Mpdf\MpdfException $e)
				{
					// mmmphh
					echo 'Error creating PDF: ' . ($e->getMessage());
				}
			}
		}

		return '';
	}

	/**
	 * Get the contents of a document include
	 *
	 * @param   string  $type     The type of renderer
	 * @param   string  $name     The name of the element to render
	 * @param   array   $attribs  Associative array of remaining attributes.
	 *
	 * @return  The output of the renderer
	 */

	public function getBuffer($type = null, $name = null, $attribs = array())
	{
		if ($type == 'head' || $type == 'component')
		{
			return parent::getBuffer($type, $name, $attribs);
		}
		else
		{
			return '';
		}
	}
}
fabrik/fabrik/Document/Renderer/Partial/ComponentRenderer.php000064400000001643151165341570020336
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Partial;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Document\DocumentRenderer;

/**
 * HTML document renderer for the component output
 *
 * @since  3.5
 */
class ComponentRenderer extends DocumentRenderer
{
	/**
	 * Renders a component script and returns the results as a string
	 *
	 * @param   string  $component  The name of the component to render
	 * @param   array   $params     Associative array of values
	 * @param   string  $content    Content script
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($component = null, $params = array(), $content =
null)
	{
		return $content;
	}
}
fabrik/fabrik/Document/Renderer/Partial/HeadRenderer.php000064400000030523151165341570017234
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Partial;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Document\HtmlDocument;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Factory;

/**
 * HTML document renderer for the document `<head>` element
 *
 * @since  3.5
 */
class HeadRenderer extends DocumentRenderer
{
    public $excludeJsFiles = array(
        '/jquery.js',
        '/bootstrap.js'
    );

    public $keeperJsFiles = array(
        '/components/com_fabrik/js/'
    );

	/**
	 * Renders the document head and returns the results as a string
	 *
	 * @param   string  $head     (unused)
	 * @param   array   $params   Associative array of values
	 * @param   string  $content  The script
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($head, $params = array(), $content = null)
	{
		return $this->fetchHead($this->_doc);
	}

	/**
	 * Generates the head HTML and return the results as a string
	 *
	 * @param   HtmlDocument  $document  The document for which the head will
be created
	 *
	 * @return  string  The head hTML
	 *
	 * @since   3.5
	 * @deprecated  4.0  Method code will be moved into the render method
	 */
	public function fetchHead($document)
	{
		// Convert the tagids to titles
		if (isset($document->_metaTags['name']['tags']))
		{
			$tagsHelper = new TagsHelper;
			$document->_metaTags['name']['tags'] =
implode(', ',
$tagsHelper->getTagNames($document->_metaTags['name']['tags']));
		}

		if ($document->getScriptOptions())
		{
			HTMLHelper::_('behavior.core');
		}

		// Trigger the onBeforeCompileHead event
		$app = Factory::getApplication();
		$app->getDispatcher()->dispatch('onBeforeCompileHead');

		// Get line endings
		$lnEnd        = $document->_getLineEnd();
		$tab          = $document->_getTab();
		$tagEnd       = ' />';
		$buffer       = '';
		$mediaVersion = $document->getMediaVersion();

		// Generate charset when using HTML5 (should happen first)
		if ($document->isHtml5())
		{
			$buffer .= $tab . '<meta charset="' .
$document->getCharset() . '" />' . $lnEnd;
		}

		// Generate base tag (need to happen early)
		$base = $document->getBase();

		if (!empty($base))
		{
			$buffer .= $tab . '<base href="' . $base .
'" />' . $lnEnd;
		}

		// Generate META tags (needs to happen as early as possible in the head)
		foreach ($document->_metaTags as $type => $tag)
		{
			foreach ($tag as $name => $content)
			{
				if ($type == 'http-equiv' &&
!($document->isHtml5() && $name == 'content-type'))
				{
					$buffer .= $tab . '<meta http-equiv="' . $name .
'" content="' . htmlspecialchars($content, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
				}
				elseif ($type != 'http-equiv' && !empty($content))
				{
					if (is_array($content))
					{
						foreach ($content as $value)
						{
							$buffer .= $tab . '<meta ' . $type .
'="' . $name . '" content="' .
htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"
/>' . $lnEnd;
						}
					}
					else
					{
						$buffer .= $tab . '<meta ' . $type . '="'
. $name . '" content="' . htmlspecialchars($content,
ENT_COMPAT, 'UTF-8') . '" />' . $lnEnd;
					}
				}
			}
		}

		// Don't add empty descriptions
		$documentDescription = $document->getDescription();

		if ($documentDescription)
		{
			$buffer .= $tab . '<meta name="description"
content="' . htmlspecialchars($documentDescription, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
		}

		// Don't add empty generators
		$generator = $document->getGenerator();

		if ($generator)
		{
			$buffer .= $tab . '<meta name="generator"
content="' . htmlspecialchars($generator, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
		}

		$buffer .= $tab . '<title>' .
htmlspecialchars($document->getTitle(), ENT_COMPAT, 'UTF-8') .
'</title>' . $lnEnd;

		// Generate link declarations
		foreach ($document->_links as $link => $linkAtrr)
		{
			$buffer .= $tab . '<link href="' . $link .
'" ' . $linkAtrr['relType'] . '="'
. $linkAtrr['relation'] . '"';

			if (is_array($linkAtrr['attribs']))
			{
				if ($temp = ArrayHelper::toString($linkAtrr['attribs']))
				{
					$buffer .= ' ' . $temp;
				}
			}

			$buffer .= ' />' . $lnEnd;
		}

		$defaultCssMimes = array('text/css');

		// Generate stylesheet links
		foreach ($document->_styleSheets as $src => $attribs)
		{
			// Check if stylesheet uses IE conditional statements.
			$conditional = isset($attribs['options']) &&
isset($attribs['options']['conditional']) ?
$attribs['options']['conditional'] : null;

			// Check if script uses media version.
			if (isset($attribs['options']['version']) &&
$attribs['options']['version'] && strpos($src,
'?') === false
				&& ($mediaVersion ||
$attribs['options']['version'] !== 'auto'))
			{
				$src .= '?' .
($attribs['options']['version'] === 'auto' ?
$mediaVersion : $attribs['options']['version']);
			}

			$buffer .= $tab;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<!--[if ' . $conditional . ']>';
			}

			$buffer .= '<link href="' . $src . '"
rel="stylesheet"';

			// Add script tag attributes.
			foreach ($attribs as $attrib => $value)
			{
				// Don't add the 'options' attribute. This attribute is
for internal use (version, conditional, etc).
				if ($attrib === 'options')
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if (in_array($attrib, array('type', 'mime'))
&& $document->isHtml5() && in_array($value,
$defaultCssMimes))
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if ($attrib === 'mime')
				{
					$attrib = 'type';
				}

				// Add attribute to script tag output.
				$buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT,
'UTF-8');

				// Json encode value if it's an array.
				$value = !is_scalar($value) ? json_encode($value) : $value;

				$buffer .= '="' . htmlspecialchars($value, ENT_COMPAT,
'UTF-8') . '"';
			}

			$buffer .= $tagEnd;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<![endif]-->';
			}

			$buffer .= $lnEnd;
		}

		// Generate stylesheet declarations
		foreach ($document->_style as $type => $content)
		{
			$buffer .= $tab . '<style';

			if (!is_null($type) && (!$document->isHtml5() ||
!in_array($type, $defaultCssMimes)))
			{
				$buffer .= ' type="' . $type . '"';
			}

			$buffer .= '>' . $lnEnd;

			// This is for full XHTML support.
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '/*<![CDATA[*/' . $lnEnd;
			}

			$content = (array)$content;
			foreach ($content as $value) {
				$buffer .= $value . $lnEnd;
			}

			// See above note
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '/*]]>*/' . $lnEnd;
			}

			$buffer .= $tab . '</style>' . $lnEnd;
		}

		// Generate scripts options
		$scriptOptions = $document->getScriptOptions();

        if (!empty($scriptOptions))
		{
			$buffer .= $tab . '<script type="application/json"
class="joomla-script-options new">';

			$prettyPrint = (JDEBUG && defined('JSON_PRETTY_PRINT')
? JSON_PRETTY_PRINT : false);
			$jsonOptions = json_encode($scriptOptions, $prettyPrint);
			$jsonOptions = $jsonOptions ? $jsonOptions : '{}';

			$buffer .= $jsonOptions;
			$buffer .= '</script>' . $lnEnd;
		}

		$defaultJsMimes         = array('text/javascript',
'application/javascript', 'text/x-javascript',
'application/x-javascript');
		$html5NoValueAttributes = array('defer', 'async');

		$excludeJsFiles = $this->getHeadCache();

        // Generate script file links
		foreach ($document->_scripts as $src => $attribs)
		{
            foreach ($excludeJsFiles as $exclude)
            {
                foreach ($this->keeperJsFiles as $keeper)
                {
                    if (strstr($exclude, $keeper))
                    {
                        continue 2;
                    }
                }

                if (strstr($src, $exclude))
                {
                    continue 2;
                }
            }
			// Check if script uses IE conditional statements.
			$conditional = isset($attribs['options']) &&
isset($attribs['options']['conditional']) ?
$attribs['options']['conditional'] : null;

			// Check if script uses media version.
			if (isset($attribs['options']['version']) &&
$attribs['options']['version'] && strpos($src,
'?') === false
				&& ($mediaVersion ||
$attribs['options']['version'] !== 'auto'))
			{
				$src .= '?' .
($attribs['options']['version'] === 'auto' ?
$mediaVersion : $attribs['options']['version']);
			}

			$buffer .= $tab;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<!--[if ' . $conditional . ']>';
			}

			$buffer .= '<script src="' . $src .
'"';

			// Add script tag attributes.
			foreach ($attribs as $attrib => $value)
			{
				// Don't add the 'options' attribute. This attribute is
for internal use (version, conditional, etc).
				if ($attrib === 'options')
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if (in_array($attrib, array('type', 'mime'))
&& $document->isHtml5() && in_array($value,
$defaultJsMimes))
				{
					continue;
				}

				// B/C: If defer and async is false or empty don't render the
attribute.
				if (in_array($attrib, array('defer', 'async'))
&& !$value)
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if ($attrib === 'mime')
				{
					$attrib = 'type';
				}
				// B/C defer and async can be set to yes when using the old method.
				elseif (in_array($attrib, array('defer', 'async'))
&& $value === true)
				{
					$value = $attrib;
				}

				// Add attribute to script tag output.
				$buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT,
'UTF-8');

				if (!($document->isHtml5() && in_array($attrib,
$html5NoValueAttributes)))
				{
					// Json encode value if it's an array.
					$value = !is_scalar($value) ? json_encode($value) : $value;

					$buffer .= '="' . htmlspecialchars($value, ENT_COMPAT,
'UTF-8') . '"';
				}
			}

			$buffer .= '></script>';

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<![endif]-->';
			}

			$buffer .= $lnEnd;
		}

		// Generate script declarations
		foreach ($document->_script as $type => $content)
		{
			$buffer .= $tab . '<script';

			if (!is_null($type) && (!$document->isHtml5() ||
!in_array($type, $defaultJsMimes)))
			{
				$buffer .= ' type="' . $type . '"';
			}

			$buffer .= '>' . $lnEnd;

			// This is for full XHTML support.
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '//<![CDATA[' . $lnEnd;
			}

			if (is_array($content)) {
				$content = implode(' ', $content);
			}
			$buffer .= $content . $lnEnd;

			// See above note
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '//]]>' . $lnEnd;
			}

			$buffer .= $tab . '</script>' . $lnEnd;
		}

		// Output the custom tags - array_unique makes sure that we don't
output the same tags twice
		foreach (array_unique($document->_custom) as $custom)
		{
			$buffer .= $tab . $custom . $lnEnd;
		}

		return ltrim($buffer, $tab);
	}

    private function getHeadCache()
    {
        $session = Factory::getSession();
        $doc = Factory::getDocument();
        $app = Factory::getApplication();
        $uri =
parse_url($app->input->server->get('HTTP_REFERER',
'', 'string'));
        $key = $uri['path'];
        $qs = ArrayHelper::getValue($uri, 'query', '');

        if (!empty($qs))
        {
            $key .= '?' . $qs;
        }

        $key = md5($key);
        $scripts = $this->excludeJsFiles;

        if (!empty($key))
        {
            $key = 'fabrik.js.head.cache.' . $key;
            $cachedScripts = $session->get($key, '');
            if (!empty($cachedScripts))
            {
                $scripts = json_decode($cachedScripts);
                $scripts = ArrayHelper::fromObject($scripts);
                $scripts = array_keys($scripts);
            }
        }

        return $scripts;
    }
}
fabrik/fabrik/Document/Renderer/Partial/MessageRenderer.php000064400000004165151165341570017762
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Partial;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Layout\LayoutHelper;

/**
 * HTML document renderer for the system message queue
 *
 * @since  3.5
 */
class MessageRenderer extends DocumentRenderer
{
	/**
	 * Renders the error stack and returns the results as a string
	 *
	 * @param   string  $name     Not used.
	 * @param   array   $params   Associative array of values
	 * @param   string  $content  Not used.
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($name, $params = array(), $content = null)
	{
		$msgList     = $this->getData();
		$displayData = array(
			'msgList' => $msgList,
			'name'    => $name,
			'params'  => $params,
			'content' => $content,
		);

		$app        = Factory::getApplication();
		$chromePath = JPATH_THEMES . '/' . $app->getTemplate() .
'/html/message.php';

		if (file_exists($chromePath))
		{
			include_once $chromePath;
		}

		if (function_exists('renderMessage'))
		{
			Log::add('renderMessage() is deprecated. Override system message
rendering with layouts instead.', Log::WARNING,
'deprecated');

			return renderMessage($msgList);
		}

		return LayoutHelper::render('joomla.system.message',
$displayData);
	}

	/**
	 * Get and prepare system message data for output
	 *
	 * @return  array  An array contains system message
	 *
	 * @since   3.5
	 */
	private function getData()
	{
		// Initialise variables.
		$lists = array();

		// Get the message queue
		$messages = Factory::getApplication()->getMessageQueue();

		// Build the sorted message list
		if (is_array($messages) && !empty($messages))
		{
			foreach ($messages as $msg)
			{
				if (isset($msg['type']) &&
isset($msg['message']))
				{
					$lists[$msg['type']][] = $msg['message'];
				}
			}
		}

		return $lists;
	}
}
fabrik/fabrik/Document/Renderer/Partial/ModuleRenderer.php000064400000005351151165341570017621
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Partial;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Registry\Registry;

/**
 * HTML document renderer for a single module
 *
 * @since  3.5
 */
class ModuleRenderer extends DocumentRenderer
{
	/**
	 * Renders a module script and returns the results as a string
	 *
	 * @param   string  $module   The name of the module to render
	 * @param   array   $attribs  Associative array of values
	 * @param   string  $content  If present, module information from the
buffer will be used
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($module, $attribs = array(), $content = null)
	{
		if (!is_object($module))
		{
			$title = isset($attribs['title']) ?
$attribs['title'] : null;

			$module = ModuleHelper::getModule($module, $title);

			if (!is_object($module))
			{
				if (is_null($content))
				{
					return '';
				}

				/**
				 * If module isn't found in the database but data has been pushed
in the buffer
				 * we want to render it
				 */
				$tmp = $module;
				$module = new \stdClass;
				$module->params = null;
				$module->module = $tmp;
				$module->id = 0;
				$module->user = 0;
			}
		}

		// Set the module content
		if (!is_null($content))
		{
			$module->content = $content;
		}

		// Get module parameters
		$params = new Registry($module->params);

		// Use parameters from template
		if (isset($attribs['params']))
		{
			$template_params = new
Registry(html_entity_decode($attribs['params'], ENT_COMPAT,
'UTF-8'));
			$params->merge($template_params);
			$module = clone $module;
			$module->params = (string) $params;
		}

		// Default for compatibility purposes. Set cachemode parameter or use
ModuleHelper::moduleCache from within the module instead
		$cachemode = $params->get('cachemode',
'oldstatic');

		if ($params->get('cache', 0) == 1 &&
Factory::getApplication()->getConfig()->get('caching')
>= 1 && $cachemode != 'id' && $cachemode !=
'safeuri')
		{
			// Default to itemid creating method and workarounds on
			$cacheparams = new \stdClass;
			$cacheparams->cachemode = $cachemode;
			$cacheparams->class = 'ModuleHelper';
			$cacheparams->method = 'renderModule';
			$cacheparams->methodparams = array($module, $attribs);

			return ModuleHelper::ModuleCache($module, $params, $cacheparams);
		}

		return ModuleHelper::renderModule($module, $attribs);
	}
}
fabrik/fabrik/Document/Renderer/Partial/ModulesRenderer.php000064400000003665151165341570020012
0ustar00<?php
/**
 * Partial Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Partial;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Layout\LayoutHelper;

/**
 * HTML document renderer for a module position
 *
 * @since  3.5
 */
class ModulesRenderer extends DocumentRenderer
{
	/**
	 * Renders multiple modules script and returns the results as a string
	 *
	 * @param   string  $position  The position of the modules to render
	 * @param   array   $params    Associative array of values
	 * @param   string  $content   Module content
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($position, $params = array(), $content = null)
	{
		$renderer = $this->_doc->loadRenderer('module');
		$buffer   = '';

		$app          = Factory::getApplication();
		$user         = Factory::getUser();
		$frontediting = ($app->isClient('site') &&
$app->get('frontediting', 1) && !$user->guest);
		$menusEditing = ($app->get('frontediting', 1) == 2)
&& $user->authorise('core.edit',
'com_menus');

		foreach (ModuleHelper::getModules($position) as $mod)
		{
			$moduleHtml = $renderer->render($mod, $params, $content);

			if ($frontediting && trim($moduleHtml) != ''
&& $user->authorise('module.edit.frontend',
'com_modules.module.' . $mod->id))
			{
				$displayData = array('moduleHtml' => &$moduleHtml,
'module' => $mod, 'position' => $position,
'menusediting' => $menusEditing);
				LayoutHelper::render('joomla.edit.frontediting_modules',
$displayData);
			}

			$buffer .= $moduleHtml;
		}

		Factory::getApplication()->getDispatcher()->dispatch('onAfterRenderModules',
array(&$buffer, &$params));

		return $buffer;
	}
}
fabrik/fabrik/Document/Renderer/Pdf/ComponentRenderer.php000064400000001633151165341570017452
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Pdf;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Document\DocumentRenderer;

/**
 * HTML document renderer for the component output
 *
 * @since  3.5
 */
class ComponentRenderer extends DocumentRenderer
{
	/**
	 * Renders a component script and returns the results as a string
	 *
	 * @param   string  $component  The name of the component to render
	 * @param   array   $params     Associative array of values
	 * @param   string  $content    Content script
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($component = null, $params = array(), $content =
null)
	{
		return $content;
	}
}
fabrik/fabrik/Document/Renderer/Pdf/HeadRenderer.php000064400000025272151165341570016356
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Pdf;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Document\HtmlDocument;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Utilities\ArrayHelper;

/**
 * HTML document renderer for the document `<head>` element
 *
 * @since  3.5
 */
class HeadRenderer extends DocumentRenderer
{
	/**
	 * Renders the document head and returns the results as a string
	 *
	 * @param   string  $head     (unused)
	 * @param   array   $params   Associative array of values
	 * @param   string  $content  The script
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($head, $params = array(), $content = null)
	{
		return $this->fetchHead($this->_doc);
	}

	/**
	 * Generates the head HTML and return the results as a string
	 *
	 * @param   JDocumentHtml  $document  The document for which the head will
be created
	 *
	 * @return  string  The head hTML
	 *
	 * @since   3.5
	 * @deprecated  4.0  Method code will be moved into the render method
	 */
	public function fetchHead($document)
	{
		// Convert the tagids to titles
		if (isset($document->_metaTags['name']['tags']))
		{
			$tagsHelper = new TagsHelper;
			$document->_metaTags['name']['tags'] =
implode(', ',
$tagsHelper->getTagNames($document->_metaTags['name']['tags']));
		}

		if ($document->getScriptOptions())
		{
			HTMLHelper::_('behavior.core');
		}

		// Trigger the onBeforeCompileHead event
		$app = Factory::getApplication();
		$app->getDispatcher()->dispatch('onBeforeCompileHead');

		// Get line endings
		$lnEnd        = $document->_getLineEnd();
		$tab          = $document->_getTab();
		$tagEnd       = ' />';
		$buffer       = '';
		$mediaVersion = $document->getMediaVersion();

		// Generate charset when using HTML5 (should happen first)
		if ($document->isHtml5())
		{
			$buffer .= $tab . '<meta charset="' .
$document->getCharset() . '" />' . $lnEnd;
		}

		// Generate base tag (need to happen early)
		$base = $document->getBase();

		if (!empty($base))
		{
			$buffer .= $tab . '<base href="' . $base .
'" />' . $lnEnd;
		}

		// Generate META tags (needs to happen as early as possible in the head)
		foreach ($document->_metaTags as $type => $tag)
		{
			foreach ($tag as $name => $content)
			{
				if ($type == 'http-equiv' &&
!($document->isHtml5() && $name == 'content-type'))
				{
					$buffer .= $tab . '<meta http-equiv="' . $name .
'" content="' . htmlspecialchars($content, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
				}
				elseif ($type != 'http-equiv' && !empty($content))
				{
					if (is_array($content))
					{
						foreach ($content as $value)
						{
							$buffer .= $tab . '<meta ' . $type .
'="' . $name . '" content="' .
htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"
/>' . $lnEnd;
						}
					}
					else
					{
						$buffer .= $tab . '<meta ' . $type . '="'
. $name . '" content="' . htmlspecialchars($content,
ENT_COMPAT, 'UTF-8') . '" />' . $lnEnd;
					}
				}
			}
		}

		// Don't add empty descriptions
		$documentDescription = $document->getDescription();

		if ($documentDescription)
		{
			$buffer .= $tab . '<meta name="description"
content="' . htmlspecialchars($documentDescription, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
		}

		// Don't add empty generators
		$generator = $document->getGenerator();

		if ($generator)
		{
			$buffer .= $tab . '<meta name="generator"
content="' . htmlspecialchars($generator, ENT_COMPAT,
'UTF-8') . '" />' . $lnEnd;
		}

		$buffer .= $tab . '<title>' .
htmlspecialchars($document->getTitle(), ENT_COMPAT, 'UTF-8') .
'</title>' . $lnEnd;

		// Generate link declarations
		foreach ($document->_links as $link => $linkAtrr)
		{
			$buffer .= $tab . '<link href="' . $link .
'" ' . $linkAtrr['relType'] . '="'
. $linkAtrr['relation'] . '"';

			if (is_array($linkAtrr['attribs']))
			{
				if ($temp = ArrayHelper::toString($linkAtrr['attribs']))
				{
					$buffer .= ' ' . $temp;
				}
			}

			$buffer .= ' />' . $lnEnd;
		}

		$defaultCssMimes = array('text/css');

		// Generate stylesheet links
		foreach ($document->_styleSheets as $src => $attribs)
		{
			// Check if stylesheet uses IE conditional statements.
			$conditional = isset($attribs['options']) &&
isset($attribs['options']['conditional']) ?
$attribs['options']['conditional'] : null;

			// Check if script uses media version.
			if (isset($attribs['options']['version']) &&
$attribs['options']['version'] && strpos($src,
'?') === false
				&& ($mediaVersion ||
$attribs['options']['version'] !== 'auto'))
			{
				$src .= '?' .
($attribs['options']['version'] === 'auto' ?
$mediaVersion : $attribs['options']['version']);
			}

			$buffer .= $tab;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<!--[if ' . $conditional . ']>';
			}

			$buffer .= '<link href="' . $src . '"
rel="stylesheet"';

			// Add script tag attributes.
			foreach ($attribs as $attrib => $value)
			{
				// Don't add the 'options' attribute. This attribute is
for internal use (version, conditional, etc).
				if ($attrib === 'options')
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if (in_array($attrib, array('type', 'mime'))
&& $document->isHtml5() && in_array($value,
$defaultCssMimes))
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if ($attrib === 'mime')
				{
					$attrib = 'type';
				}

				// Add attribute to script tag output.
				$buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT,
'UTF-8');

				// Json encode value if it's an array.
				$value = !is_scalar($value) ? json_encode($value) : $value;

				$buffer .= '="' . htmlspecialchars($value, ENT_COMPAT,
'UTF-8') . '"';
			}

			$buffer .= $tagEnd;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<![endif]-->';
			}

			$buffer .= $lnEnd;
		}

		// Generate stylesheet declarations
		foreach ($document->_style as $type => $content)
		{
			$buffer .= $tab . '<style';

			if (!is_null($type) && (!$document->isHtml5() ||
!in_array($type, $defaultCssMimes)))
			{
				$buffer .= ' type="' . $type . '"';
			}

			$buffer .= '>' . $lnEnd;

			// This is for full XHTML support.
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '/*<![CDATA[*/' . $lnEnd;
			}

			$buffer .= $content . $lnEnd;

			// See above note
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '/*]]>*/' . $lnEnd;
			}

			$buffer .= $tab . '</style>' . $lnEnd;
		}

		// Generate scripts options
		$scriptOptions = $document->getScriptOptions();

		if (!empty($scriptOptions))
		{
			$buffer .= $tab . '<script type="application/json"
class="joomla-script-options new">';

			$prettyPrint = (JDEBUG && defined('JSON_PRETTY_PRINT')
? JSON_PRETTY_PRINT : false);
			$jsonOptions = json_encode($scriptOptions, $prettyPrint);
			$jsonOptions = $jsonOptions ? $jsonOptions : '{}';

			$buffer .= $jsonOptions;
			$buffer .= '</script>' . $lnEnd;
		}

		$defaultJsMimes         = array('text/javascript',
'application/javascript', 'text/x-javascript',
'application/x-javascript');
		$html5NoValueAttributes = array('defer', 'async');

		// Generate script file links
		foreach ($document->_scripts as $src => $attribs)
		{
			// Check if script uses IE conditional statements.
			$conditional = isset($attribs['options']) &&
isset($attribs['options']['conditional']) ?
$attribs['options']['conditional'] : null;

			// Check if script uses media version.
			if (isset($attribs['options']['version']) &&
$attribs['options']['version'] && strpos($src,
'?') === false
				&& ($mediaVersion ||
$attribs['options']['version'] !== 'auto'))
			{
				$src .= '?' .
($attribs['options']['version'] === 'auto' ?
$mediaVersion : $attribs['options']['version']);
			}

			$buffer .= $tab;

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<!--[if ' . $conditional . ']>';
			}

			$buffer .= '<script src="' . $src .
'"';

			// Add script tag attributes.
			foreach ($attribs as $attrib => $value)
			{
				// Don't add the 'options' attribute. This attribute is
for internal use (version, conditional, etc).
				if ($attrib === 'options')
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if (in_array($attrib, array('type', 'mime'))
&& $document->isHtml5() && in_array($value,
$defaultJsMimes))
				{
					continue;
				}

				// B/C: If defer and async is false or empty don't render the
attribute.
				if (in_array($attrib, array('defer', 'async'))
&& !$value)
				{
					continue;
				}

				// Don't add type attribute if document is HTML5 and it's a
default mime type. 'mime' is for B/C.
				if ($attrib === 'mime')
				{
					$attrib = 'type';
				}
				// B/C defer and async can be set to yes when using the old method.
				elseif (in_array($attrib, array('defer', 'async'))
&& $value === true)
				{
					$value = $attrib;
				}

				// Add attribute to script tag output.
				$buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT,
'UTF-8');

				if (!($document->isHtml5() && in_array($attrib,
$html5NoValueAttributes)))
				{
					// Json encode value if it's an array.
					$value = !is_scalar($value) ? json_encode($value) : $value;

					$buffer .= '="' . htmlspecialchars($value, ENT_COMPAT,
'UTF-8') . '"';
				}
			}

			$buffer .= '></script>';

			// This is for IE conditional statements support.
			if (!is_null($conditional))
			{
				$buffer .= '<![endif]-->';
			}

			$buffer .= $lnEnd;
		}

		// Generate script declarations
		foreach ($document->_script as $type => $content)
		{
			$buffer .= $tab . '<script';

			if (!is_null($type) && (!$document->isHtml5() ||
!in_array($type, $defaultJsMimes)))
			{
				$buffer .= ' type="' . $type . '"';
			}

			$buffer .= '>' . $lnEnd;

			// This is for full XHTML support.
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '//<![CDATA[' . $lnEnd;
			}

			$buffer .= $content . $lnEnd;

			// See above note
			if ($document->_mime != 'text/html')
			{
				$buffer .= $tab . $tab . '//]]>' . $lnEnd;
			}

			$buffer .= $tab . '</script>' . $lnEnd;
		}

		// Output the custom tags - array_unique makes sure that we don't
output the same tags twice
		foreach (array_unique($document->_custom) as $custom)
		{
			$buffer .= $tab . $custom . $lnEnd;
		}

		return ltrim($buffer, $tab);
	}
}
fabrik/fabrik/Document/Renderer/Pdf/MessageRenderer.php000064400000004155151165341570017076
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Pdf;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Layout\LayoutHelper;

/**
 * HTML document renderer for the system message queue
 *
 * @since  3.5
 */
class MessageRenderer extends DocumentRenderer
{
	/**
	 * Renders the error stack and returns the results as a string
	 *
	 * @param   string  $name     Not used.
	 * @param   array   $params   Associative array of values
	 * @param   string  $content  Not used.
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($name, $params = array(), $content = null)
	{
		$msgList     = $this->getData();
		$displayData = array(
			'msgList' => $msgList,
			'name'    => $name,
			'params'  => $params,
			'content' => $content,
		);

		$app        = Factory::getApplication();
		$chromePath = JPATH_THEMES . '/' . $app->getTemplate() .
'/html/message.php';

		if (file_exists($chromePath))
		{
			include_once $chromePath;
		}

		if (function_exists('renderMessage'))
		{
			Log::add('renderMessage() is deprecated. Override system message
rendering with layouts instead.', Log::WARNING,
'deprecated');

			return renderMessage($msgList);
		}

		return LayoutHelper::render('joomla.system.message',
$displayData);
	}

	/**
	 * Get and prepare system message data for output
	 *
	 * @return  array  An array contains system message
	 *
	 * @since   3.5
	 */
	private function getData()
	{
		// Initialise variables.
		$lists = array();

		// Get the message queue
		$messages = Factory::getApplication()->getMessageQueue();

		// Build the sorted message list
		if (is_array($messages) && !empty($messages))
		{
			foreach ($messages as $msg)
			{
				if (isset($msg['type']) &&
isset($msg['message']))
				{
					$lists[$msg['type']][] = $msg['message'];
				}
			}
		}

		return $lists;
	}
}
fabrik/fabrik/Document/Renderer/Pdf/ModuleRenderer.php000064400000005341151165341570016735
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Pdf;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Registry\Registry;

/**
 * HTML document renderer for a single module
 *
 * @since  3.5
 */
class ModuleRenderer extends DocumentRenderer
{
	/**
	 * Renders a module script and returns the results as a string
	 *
	 * @param   string  $module   The name of the module to render
	 * @param   array   $attribs  Associative array of values
	 * @param   string  $content  If present, module information from the
buffer will be used
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($module, $attribs = array(), $content = null)
	{
		if (!is_object($module))
		{
			$title = isset($attribs['title']) ?
$attribs['title'] : null;

			$module = ModuleHelper::getModule($module, $title);

			if (!is_object($module))
			{
				if (is_null($content))
				{
					return '';
				}

				/**
				 * If module isn't found in the database but data has been pushed
in the buffer
				 * we want to render it
				 */
				$tmp = $module;
				$module = new \stdClass;
				$module->params = null;
				$module->module = $tmp;
				$module->id = 0;
				$module->user = 0;
			}
		}

		// Set the module content
		if (!is_null($content))
		{
			$module->content = $content;
		}

		// Get module parameters
		$params = new Registry($module->params);

		// Use parameters from template
		if (isset($attribs['params']))
		{
			$template_params = new
Registry(html_entity_decode($attribs['params'], ENT_COMPAT,
'UTF-8'));
			$params->merge($template_params);
			$module = clone $module;
			$module->params = (string) $params;
		}

		// Default for compatibility purposes. Set cachemode parameter or use
ModuleHelper::moduleCache from within the module instead
		$cachemode = $params->get('cachemode',
'oldstatic');

		if ($params->get('cache', 0) == 1 &&
Factory::getApplication()->getConfig()->get('caching')
>= 1 && $cachemode != 'id' && $cachemode !=
'safeuri')
		{
			// Default to itemid creating method and workarounds on
			$cacheparams = new \stdClass;
			$cacheparams->cachemode = $cachemode;
			$cacheparams->class = 'ModuleHelper';
			$cacheparams->method = 'renderModule';
			$cacheparams->methodparams = array($module, $attribs);

			return ModuleHelper::ModuleCache($module, $params, $cacheparams);
		}

		return ModuleHelper::renderModule($module, $attribs);
	}
}
fabrik/fabrik/Document/Renderer/Pdf/ModulesRenderer.php000064400000003655151165341570017126
0ustar00<?php
/**
 * PDF Document class
 *
 * @package     Joomla
 * @subpackage  Fabrik.Documents
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Document\Renderer\Pdf;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Layout\LayoutHelper;

/**
 * HTML document renderer for a module position
 *
 * @since  3.5
 */
class ModulesRenderer extends DocumentRenderer
{
	/**
	 * Renders multiple modules script and returns the results as a string
	 *
	 * @param   string  $position  The position of the modules to render
	 * @param   array   $params    Associative array of values
	 * @param   string  $content   Module content
	 *
	 * @return  string  The output of the script
	 *
	 * @since   3.5
	 */
	public function render($position, $params = array(), $content = null)
	{
		$renderer = $this->_doc->loadRenderer('module');
		$buffer   = '';

		$app          = Factory::getApplication();
		$user         = Factory::getUser();
		$frontediting = ($app->isClient('site') &&
$app->get('frontediting', 1) && !$user->guest);
		$menusEditing = ($app->get('frontediting', 1) == 2)
&& $user->authorise('core.edit',
'com_menus');

		foreach (ModuleHelper::getModules($position) as $mod)
		{
			$moduleHtml = $renderer->render($mod, $params, $content);

			if ($frontediting && trim($moduleHtml) != ''
&& $user->authorise('module.edit.frontend',
'com_modules.module.' . $mod->id))
			{
				$displayData = array('moduleHtml' => &$moduleHtml,
'module' => $mod, 'position' => $position,
'menusediting' => $menusEditing);
				LayoutHelper::render('joomla.edit.frontediting_modules',
$displayData);
			}

			$buffer .= $moduleHtml;
		}

		Factory::getApplication()->getDispatcher()->dispatch('onAfterRenderModules',
array(&$buffer, &$params));

		return $buffer;
	}
}
fabrik/fabrik/Helpers/ArrayHelper.php000064400000026450151165341600013602
0ustar00<?php
/**
 * Array helper class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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');

/**
 * 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)
			{
				// avoid PHP error if property name is empty
				if ($k !== '')
				{
					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 ArrayHelper::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 (!is_scalar($name))
		{
			return $default;
		}

		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;
    }

}
fabrik/fabrik/Helpers/CustomSample.php000064400000003557151165341600014003
0ustar00<?php
/*
* Send sms's
*
* @package     Joomla
* @subpackage  Fabrik.helpers
* @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;
use Joomla\Utilities\ArrayHelper;

/**
 * Custom code
 *
 * To use, copy this file to Custom.php and rename the class from
CustomSample to Custom.
 *
 * Add your functions as 'public static' methods.
 *
 * Call them from anywhere you can run PHP code in Fabrik as
\Fabrik\Helpers\Custom::doMyThing(),
 * or FabrikCustom::doMyThing().  The latter is a class alias, which may be
deprecated in future versions.
 *
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @since       3.8
 */
class CustomSample
{
	private static $init = null;

	private static $config = null;

	private static $user = null;

	private static $app = null;

	private static $lang = null;

	private static $date = null;

	private static $session = null;

	private static $formModel = null;

	public static function __initStatic($config = array())
	{
		if (!isset(self::$init))
		{
			self::$config  = ArrayHelper::getValue($config, 'config',
Factory::getApplication()->getConfig());
			self::$user    = ArrayHelper::getValue($config, 'user',
Factory::getUser());
			self::$app     = ArrayHelper::getValue($config, 'app',
Factory::getApplication());
			self::$lang    = ArrayHelper::getValue($config, 'lang',
Factory::getApplication()->getLanguage());
			self::$date    = ArrayHelper::getValue($config, 'date',
Factory::getDate());
			self::$session = ArrayHelper::getValue($config, 'session',
Factory::getSession());
			self::$formModel = ArrayHelper::getValue($config, 'formModel',
null);
			self::$init    = true;
		}
	}

	public static function doMyThing()
	{
		return true;
	}
}
fabrik/fabrik/Helpers/Element.php000064400000010462151165341600012751
0ustar00<?php
/**
 * Element Helper class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;

/**
 * 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 = Factory::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;
	}
}
fabrik/fabrik/Helpers/FCipher.php000064400000020753151165341600012704
0ustar00<?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\Table\Table;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
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 = Factory::getApplication()->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);
			$this->cipher = new Aes($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 = ComponentHelper::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 = ComponentHelper::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 =
ComponentHelper::getComponent('com_fabrik')->id;
		$table = Table::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 =
Factory::getApplication()->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;
	}


}fabrik/fabrik/Helpers/Googlemap.php000064400000004002151165341600013263
0ustar00<?php
/**
 * Google Map helper class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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;
	}
}
fabrik/fabrik/Helpers/Html.php000064400000261001151165341600012261
0ustar00<?php
/**
 * Fabrik Component HTML Helper
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Layout\LayoutInterface;
use Joomla\CMS\Version;
use Joomla\CMS\Environment\Browser;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\HTML\Helpers\Bootstrap;
use Joomla\CMS\Filesystem\File;
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')
	{
		$layout                =
self::getLayout('listactions.dropdown');
		$displayData           = array();
		$displayData['align']  = $align;
		$displayData['items']  = $lis;

		return $layout->render($displayData);
	}

	/**
	 * 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    = Factory::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                   = Factory::getApplication();
		$input                 = $app->input;
		$layout                =
self::getLayout('form.fabrik-email-form');
		$displayData           = new stdClass;
//		$displayData->j3       = Worker::j3();
		$displayData->j3       = true;
		$displayData->package  =
$app->getUserState('com_fabrik.package', 'fabrik');
		$displayData->referrer = $input->get('referrer',
'', 'string');
		$document              = Factory::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   = Factory::getApplication()->getConfig();
		$document = Factory::getDocument();
		$document->setTitle($config->get('sitename'));

	}

	/**
	 * 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 = '&nbsp;' .
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     = Factory::getApplication();
		$input   = $app->input;
		$form    = $formModel->getForm();
		$package = $app->getUserState('com_fabrik.package',
'fabrik');
		$table   = $formModel->getTable();

		if ($app->isClient('administrator'))
		{
			$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 = Route::_($url);

		// $$$ rob for some reason Route wasn't doing this ???
		//$url            = str_replace('&', '&amp;',
$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     = Factory::getApplication();
		$input   = $app->input;
		$package = $app->getUserState('com_fabrik.package',
'fabrik');

		if ($app->isClient('administrator'))
		{
			$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(Uri::getInstance()->toString());
		self::$emailURL = Route::_($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[] = HTMLHelper::_('select.option', 'AND',
Text::_('COM_FABRIK_AND'));
		$conditions[] = HTMLHelper::_('select.option', 'OR',
Text::_('COM_FABRIK_OR'));
		$name         = 'fabrik___filter[list_' . $listId .
'][join][]';

		return HTMLHelper::_('select.genericlist', $conditions, $name,
'class="form-select-sm" 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('#__fabrik_lists')->where('published
= 1')->order('label');
		$db->setQuery($query);
		$rows = $db->loadObjectList();

		return HTMLHelper::_('select.genericlist', $rows,
'fabrik__swaptable', 'class="form-select" ',
'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 = [])
	{
		// $$$ 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;
		}

		$opts = http_build_query($attribs);
		if (!empty($opts)) $file .= $opts;

		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\">
					function loadMyJs(file) {
						let s = document.createElement('script');
						s.setAttribute('src', file);
						s.setAttribute('async', true);
						document.head.appendChild(s);
						return;
					}
				loadMyJs('".$file."');
    		</script>\n";
					self::$ajaxCssFiles[] = $file;
				}
			}
		}
		else
		{
			$document = Factory::getDocument();
			/* $$$ rob 27/04/2011 changed from HTMLHelper::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    = Factory::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 (File::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;
		}

		HTMLHelper::_('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 = Factory::getDocument();
		$tag      = Factory::getApplication()->getLanguage()->getTag();
		$attribs  = array('title' =>
Text::_('JLIB_HTML_BEHAVIOR_GREEN'), 'media' =>
'all');
		HTMLHelper::_('stylesheet',
'com_fabrik/calendar-jos.css', array('version' =>
'auto', 'relative' => true), $attribs);
		HTMLHelper::_('script',
'media/com_fabrik/js/dist/calendar.js');
		HTMLHelper::_('script',
'media/com_fabrik/js/dist/calendar-setup.js');
		$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 Text::_() using
array_map()
		$callback = array('Joomla\CMS\Language\Text', '_');
		$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 = " " . Text::_('JLIB_HTML_BEHAVIOR_TODAY') .
" ";
		$text = array(
			'INFO'           =>
Text::_('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"
				. Text::_('JLIB_HTML_BEHAVIOR_DATE_SELECTION')
				. Text::_('JLIB_HTML_BEHAVIOR_YEAR_SELECT')
				. Text::_('JLIB_HTML_BEHAVIOR_MONTH_SELECT')
				. Text::_('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'       =>
Text::_('JLIB_HTML_BEHAVIOR_PREV_YEAR_HOLD_FOR_MENU'),
			'PREV_MONTH'      =>
Text::_('JLIB_HTML_BEHAVIOR_PREV_MONTH_HOLD_FOR_MENU'),
			'GO_TODAY'        =>
Text::_('JLIB_HTML_BEHAVIOR_GO_TODAY'),
			'NEXT_MONTH'      =>
Text::_('JLIB_HTML_BEHAVIOR_NEXT_MONTH_HOLD_FOR_MENU'),
			'SEL_DATE'        =>
Text::_('JLIB_HTML_BEHAVIOR_SELECT_DATE'),
			'DRAG_TO_MOVE'    =>
Text::_('JLIB_HTML_BEHAVIOR_DRAG_TO_MOVE'),
			'PART_TODAY'      => $today,
			'DAY_FIRST'       =>
Text::_('JLIB_HTML_BEHAVIOR_DISPLAY_S_FIRST'),
			'WEEKEND'         =>
Factory::getApplication()->getLanguage()->getWeekEnd(),
			'CLOSE'           =>
Text::_('JLIB_HTML_BEHAVIOR_CLOSE'),
			'TODAY'           =>
Text::_('JLIB_HTML_BEHAVIOR_TODAY'),
			'TIME_PART'       =>
Text::_('JLIB_HTML_BEHAVIOR_SHIFT_CLICK_OR_DRAG_TO_CHANGE_VALUE'),
			'DEF_DATE_FORMAT' => "%Y-%m-%d",
			'TT_DATE_FORMAT'  =>
Text::_('JLIB_HTML_BEHAVIOR_TT_DATE_FORMAT'),
			'WK'              =>
Text::_('JLIB_HTML_BEHAVIOR_WK'),
			'TIME'            =>
Text::_('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     = Factory::getApplication();
			Html::modalLayoutInterfaces();
			$liveSiteSrc = array();
			$liveSiteReq = array();
			$fbConfig    = ComponentHelper::getParams('com_fabrik');

			$mediaFolder = self::getMediaFolder(); 
			$src         = array();
			HTMLHelper::_('jquery.framework', true);

			HTMLHelper::_('bootstrap.framework');
			self::loadBootstrapCSS();

			/* Load mootools & jquery-ui as it is not loaded by Joomla any more
*/
			if(!self::isDebug()){
				HTMLHelper::_('script',
'media/com_fabrik/js/lib/jquery-ui/jquery-ui.min.js');
//jquery-ui for fabrik v1.13.2 - 2022-07-14
				//HTMLHelper::_('script',
'media/vendor/jquery-ui/jquery-ui.min.js'); //jquery-ui for
joomla v1.9.2 - 2016-01-22
				HTMLHelper::_('script',
'media/com_fabrik/js/dist/mootools-core.js');
				HTMLHelper::_('script',
'media/com_fabrik/js/dist/mootools-more.js');
			} else {
				HTMLHelper::_('script',
'media/com_fabrik/js/lib/jquery-ui/jquery-ui.js'); //jquery-ui
for fabrik v1.13.2 - 2022-07-14
				//HTMLHelper::_('script',
'media/vendor/jquery-ui/jquery-ui.js'); //jquery-ui for joomla
v1.9.2 - 2016-01-22
				HTMLHelper::_('script',
'media/com_fabrik/js/mootools-core.js');
				HTMLHelper::_('script',
'media/com_fabrik/js/mootools-more.js');
			}	

			HTMLHelper::_('behavior.formvalidator');

			$liveSiteReq['Chosen'] = $mediaFolder .
'/chosen-loader';
			$liveSiteReq['Fabrik'] = $mediaFolder . '/fabrik';
			$liveSiteReq['FloatingTips'] = $mediaFolder .
'/tipsBootStrapMock';

			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 =
Text::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS');
                }

				if (is_object($chosenOptions) &&
!isset($chosenOptions->placeholder_text_single))
				{
					$chosenOptions->placeholder_text_single =
Text::_('JGLOBAL_SELECT_AN_OPTION');
				}

				if (is_object($chosenOptions) &&
!isset($chosenOptions->no_results_text))
				{
					$chosenOptions->no_results_text =
Text::_('JGLOBAL_SELECT_NO_RESULTS_MATCH');
				}

				$chosenOptions = empty($chosenOptions) ? new stdClass :
ArrayHelper::fromObject($chosenOptions);
				if(self::isDebug()){
					HTMLHelper::_('stylesheet',
'media/com_fabrik/css/chosen.css');
					HTMLHelper::_('script',
'media/com_fabrik/js/chosen.jquery.js');					
				}
				else {
					HTMLHelper::_('stylesheet',
'media/com_fabrik/css/chosen.min.css');
					HTMLHelper::_('script',
'media/com_fabrik/js/dist/chosen.jquery.js');
					//HTMLHelper::_('script', 'jui/ajax-chosen.min',
false, true, false, false, self::isDebug());
				}
			}

			if ($app->isClient('administrator') &&
$app->input->get('format') !== 'pdf') {
				/* For some reason this navbar is being shown for fabrik menu items, I
gave up after 5 hours of debug, this is easier 
				* trob: this is breaking domPDF on backend lists (somehow the style
loading as array), so don't do it if format=pdf
				*/
				Factory::getDocument()->addStyleDeclaration("button.navbar-toggler.toggler-burger
{display : none !important;}");
			}

			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()
				Text::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";
				$liveSiteSrc[] = "\tFabrik.bootstrapped = true;";

				$liveSiteSrc[] = self::tipInt();
				$liveSiteSrc   = implode("\n", $liveSiteSrc);
			}
			else
			{
				$liveSiteSrc[] = "\tFabrik.bootstrapped = true;";

				$liveSiteSrc[] = "\tif (!Fabrik.jLayouts) {
				Fabrik.jLayouts = {};
				}
				Fabrik.jLayouts = jQuery.extend(Fabrik.jLayouts, %%jLayouts%%);";
			}

			self::script($liveSiteReq, $liveSiteSrc, '-min.js');
			self::$framework = $src;
		}

		self::addToSessionLayoutInterfaces();

		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      = ComponentHelper::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      = Factory::getSession();
		self::$allRequirePaths = (object) array_merge((array)
self::requirePaths(), $paths);
		$framework    = array();
		$deps         = array();
//		$j3           = Worker::j3();
//		$j3           = true;

		$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 = Browser::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    = Factory::getApplication();
		$config = ComponentHelper::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/com_fabrik/js/dist/chosen.jquery';
			//$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';
			}

			$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 = Factory::getApplication();
		if ($force || $app->input->get('loadbootstrapcss',
'') !== '')
		{
			$doc = Factory::getDocument();
			Bootstrap::loadCss(true, $doc->direction);
		}
	}

	/**
	 * Get tip options to control its fx - set in Fabrik global configuration
	 *
	 * @return stdClass
	 */
	public static function tipOpts()
	{
		$usersConfig = ComponentHelper::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 LayoutInterface 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, object $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 = Factory::getApplication();

		if ($app->input->get('format') == 'raw')
		{
			echo '<style type="text/css">' . $style .
'</style>';
		}
		else
		{
			Factory::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     = Factory::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    = Factory::getApplication();
		$input  = $app->input;
		$option = $input->get('option');

		if ($option !== 'com_' . $package && $option !==
'com_content')
		{
			return false;
		}

		if (class_exists('JSite'))
		{
			$app   = Factory::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    = Factory::getApplication();
			$config = ComponentHelper::getParams('com_fabrik');

			/*
			if ($app->input->get('format', 'html') ===
'raw')
            {
                $debug = false;

                return false;
            }
			*/

			if ($enabled && $config->get('use_fabrikdebug') ==
0)
			{
			    $debug = false;

				return false;
			}

			if ($config->get('use_fabrikdebug') == 2)
			{
			    $debug = true;

				return true;
			}

			$config = Factory::getApplication()->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    = Factory::getApplication();
		$config = ComponentHelper::getParams('com_fabrik');

		if ($config->get('use_fabrikdebug') == 0)
		{
			return false;
		}

		$jConfig = Factory::getApplication()->getConfig();
		$debug   = (int) $jConfig->get('debug');

		return $debug === 1 &&
$app->input->get('fabrikdebug', 0) == 2;
	}

	/**
	 * Wrapper for HTMLHelperScript() 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 (File::exists(COM_FABRIK_BASE . $file))
				{
					$compressedFile = str_replace('.js', $ext, $file);

					if (File::exists(COM_FABRIK_BASE . $compressedFile) ||
File::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 addToSessionLayoutInterfaces()
	{
		$key     = 'fabrik.js.jlayouts';
		$session = Factory::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 = Factory::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 = Factory::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 = Factory::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 = Factory::getApplication()->input;

		if ($input->get('format') === 'raw')
		{
			return;
		}

		if (!self::$modal)
		{
			$fbConfig = ComponentHelper::getParams('com_fabrik');

			if ($fbConfig->get('include_lightbox_js', 1) == 0)
			{
				return;
			}

			if ($fbConfig->get('use_mediabox', 1))
			{
				$folder  = 'components/com_fabrik/libs/mediabox-advanced/';
				$mbStyle = $fbConfig->get('mediabox_style',
'Dark');
				HTMLHelper::stylesheet($folder . 'mediabox-' . $mbStyle .
'.css');
				self::script($folder . 'mediaboxAdv.js');
			}
			else
			{
//				if (Worker::j3())
//				{
					HTMLHelper::stylesheet('components/com_fabrik/libs/slimbox2/css/slimbox2.css');
					self::script('components/com_fabrik/libs/slimbox2/js/slimbox2.js');
/*
				}
				else
				{
					HTMLHelper::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()
	{
		$folder = 'media/com_fabrik/js/lib/slick/';
		$ext = self::isDebug() ? '.js' : '.min.js';
		self::script($folder . 'slick' . $ext);
		Html::stylesheet(COM_FABRIK_LIVESITE .
'media/com_fabrik/js/lib/slick/slick.css');
		Html::stylesheet(COM_FABRIK_LIVESITE .
'media/com_fabrik/js/lib/slick/slick-theme.css');

		$folder = 'media/com_fabrik/js/lib/elevatezoom-plus/';
		$ext = self::isDebug() ? '.js' : '.js';
		self::script($folder . 'jquery.ez-plus' . $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  = ComponentHelper::getParams('com_fabrik');
		$app     = Factory::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 = Factory::getApplication()->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 = Factory::getApplication()->input;

		if ($input->get('format') === 'raw')
		{
			return;
		}
		*/

		$json = self::autoCompleteOptions($htmlId, $elementId, $formId, $plugin,
$opts);
		$str  = json_encode($json);
		Text::script('COM_FABRIK_NO_AUTOCOMPLETE_RECORDS');
		Text::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           =
ComponentHelper::getParams('com_fabrik');
			$json->minTriggerChars = (int)
$usersConfig->get('autocomplete_min_trigger_chars',
'3');
		}

        if (!array_key_exists('max', $opts))
        {
            $usersConfig =
ComponentHelper::getParams('com_fabrik');
            $json->max   = (int)
$usersConfig->get('autocomplete_max_rows', '10');
        }

		if (!array_key_exists('autoLoadSingleResult', $opts))
		{
			$usersConfig           =
ComponentHelper::getParams('com_fabrik');
			$json->autoLoadSingleResult = (int)
$usersConfig->get('autocomplete_autoload_single',
'0');
		}

		$app       = Factory::getApplication();
		$package   = $app->getUserState('com_fabrik.package',
'fabrik');
		//$json->url = COM_FABRIK_LIVESITE . 'index.php?option=com_'
. $package . '&format=raw';
		$json->url = 'index.php?option=com_' . $package .
'&format=raw';
		$json->url .= $app->isClient('administrator') ?
'&task=plugin.pluginAjax' :
'&view=plugin&task=pluginAjax';
		$json->url .= '&' . Session::getFormToken() .
'=1';
		$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  = Factory::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                      = Factory::getApplication();
			$template                 = $app->getTemplate();

			switch ($type)
			{
				case 'image':
					if ($app->isClient('administrator'))
					{
						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_BASE . 'templates/'
. $template . '/custom/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 (File::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)
		if ($forceImage !== true)
		{
			unset($properties['alt']);
			$class = ArrayHelper::getValue($properties, 'icon-class',
'');
			$class = 'icon-' . File::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-bs-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())
	{
		$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[] = '<div class="btn-group"
role="group" ' . $dataAttributes . '>';

			foreach ($items as $i => $s)
			{
				$grid[] = $s;
			}

			$grid[] = '</div>';
		}
		else
		{
				$grid = self::bootstrapGrid($items, $optionsPerRow, 'form-check
fabrikgrid_' . $type);
		}

		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 = Browser::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    = Factory::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 = HTMLHelper::_('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 = HTMLHelper::_('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 = Factory::getApplication();

		if ($app->isClient('administrator'))
		{
			$db    = Factory::getContainer()->get('DatabaseDriver');
			$query = $db->getQuery(true);
			$query->select('introtext, ' .
$db->quoteName('fulltext'))->from('#__content')->where('id
= ' . (int) $contentTemplate);
			$db->setQuery($query);
			$res = $db->loadObject();
		}
		else
		{
			BaseDatabaseModel::addIncludePath(COM_FABRIK_BASE .
'components/com_content/models');
			$articleModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('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       = ComponentHelper::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;
            case 3:
                $rel = 'data-rel="lightcase';

                if (!empty($group))
	            {
		            $rel .= ':' . addslashes($group);
	            }

	            $rel .= '"';
                $attributes[] = $rel;

	            if (!empty($title))
	            {
		            $attributes[] = 'title="' . addslashes($title)
. '"';
	            }

                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(Uri::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(File::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
		Text::script('JLIB_FORM_FIELD_INVALID');

		// Include MooTools More framework
		static::framework('more');

		$debug   =
Factory::getApplication()->getConfig()->get('debug');
		HTMLHelper::_('script', 'system/fields/validate.js',
['version' => 'auto', 'relative' =>
true, 'detectDebug' => $debug]);
		static::$loaded[__METHOD__] = true;
	}

	/**
	 * Get the element's LayoutInterface 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 . '/' .
Factory::getApplication()->getTemplate() . '/html/layouts');
		$layout->addIncludePaths(JPATH_THEMES . '/' .
Factory::getApplication()->getTemplate() .
'/html/layouts/com_fabrik');

		foreach ($paths as $path)
		{
			$layout->addIncludePath($path);
		}

		return $layout;
	}

	/**
	 * Render an icon using LayoutInterfaces
	 *
	 * @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 modalLayoutInterfaces()
	{
		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 &eacute;,
	 * 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+;)/',
'&amp;', $html);
        $doc->loadXML($html);
		libxml_clear_errors();
		libxml_use_internal_errors($previous);

		return $doc;
	}
}
fabrik/fabrik/Helpers/Image.php000064400000006457151165341600012413
0ustar00<?php
/**
 * Image manipulation helper
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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;

use Joomla\CMS\HTML\HTMLHelper;
use \RuntimeException;

/**
 * Image manipulation class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - 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[] = HTMLHelper::_('select.option', $key, $val);
		}

		$im = self::testImagemagick();

		foreach ($im as $key => $val)
		{
			$libs[] = HTMLHelper::_('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;
	}
}

fabrik/fabrik/Helpers/Image/Image.php000064400000017004151165341610013424
0ustar00<?php
/**
 * @package     Joomla
 * @subpackage  Fabrik.image
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Helpers\Image;

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Fabrik\Helpers\ArrayHelper;
use Fabrik\Helpers\StringHelper;

/**
 * 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 File::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 (!Folder::exists($folder))
		{
			Folder::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 (File::exists($cacheFile))
		{
			// Check its age- Google T&C allow you to store for no more than 30
days.
			$createDate = Factory::getDate(filemtime($cacheFile));
			$now        = Factory::getDate();
			$interval   = $now->diff($createDate);
			$daysOld    = (float) $interval->format('%R%a');

			if ($daysOld < -$lifeTime)
			{
				// Remove out of date
				File::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&#xB0; %02d&#x2032;
%05.2f&#x2033;", $sign, $degree, $minute, $second);
	}

}
fabrik/fabrik/Helpers/Image/Imagegd.php000064400000025077151165341610013750
0ustar00<?php
/**
 * @package     Joomla
 * @subpackage  Fabrik.image
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Helpers\Image;

defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Filesystem\File;
use Fabrik\Helpers\StringHelper;

/**
 * GD image manipulation class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - 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(File::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(File::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    = Factory::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');
				}
			}
		}
	}
}
fabrik/fabrik/Helpers/Image/Imagegd2.php000064400000011034151165341610014016
0ustar00<?php
/**
 * @package     Joomla
 * @subpackage  Fabrik.image
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Helpers\Image;

defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Filesystem\File;
use \RuntimeException;
use Fabrik\Helpers\StringHelper;

/**
 * GD2 image manipulation class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - 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 = Factory::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);
				
				// 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;
			}
		}

		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 File 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);
		}
	}
}

fabrik/fabrik/Helpers/Image/Imageim.php000064400000012301151165341610013745
0ustar00<?php
/**
 * @package     Joomla
 * @subpackage  Fabrik.image
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Helpers\Image;

defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use \Fabrik\Helpers\StringHelper;
use \NewMagickWand;

/**
 * Image magic image manipulation class
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - 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    = Factory::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);
	}

}
fabrik/fabrik/Helpers/LayoutFile.php000064400000005661151165341610013443
0ustar00<?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\Filesystem\Path;
use Joomla\CMS\Layout\FileLayout;

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 FileLayout
{
	/**
	 * 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 = Path::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 = Path::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];
	}

}
fabrik/fabrik/Helpers/LogHelper.php000064400000010440151165341610013236
0ustar00<?php
/**
 * @package     Fabrik\Helpers
 * @subpackage
 *
 * @copyright   A copyright
 * @license     A "Slug" license name e.g. GPL2
 */

namespace Fabrik\Helpers;

defined('_JEXEC') or die;

use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Factory;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Session\Session;
use Joomla\CMS\User\User;
use Joomla\Utilities\ArrayHelper;
use GuzzleHttp\Client;

/**
 * Helper for the log form plugin.  Used when needing to log changes to
data outside of a
 * form submission.
 *
 * Call ...
 *
 * \Fabrik\Helpers\LogHelper::setOrigData($formId, $rowId);
 *
 * ... before changing the data for a row, and ...
 *
 * \Fabrik\Helpers\LogHelper::logRowChange($formId, $rowId);
 *
 * ... after changing data.  Will get settings from log plugin on that
formid, and
 * log accordingly.
 */

class LogHelper
{
	private static $init = null;

	/**
	 * @var null JConfig
	 * @since version
	 */
	private static $config = null;

	/**
	 * @var User
	 * @since version
	 */
	private static $user = null;

	/**
	 * @var CMSApplication
	 * @since version
	 */
	private static $app = null;

	private static $lang = null;

	private static $date = null;

	/**
	 * @var Session
	 * @since version
	 */
	private static $session = null;

	private static $formModel = null;

	private static $origData = [];

	public static function __initStatic($config = array())
	{
		if (!isset(self::$init))
		{
			self::$config  = ArrayHelper::getValue($config, 'config',
Factory::getApplication()->getConfig());
			self::$user    = ArrayHelper::getValue($config, 'user',
Factory::getUser());
			self::$app     = ArrayHelper::getValue($config, 'app',
Factory::getApplication());
			self::$lang    = ArrayHelper::getValue($config, 'lang',
Factory::getApplication()->getLanguage());
			self::$date    = ArrayHelper::getValue($config, 'date',
Factory::getDate());
			self::$session = ArrayHelper::getValue($config, 'session',
Factory::getSession());
			self::$formModel = ArrayHelper::getValue($config, 'formModel',
null);
			self::$init    = true;
		}
	}

	/**
	 * @param $formId
	 *
	 * @return \FabrikFEModelForm
	 *
	 * @since version
	 */
	private static function getFormModel($formId, $rowId)
	{
		Table::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_fabrik/tables');
		BaseDatabaseModel::addIncludePath(COM_FABRIK_FRONTEND .
'/models', 'FabrikFEModel');
		/** @var \FabrikFEModelForm $formModel */
		$formModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Form',
'FabrikFEModel');
		$formModel->setId($formId);
		$formModel->setRowId($rowId);
		$formModel->origRowId = $rowId;
		$formModel->unsetData();
		$formModel->_origData = self::getOrigData($formId, $rowId);
		self::$app->input->set('rowid', $rowId);
		return $formModel;
	}

	/**
	 * @param $formModel \FabrikFEModelForm
	 *
	 *
	 * @since version
	 */
	private static function getLogPlugin($formModel)
	{
		$pluginManager =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Pluginmanager',
'FabrikFEModel');
		$params = $formModel->getParams();
		$logPlugin = $pluginManager->getPlugin('log',
'form');
		$plugins = $params->get('plugins');

		foreach ($plugins as $c => $type)
		{
			if ($type === 'log')
			{
				$logPlugin->setModel($formModel);
				$logPlugin->setParams($params, $c);
			}
		}

		return $logPlugin;
	}

	private static function getOrigData($formId, $rowId)
	{
		if (array_key_exists($formId, self::$origData))
		{
			if (array_key_exists($rowId, self::$origData[$formId]))
			{
				return self::$origData[$formId][$rowId];
			}
		}

		return null;
	}

	public static function setOrigData($formId, $rowId)
	{
		if (!array_key_exists($formId, self::$origData))
		{
			self::$origData[$formId] = [];
		}

		if (!array_key_exists($rowId, self::$origData[$formId]))
		{
			$formModel = self::getFormModel($formId, $rowId);
			self::$origData[$formId][$rowId] = $formModel->getOrigData();
			//$logPlugin = self::getLogPlugin($formModel);
			//$logPlugin->onBeforeProcess();
		}
	}

	public static function logRowChange($formId, $rowId)
	{
		$formModel = self::getFormModel($formId, $rowId);
		$logPlugin = self::getLogPlugin($formModel);
		$logPlugin->setOrigData(self::getOrigData($formId, $rowId));
		$logPlugin->onAfterProcess();
	}

}
fabrik/fabrik/Helpers/Pagination.php000064400000031522151165341610013452
0ustar00<?php
/**
 * Makes the list navigation html to traverse the list data
 *
 * @package     Joomla
 * @subpackage  Fabrik
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Layout\LayoutInterface;
use Joomla\CMS\Version;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Pagination\PaginationObject;
use Joomla\CMS\Language\Text;
use \stdClass;

/**
 * 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');

class FabrikPaginationObject extends
\Joomla\CMS\Pagination\PaginationObject {
	public $key = null;
}

/**
 * Extension to the normal page-nav functions
 * $total, $limitstart, $limit
 *
 * @package  Fabrik
 * @since    3.0
 */
class Pagination extends \Joomla\CMS\Pagination\Pagination
{
	/**
	 * 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;


	/* dynamic properties to make php8.2 happy */
	public $showNav  = null;
	public $startLimit  = null;
	public $tmpl  = null;
//	public $key  = null;

	/**
	 * 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                  = Factory::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   PaginationObject $item The object with which to make an active
link.
	 *
	 * @return   string  HTML link
	 */
	protected function _item_active($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   PaginationObject $item The item to be processed
	 *
	 * @return  string
	 *
	 * @since   1.5
	 */
	protected function _item_inactive( $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 (File::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
		$items = ['all', 'start', 'previous',
'pages', 'next', 'end'];
		$activeFound = false;
		foreach ($items as $dataitem) {
			if ($dataitem !== 'pages') {
				$activeFound |= $data->$dataitem->active;
				$list[$dataitem]['active'] = $data->$dataitem->active;
				$list[$dataitem]['data'] = $itemOverride ?
fabrik_pagination_item_active($data->$dataitem, $this->listRef) :
$this->_item_active($data->$dataitem);
			} else {
				foreach ($data->$dataitem as $i => $page)
				{
					$activeFound |= $page->active;
					$list['pages'][$i]['active'] = $page->active;
					$list['pages'][$i]['data']   = $itemOverride ?
fabrik_pagination_item_active($page, $this->listRef) :
$this->_item_active($page);
				}
			}
		}
		if (!$activeFound) {
			$list['pages'][$this->limitstart == 0 ? 1 :
$this->limitstart/$this->limit + 1]['active'] = true;
		}

		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, '?') ?
'&amp;' : '?';
		$data->all      = new
FabrikPaginationObject(Text::_('COM_FABRIK_VIEW_ALL'));
		$data->all->key = 'all';

		if (!$this->viewAll)
		{
			$data->all->base = '0';
			$data->all->link = Route::_("{$sepchar}limitstart=");
		}

		// Set the start and previous data objects
		$data->start         = new
FabrikPaginationObject(Text::_('COM_FABRIK_START'));
		$data->start->key    = 'start';
		$data->previous      = new
FabrikPaginationObject(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    = Route::_($this->url .
"{$sepchar}limitstart{$this->id}=0");
			$data->previous->base = $page;
			$data->previous->link = Route::_($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
FabrikPaginationObject(Text::_('COM_FABRIK_NEXT'));
		$data->next->key = 'next';
		$data->end       = new
FabrikPaginationObject(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 = Route::_($this->url .
"{$sepchar}limitstart{$this->id}=" . $next);
			$data->end->base  = $end;
			$data->end->link  = Route::_($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 FabrikPaginationObject($i);
			$data->pages[$i]->key = $i;

			if ($i != $this->get('pages.current') || $this->viewAll)
			{
				$data->pages[$i]->base = $offset;
				$data->pages[$i]->link = Route::_($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)
	{

		if (strpos($property, '.'))
		{
			$prop     = explode('.', $property);
			$prop[1]  = ucfirst($prop[1]);
			$property = implode($prop);
		}

		if (isset($this->$property))
		{
			return $this->$property;
		}

		return $default;

	}

	/**
	 * Get a pagination LayoutInterface 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 . '/' .
Factory::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;
	}
}
fabrik/fabrik/Helpers/Pdf.php000064400000016042151165341610012072
0ustar00<?php
/**
 * PDF Set up helper
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
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 = ComponentHelper::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'     =>
Factory::getApplication()->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 = ComponentHelper::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 = Factory::getApplication()->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:') && !strstr($href, 'tel:'))
				{
					$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 = ComponentHelper::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);
			}
		}
	}
}
fabrik/fabrik/Helpers/Php.php000064400000014063151165341610012111
0ustar00<?php
/**
 * @package         Regular Labs Library
 * @version         22.10.1331
 *
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            http://regularlabs.com
 * @copyright       Copyright © 2022 Regular Labs All Rights Reserved
 * @license         http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 */

/**
 * This is a derivative of the Php class from Peter van Weston at Regular
Labs,
 * it is highly modified for Fabrik's needs but the underlying
 * architecture is copyright Peter van Westen. The following applies to
 * the derivations.
 *
 * @package     Joomla
 * @subpackage  Fabrik
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

namespace Fabrik\Helpers;

use Joomla\CMS\Factory;
use Joomla\CMS\Version;
use Joomla\CMS\Document\Document;
use Joomla\CMS\Filesystem\File;

class Php
{

    public static function Eval($params = [])
    {
        if (empty($params)) {
            return null;
        }

       
        if (array_key_exists('code', $params) === false) {
            /* we must have some code to deal with */
            return null;
        }
        
        $params['className'] = self::getClassName($params);
        if (empty($params['className'])) {
            // Something went wrong!
            return true;
        }
        
        $result = null;
        ob_start();
        $newClass = new $params['className'];
        $result = $newClass->doExecute($params['vars'] ?? [],
$params['thisVars'] ?? []);
        $output = ob_get_contents();
        ob_end_clean();
        
        if (is_null($result) === false) {
            return $result;
        }
        
        return $output;
    }
    
    
    private static function createFunctionInMemory($string) 
    {
        eval($string);        
    }
    
    private static function generateClassContents($params)
     {
        $content = [];
        /* Process the passed in variables */
        $init_variables = [];
        $thisVars = $params['thisVars'] ?? [];
        $vars = $params['vars'] ?? [];

        $privateThisVars = [];
        foreach(array_keys($thisVars) as $thisVarName) {
            $privateThisVars[] = 'private
$'.$thisVarName.';';
        }
        $initBasicVars = [];
        foreach(array_keys($vars) as $varName) {
            $initBasicVars[] = 'private
$'.$varName.';';
        }

        $useLines = [];

        /* Check for complicated code structure */
        $codeTypes = ['preCode' => [], 'postCode'
=> []];
        if (is_array($params['code']) === false)
$params['code'] = ['preCode' =>
$params['code']];
        /* Split the code into seperate lines */
        foreach (array_keys($codeTypes) as $codeType) {
            if (empty($params['code'][$codeType])) continue;
            $codeTypes[$codeType] = array_map('trim',
preg_split('/\r\n|\r|\n/',
$params['code'][$codeType]));
            /* Capture any use statements */
            foreach ($codeTypes[$codeType] as $idx => $codeLine) {
                if (strpos($codeLine, 'use ') === 0) {
                    /* Found one, while we are at it remove any double
backslashes, these were recommended early on with F4 */
                    $useLines[] = str_replace('\\\\',
'\\', $codeLine);
                    unset($codeTypes[$codeType][$idx]);
                } 
            }
        }

        /* Opening stuff  */
        $content[] = 'defined(\'_JEXEC\') or die;';

        /* the use lines from the original source */  
        $content = array_merge($content, $useLines);

        /* Define the class */
        $content[] = 'class
'.$params['className'].'{';

         /* Our new function */
        $content[] = 'function doExecute($vars, $thisVars) {'; 

        /* Insert any $thisVars setup */    
        if (count($thisVars)) {
            $content = array_merge($content, [
                'foreach ($thisVars as $thisVarKey =>
&$thisVarValue) {',
                '   $this->{$thisVarKey} =
&$thisVarValue;',
                '};'
            ]);
        } 

        /* Insert any regular var setup */    
        if (count($vars)) {
            $content = array_merge($content, [
                'foreach ($vars as $varKey => &$varValue)
{',
                '   ${$varKey} = &$varValue;',
                '};'
            ]);
        }

        /* Now the actual code */
        $content = array_merge($content, $codeTypes['preCode']);
        if (!empty($params['code']['file'])) {
            $content[] =
'require_once("'.$params['code']['file'].'");';
        }
        $content = array_merge($content, $codeTypes['postCode']);
        /* In case the code left us out of php mode */
        $content[] = '?><?php';    
        
        /* Close the doExecute function */                                 
                          
        $content[] = '}';  
        /* And close off the class */
        $content[] = "};";                                       
         

        $content = implode(PHP_EOL, $content);
        
        // Remove Zero Width spaces / (non-)joiners
        $content = str_replace(
            [
                "\xE2\x80\x8B",
                "\xE2\x80\x8C",
                "\xE2\x80\x8D",
            ],
            '',
            $content
        );
        
        return $content;
    }
    
    private static function getClassName(&$params) 
    {
        $code = '';
        if (is_array($params['code'])) {
            foreach ($params['code'] as $codeKey => $codePart)
{
                $code .= $params['code'][$codeKey] ??
'';
            }
            $md5 = md5($code);
        } else {
            $md5 = md5($params['code']);
        }
        $params['className'] = 'FabrikEvalClass_' .
$md5;
        
        if (class_exists($params['className'])) {
            return $params['className'];
        }

        $contents = self::generateClassContents($params);
        self::createFunctionInMemory($contents);
        
        if (!class_exists($params['className'])) {
            // Something went wrong!
            return false;
        }
        
        return $params['className'];
    }
}
fabrik/fabrik/Helpers/sms_gateways/clickatell.php000064400000003672151165341620016204
0ustar00<?php
/**
 * Clickatell SMS gateway class
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;
use Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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)
		{
			Factory::getApplication()->enqueueMessage($e->getMessage(),
'error');

			return false;
		}

		// check the response array
		foreach ($response as $item)
		{
			if ($item->error !== false)
			{
				// @TODO add language for this
				Factory::getApplication()->enqueueMessage('SMS failed with
error code: ' . $item->errorCode, 'error');

				return false;
			}
		}

		return true;
	}
}
fabrik/fabrik/Helpers/sms_gateways/itagg.php000064400000004416151165341620015165
0ustar00<?php
/**
 * Itagg SMS gateway class
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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);
		}
	}
}
fabrik/fabrik/Helpers/sms_gateways/kapow.php000064400000002453151165341620015212
0ustar00<?php
/**
 * Send an SMS via the kapow sms gateway
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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, '');
		}
	}
}
fabrik/fabrik/Helpers/sms_gateways/smssza.php000064400000002466151165341620015415
0ustar00<?php
/**
 * Send an SMS via the SMSS (ZA) gateway
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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;
	}
}
fabrik/fabrik/Helpers/sms_gateways/textopoly.php000064400000002471151165341620016140
0ustar00<?php
/**
 * Textopoly SMS gateway class
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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, '');
		}
	}
}
fabrik/fabrik/Helpers/sms_gateways/twilio.php000064400000002742151165341620015401
0ustar00<?php
/**
 * Twilio SMS gateway class
 *
 * @package     Joomla.Plugin
 * @subpackage  Fabrik.form.sms
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;
use Joomla\CMS\Object\CMSObject;
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 CMSObject
{
	/**
	 * 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)
			{
				Factory::getApplication()->enqueueMessage($e->getMessage(),
'error');

				return false;
			}
		}

		return true;
	}
}
fabrik/fabrik/Helpers/Sms.php000064400000003451151165341620012124
0ustar00<?php
/**
 * Send sms's
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;
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 = Factory::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);
		}
	}
}
fabrik/fabrik/Helpers/StringHelper.php000064400000064116151165341630013776
0ustar00<?php
/**
 * String helpers
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Language\Text;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Factory;
use PHPLicengine\Api\Api;
use PHPLicengine\Service\Bitlink;
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((string)$col, '.'))
		{
			$bits = explode('.', $col);
			$col = array_pop($bits);
		}
		elseif (strstr((string)$col, '___'))
		{
			$bits = explode('___', $col);
			$col = array_pop($bits);
		}

		$col = str_replace('`', '', (string)$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 = InputFilter::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, '&amp;') ? '&amp;' :
'&';
		$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 Text 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)
	{
		if (empty($text)) return $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 = Factory::getContainer()->get('DatabaseDriver');
				$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 = Factory::getContainer()->get('DatabaseDriver');
				$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)
	{
	    // when called in form context, could be a single value array
	    if (is_array($value))
        {
            $value = empty($value) ? '' : reset($value);
        }

		$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 - NOT USED
	 * @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 !==
'')
		{
			$api = new Api($apikey);
			$bitlink = new Bitlink($api);
			$result = $bitlink->createBitlink(['long_url' =>
$link]);

			if ($api->isCurlError())
			{
				Worker::log('fabrik.helpers.bitlify.error',$api->getCurlErrno().':
'.$api->getCurlError());
			}
			else
			{
				if ($result->isError())
				{
					Worker::log('fabrik.helpers.bitlify.error',
print($result->getResponse()));
				}
				else
				{
					if ($result->isSuccess())
					{
						$link = $result->getResponseObject()->link;
					}
					else
					{
						Worker::log('fabrik.helpers.bitlify.error',
print($result->getResponse()));
					}
				}
			}
		}

		return $link;
	}
}
fabrik/fabrik/Helpers/Stripe.php000064400000002437151165341630012634
0ustar00<?php
/**
 * PDF Set up helper
 *
 * @package     Joomla
 * @subpackage  Fabrik.helpers
 * @copyright   Copyright (C) 2005-2020  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;
	}
}
fabrik/fabrik/Helpers/Uploader.php000064400000015506151165341630013142
0ustar00<?php
/**
 * Fabrik upload helper
 *
 * @package     Joomla
 * @subpackage  Fabrik
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\Factory;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Joomla\Registry\Registry;
use RuntimeException;

/**
 * Fabrik upload helper
 *
 * @package     Joomla
 * @subpackage  Fabrik
 * @since       3.0
 */
class Uploader extends CMSObject
{
	/**
	 * 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 (!Folder::exists($folderPath))
		{
			if (!Folder::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 we're AJAX uploading and WiP is set, don't check
is_uploaded_file, because it won't be,
		 * we uploaded it direct from the form through AJAX to our own tmp
location, now we're just moving it
		 */
		if (!($params->get('ajax_upload', '0') ===
'1' && $params->get('upload_use_wip',
'0') === '1') &&
!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(File::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 = Factory::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;
	}
}
fabrik/fabrik/Helpers/Worker.php000064400000230161151165341630012634
0ustar00<?php
/**
 * Generic tools that all models use
 *
 * @package     Joomla
 * @subpackage  Fabrik
 * @copyright   Copyright (C) 2005-2020  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 Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Access\Access;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Version;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Factory;
use Joomla\CMS\Mail\Mail;
use Joomla\CMS\Mail\MailHelper;
use Joomla\CMS\Crypt\Crypt;
use Joomla\CMS\Crypt\Key;
use Joomla\CMS\Crypt\Cipher\SimpleCipher;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Path;
use Joomla\Database\DatabaseDriver;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Language\Text;
use Fabrik\Helpers\FCipher;
use FabTable;
use Joomla\CMS\Application\CMSApplication;
use Fabrik\Helpers\Php;

/**
 * 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    = Factory::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    = Factory::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    = Factory::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                 = InputFilter::getInstance();
				$post              = $f->clean($_REQUEST, 'array');
				
				//J!4 & SEF: $_REQUEST is empty, take also inputVars
                $app = Factory::getApplication();
                $inputVars = $app->getInput()->getArray();
                $inputVars = $f->clean($inputVars,'string');
				$searchData = is_null($searchData) ? $inputVars :
array_merge($inputVars, $searchData);
				
				$this->_searchData = is_null($searchData) ? $post :
array_merge($post, $searchData);

				// Enable users to use placeholder to insert session token
				$this->_searchData['Session::getFormToken'] =
Session::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]+|<\?php.*\?>)*)}/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       = InputFilter::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 = Factory::getApplication();

		if (is_null($user))
		{
			$user = Factory::getUser();
		}

		if ($user->id == 0) return $msg;

		$user->levels = $user->getAuthorisedViewLevels();

		if (is_object($user))
		{
			foreach ($user as $key => $val)
			{
				if (substr($key, 0, 1) != '_' && !empty($val))
				{
					if (!is_object($val) && !is_array($val))
					{
						$msg = str_replace('{$' . $prefix . '->' .
$key . '}', $val, $msg);
						$msg = str_replace('{$' . $prefix . '-&gt;' .
$key . '}', $val, $msg);
					}
					elseif (is_array($val))
					{
						$msg = str_replace('{$' . $prefix . '->' .
$key . '}', implode(',', $val), $msg);
						$msg = str_replace('{$' . $prefix . '-&gt;' .
$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 = Factory::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)
		{
			if (!empty($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   = Factory::getSession();
			$sessionData = array(
				'id' => $session->getId(),
				'token' => $session->get('session.token'),
				'formtoken' => Session::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 = Factory::getApplication()->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       = Factory::getApplication();
		$itemId    = self::itemId();
		$config    = Factory::getApplication()->getConfig();
		$session   = Factory::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-&gt;' . $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   = Factory::getApplication()->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 = Factory::getApplication()->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>')))
			{
			    // experiment with eval'ed code in defaults
                if (strstr($bits[1], '<?php'))
                {
                    $code =
preg_replace('/^<\?php(.*)(\?>)$/s', '$1',
$bits[1]);
					FabrikWorker::clearEval();
					$bits[1] = Php::Eval(['code' => $code]);
					FabrikWorker::logEval($default, 'Caught exception on eval of
' . $formModel->label . ': %s');
                }

				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 (!empty($match) && $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[] = HTMLHelper::_('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 ?
HTMLHelper::_('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   = Path::clean($path . '/' . $file);
			$isDir = is_dir($dir);

			if ($file != "." && $file != "..")
			{
				if (preg_match("/$filter/", $file))
				{
					if (($isDir && $foldersOnly) || !$foldersOnly)
					{
						if ($fullPath)
						{
							$arr[] = trim(Path::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 = Factory::getApplication()->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 (Multilanguage::isEnabled())
		{
			$lang      = Factory::getApplication()->getLanguage()->getTag();
			$languages = LanguageHelper::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  = ComponentHelper::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 = ComponentHelper::getParams('com_content');
			$filters       = $contentParams->get('filters');
		}

		$user       = Factory::getUser();
		$userGroups = Access::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' || $filterType == 'CBL')
				{
					$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 = InputFilter::getInstance($tags,
array_diff($blackListAttributes, $whiteListAttributes), 1, 1);
			}
			// White lists take third precedence.
			elseif ($whiteList)
			{
				// Turn off xss auto clean
				$filter = InputFilter::getInstance($whiteListTags,
$whiteListAttributes, 0, 0, 0);
			}
			// No HTML takes last place.
			else
			{
				$filter = InputFilter::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.
		 */
		if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
			error_clear_last();
		}
		else
		{
			@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/>&nbsp;&nbsp;&nbsp;&nbsp;Debug:&nbsp;';
		$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 = Factory::getApplication();
			$app->enqueueMessage($errString, $msgType);
		}
		else
		{
			switch ($msgType)
			{
				case 'message':
					$priority = Log::INFO;
					break;
				case 'warning':
					$priority = Log::WARNING;
					break;
				case 'error':
				default:
					$priority = Log::ERROR;
					break;
			}

			Log::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 "fabrik"
	 *                              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))
		{
			Table::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_fabrik/tables');
			$conf = Factory::getApplication()->getConfig();

			if (!$loadJoomlaDb)
			{
				$cnModel  =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('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
			$options = array('driver' => $driver, 'host'
=> $host, 'user' => $user, 'password' =>
$password, 'database' => $database,
				'prefix' => $dbPrefix);

			self::$database[$sig] = DatabaseDriver::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 = ComponentHelper::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   = Factory::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 =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('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 =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('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)
	{
		if (is_string($d)) $d = trim($d);
		$db         = self::getDbo(true);
		$aNullDates = array('0000-00-000000-00-00', '0000-00-00
00:00:00', '0000-00-00', '', 0,
$db->getNullDate(), null);

		if ($d < '1000-01-01') return true;

		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 (empty($d) || strlen($d) < 2)
		{
			return false;
		}

		if ($notNull && self::isNullDate($d))
		{
			return false;
		}

		$cerl = error_reporting ();
		error_reporting (0);

		try
		{
			$dt = new Date($d);
		} catch (\Exception $e)
		{
			error_reporting ($cerl);
			self::clearEval();
			return false;
		}

		error_reporting ($cerl);
		return true;
	}


	public static function addMonthsInterval($months, Date $date)
	{
		$next = new Date($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, Date $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 Date("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 Date("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 = Factory::getUser();
		}
		else
		{
			$user = Factory::getUser($userId);
		}
		$config = Factory::getApplication()->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
MailHelper::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   = Factory::getApplication()->getConfig();
		$mailer = $conf->get('mailer');

		if ($mailer === 'mail')
		{
			// Sendmail and Joomla isEmailAddress don't use the same conditions
			return (MailHelper::isEmailAddress($email) &&
Mail::ValidateAddress($email));
		}

		return MailHelper::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 Mail instance
		$mailer = Factory::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)
		{
            self::log('fabrik.helper.sendmail.error',
'Exception in addRecipient: ' . $e->getMessage(), false);

            return false;
		}

		try
		{
			$mailer->addCc($cc);
		}
		catch (\Exception $e)
		{
            self::log('fabrik.helper.sendmail.warning',
'Exception in addCc: ' . $e->getMessage());

			// not sure if we should bail if Cc is bad, for now just soldier on
		}

		try
		{
			$mailer->addBcc($bcc);
		}
		catch (\Exception $e)
		{
            self::log('fabrik.helper.sendmail.warning',
'Exception in addBcc: ' . $e->getMessage(), false);

            // 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)
			{
                self::log('fabrik.helper.sendmail.warning',
'Exception in addAttachment: ' . $e->getMessage(), false);

                // 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)
				{
                    self::log('fabrik.helper.sendmail.warning',
'Exception in addReplyTo: ' . $e->getMessage(), false);

                    // carry on
				}
			}
		}
		elseif (isset($replyTo))
		{
			try
			{
				$mailer->addReplyTo($replyTo, $replyToName);
			}
			catch (\Exception $e)
			{
                self::log('fabrik.helper.sendmail.warning',
'Exception in addReplyTo: ' . $e->getMessage(), false);

                // carry on
			}
		}
		else
		{
			$autoReplyTo = true;
		}

		try
		{
			$mailer->setSender(array($from, $fromName, $autoReplyTo));
		}
		catch (\Exception $e)
		{
            self::log('fabrik.helper.sendmail.error',
'Exception in setSender: ' . $e->getMessage(), false);

            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 = MailHelper::cleanText(strip_tags($body));
		}

		foreach ($headers as $headerName => $headerValue) {
			$mailer->addCustomHeader($headerName, $headerValue);
		}

		$config = ComponentHelper::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)
		{
            self::log('fabrik.helper.sendmail.error',
'Exception in Send: ' . $e->getMessage(), false);

            return false;
		}

        // if ACY mailing is installed, it returns an exception (!) rather
than false
        if (is_bool($ret) === false && get_parent_class($ret) ===
'Exception')
        {
            self::log('fabrik.helper.sendmail.error',
'Exception in Send: ' . $ret->getMessage(), false);

            $ret = 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 = Uri::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 = Factory::getApplication();

		if (!$app->isClient('administrator'))
		{
			// Attempt to get Itemid from possible list menu item.
			if (!is_null($listId))
			{
				if (!array_key_exists($listId, $listIds))
				{
					$db         =
Factory::getContainer()->get('DatabaseDriver');
					$myLanguage = Factory::getApplication()->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   = Factory::getApplication();
        $input = $app->input;

        if ($priority === 'menu')
        {

            $val = $input->get($name, $val, 'string');

            if (!$app->isClient('administrator'))
            {
                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->getParams()->get($name,
$val);
                        }
                    }
                }
            }
        }
        else
        {
            if (!$app->isClient('administrator'))
            {
                $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->getParams()->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     = Factory::getApplication();
			$input   = $app->input;
			$user    = Factory::getUser();
			$userCol = $params->get($col, '');

			if ($userCol != '')
			{
				$userCol = StringHelper::safeColNameToArrayKey($userCol);

				if ((is_array($row) && !array_key_exists($userCol, $row)) ||
(is_object($row) && !isset($row->{$userCol})))
				{
					return false;
				}
				else
				{
					$userColRaw = $userCol . '_raw';

					if ((is_array($row) && array_key_exists($userColRaw, $row)) ||
(is_object($row) && property_exists($row,$userColRaw)))
					{
						$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 = ComponentHelper::getParams('com_fabrik');

		$pdfLibrary = $config->get('fabrik_pdf_lib',
'dompdf');

		if ($pdfLibrary === 'dompdf')
		{
			$file = COM_FABRIK_LIBRARY .
'/vendor/vendor/dompdf/dompdf/composer.json';
		}
		else
		{
			$file = COM_FABRIK_LIBRARY .
'/vendor/vendor/mpdf/mpdf/composer.json';
		}

		if (!File::exists($file))
		{
			if ($puke)
			{
				throw new \RuntimeException(Text::_($pdfLibrary === 'dompdf'
? 'COM_FABRIK_NOTICE_DOMPDF_NOT_FOUND' :
'COM_FABRIK_NOTICE_MPDF_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  Cache
	 */
	public static function getCache($listModel = null, $group = null, $ttl =
null)
	{
		$config  = Factory::getApplication()->getConfig();
		$app     = Factory::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   = Cache::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  Cache
	 */
	public static function useCache($listModel = null, $noGuest = true,
$excludedFormats = null)
	{
		$config  = Factory::getApplication()->getConfig();
		$app = Factory::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 = ComponentHelper::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 (Factory::getUser()->get('id') === '0')
				{
					return false;
				}
			}

			if (in_array($app->input->get('format'),
$excludedFormats))
			{
				return false;
			}
		}

		return $doCache;
	}

	/**
	 * Get the default values for a given Form
	 *
	 * @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)
	{
		Form::addFormPath(JPATH_COMPONENT . '/models/forms');
		Form::addFieldPath(JPATH_COMPONENT . '/models/fields');
		$form = Form::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()
	{
		// do we need this function any more?
		return true;
	}
*/
	/**
	 * Are we in a form process task
	 *
	 * @since 3.2
	 *
	 * @return bool
	 */
	public static function inFormProcess()
	{
		$app = Factory::getApplication();

		return $app->input->get('task') ==
'form.process' || ($app->isClient('administrator')
&& $app->input->get('task') ==
'process');
	}

	/**
	 * Are we in an AJAX validation process task
	 *
	 * @since 3.9.2
	 *
	 * @return bool
	 */
	public static function inAJAXValidation()
	{
		$app = Factory::getApplication();

		return $app->input->get('task', '') ===
'form.ajax_validate';
	}


	/**
	 * 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 (File::exists(COM_FABRIK_LIBRARY .
'/libs/libs/getid3/getid3/getid3.php'))
		{
			ini_set('display_errors', true);
			require_once COM_FABRIK_LIBRARY .
'/libs/libs/getid3/getid3/getid3.php';
			require_once COM_FABRIK_LIBRARY .
'/libs/libs/getid3/getid3/getid3.lib.php';

			\getid3_lib::IncludeDependency(COM_FABRIK_LIBRARY .
'/libs/libs/getid3/getid3/extension.cache.mysqli.php', __FILE__,
true);
			$config   = Factory::getApplication()->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;
		}

		if ($memory === '-1')
		{
			return PHP_INT_MAX;
		}

		$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;
	}
}
fabrik/include.php000064400000015540151165341640010151 0ustar00<?php
/**
 * Fabrik Autoloader Class
 *
 * @package     Fabrik
 * @copyright   Copyright (C) 2014 fabrikar.com - All rights reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 */

// No direct access
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\String\Inflector;
use Joomla\String\Normalise;

/**'
 * Autoloader Class
 *
 * @package  Fabble
 * @since    1.0
 */
class FabrikAutoloader
{
	public function __construct()
	{
		spl_autoload_register(array($this, 'controller'));
		spl_autoload_register(array($this, 'helper'));
		spl_autoload_register(array($this, 'document'));
		spl_autoload_register(array($this, 'view'));

		// @TODO - at some point allow auto-loading of these as per Fabble
		/*
		spl_autoload_register(array($this, 'model'));
		spl_autoload_register(array($this, 'view'));
		spl_autoload_register(array($this, 'library'));
		spl_autoload_register(array($this, 'plugin'));*/
	}

	/**
	 * Load plugin class
	 *
	 * @param   string $class Class name
	 */
	private function plugin($class)
	{

		if (!strstr(strtolower($class), 'fabble\form\plugin\\')
&& !strstr(strtolower($class), 'fabble\lizt\plugin\\'))
		{
			return;
		}

		$class = str_replace('\\', '/',
str_replace('Fabble\\', '', $class));
		$file  = explode('/', $class);
		$file  = array_pop($file);
		$path  = JPATH_SITE . '/libraries/fabble/' . $class .
'/' . $file . '.php';

		require_once $path;
	}

	/**
	 * Load model class
	 *
	 * @param   string $class Class name
	 */
	private function model($class)
	{
		if (!strstr(strtolower($class), 'model'))
		{
			return;
		}

		$kls      = explode('\\', $class);
		$class    = array_pop($kls);
		$scope    = Factory::getApplication()->scope;
		$isFabble = strtolower(substr($class, 0, 11)) ===
'fabblemodel';

		if ($this->appName($class) === $scope || $isFabble)
		{
			$path        = JPATH_SITE . '/libraries/fabble/';
			$defaultPath = JPATH_SITE . '/libraries/fabble/';
			$plural      = Inflector::getInstance();
			$parts       = Normalise::fromCamelCase($class, true);
			unset($parts[0]);
			$parts = array_values($parts);

			foreach ($parts as &$part)
			{
				$part = strtolower($part);

				if ($plural->isPlural($part))
				{
					$part = $plural->toSingular($part);
				}

				$part = StringHelper::ucfirst(strtolower($part));
			}

			$path .= implode('/', $parts) . '.php';

			if (file_exists($path))
			{
				require_once $path;
				$type = array_pop($parts);

				if (!$isFabble)
				{
					class_alias('\\Fabble\\Model\\FabbleModel' .
StringHelper::ucfirst($type), $class);
				}

				return;
			}

			// IF no actual model name found try loading default model
			$parts[count($parts) - 1] = 'Default';
			$defaultPath .= implode('/', $parts) . '.php';

			if (file_exists($defaultPath))
			{
				require_once $defaultPath;
				$type = array_pop($parts);
				class_alias("\\Fabble\\Model\\FabbleModel" .
StringHelper::ucfirst($type), $class);

				return;
			}
		}
	}

	/**
	 * Load view class
	 *
	 * @param   string $class Class name
	 */
	private function view($class)
	{
		/*
		if (!strstr(strtolower($class), 'view'))
		{
			return;
		}

		$scope = Factory::getApplication()->scope;

		// Load component specific files
		if ($this->appName($class) === $scope)
		{
			$parts    = Normalise::fromCamelCase($class, true);
			$type     = array_pop($parts);
			$path     = JPATH_SITE . '/libraries/fabble/Views/' .
StringHelper::ucfirst($type) . '.php';
			$original = $type;

			if (file_exists($path))
			{
				require_once $path;
				class_alias('\\Fabble\\Views\\' . $original, $class);

				return;
			}
		}
		*/

		if ($class !== 'FabrikView')
		{
			return;
		}

		$path = JPATH_SITE .
'/components/com_fabrik/views/FabrikView.php';

		if (file_exists($path))
		{
			require_once $path;
		}

	}

	private function appName($class)
	{
		$scope = Factory::getApplication()->scope;

		return 'com_' . strtolower(substr($class, 0, strlen($scope) -
4));
	}

	/**
	 * Load controller file
	 *
	 * @param   string $class Class name
	 */
	private function controller($class)
	{
		if (!str_starts_with($class, 'Fabrik') || strpos($class,
'\\') !== false)
		{
			return;
		}

		$parts = preg_split('/(?<=\\w)(?=[A-Z])/', $class);
		if ($parts[1] == 'Admin') {
			if ($parts[2] != 'Controller' || array_key_exists(3, $parts)
=== false) {
				return;
			}
			$jpath = JPATH_ADMINISTRATOR;
			$file = strtolower($parts[3]);
		} else {
			if ($parts[1] != 'Controller') {
				return;
			}
			$jpath = JPATH_SITE;
			$file = strtolower($parts[2]);
		}

		if (Factory::getApplication()->input->get('format') ==
'raw') {
			$file .= '.raw';
		}
		
		$path  = $jpath . '/components/com_fabrik/controllers/' . $file
. '.php';

		if (file_exists($path))
		{
			require_once $path;
		}

	}

	/**
	 * Load library files, and possible helpers
	 *
	 * @param   string $class Class Name
	 */
	private function library($class)
	{
		if (strstr($class, '\\'))
		{
			return;
		}

		if (strtolower(substr($class, 0, 3)) === 'fab')
		{
			$class = (substr($class, 3));

			// Change from camel cased (e.g. ViewHtml) into a lowercase array (e.g.
'view','html') taken from FOF
			$class = preg_replace('/(\s)+/', '_', $class);
			$class = strtolower(preg_replace('/(?<=\\w)([A-Z])/',
'_\\1', $class));
			$class = explode('_', $class);

			$file      = (count($class) === 1) ? $class[0] : array_pop($class);
			$path      = JPATH_SITE . '/libraries/fabble/' .
implode('/', $class);
			$classFile = $path . '/' . $file . '.php';
			$helper    = $path . '/helper.php';

			if (file_exists($classFile))
			{
				include_once $classFile;
			}

			if (file_exists($helper))
			{
				include_once $helper;
			}
		}
	}

	/**
	 * Load helper file
	 **/
	private function helper($class)
	{
		if (!strstr($class, 'Fabrik\Helpers'))
		{
			return;
		}

		$className = str_replace('\\', '/', $class);
		//$file  = explode('/', $class);
		//$file  = strtolower(array_pop($file));
		$path = preg_replace('#Fabrik\/Helpers\/#',
'/libraries/fabrik/fabrik/fabrik/Helpers/', $className);
		$path  = JPATH_SITE . $path . '.php';

		if (file_exists($path))
		{
			require_once $path;
			if (is_callable(array($class, '__initStatic')))
			{
				$class::__initStatic();
			}
		}
	}

	/**
	 * Load document file
	 **/
	private function document($class)
	{
		if (!strstr($class, 'Fabrik\Document'))
		{
			return;
		}

		$class = str_replace('\\', '/', $class);
		//$file  = explode('/', $class);
		//$file  = strtolower(array_pop($file));
		$path = preg_replace('#Fabrik\/Document\/#',
'/libraries/fabrik/fabrik/fabrik/Document/', $class);
		$path  = JPATH_SITE . $path . '.php';

		if (file_exists($path))
		{
			require_once $path;
		}
	}
}

/*
 * If the Fabrik library package has been installed, or we have full github
code, we can use Composer autoload
 */
if (file_exists(JPATH_LIBRARIES .
'/fabrik/vendor/vendor/autoload.php'))
{
	$loader = require JPATH_LIBRARIES .
'/fabrik/vendor/vendor/autoload.php';
}

$autoLoader = new FabrikAutoloader();
fabrik/fabrik.xml000064400000001140151165341640007764 0ustar00<?xml
version="1.0" encoding="UTF-8"?>
<extension type="library" method="upgrade"
version="4">
  <name>Fabrik library</name>
  <libraryname>fabrik/fabrik</libraryname>
  <author>Fabrikar</author>
  <creationDate>2024-06-26</creationDate>
  <copyright>Copyright (C) 2005-2024 Fabrikar - All rights
reserved.</copyright>
  <license>GNU General Public License version 3 or later; see
software_license.txt</license>
  <version>4.2</version>
  <description>Fabrik - Library files</description>
  <files>
    <folder>fabrik</folder>
    <file>include.php</file>
    <file>fabrik.xml</file>
  </files>
</extension>