Your IP : 216.73.216.190


Current Path : /proc/1908984/root/proc/2603263/cwd/
Upload File :
Current File : //proc/1908984/root/proc/2603263/cwd/Layout.tar

BaseLayout.php000064400000012516152344344400007335 0ustar00<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\CMS\Layout;

defined('JPATH_PLATFORM') or die;

use Joomla\Registry\Registry;

/**
 * Base class for rendering a display layout
 *
 * @link   https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout
 * @since  3.0
 */
class BaseLayout implements LayoutInterface
{
	/**
	 * Options object
	 *
	 * @var    Registry
	 * @since  3.2
	 */
	protected $options = null;

	/**
	 * Data for the layout
	 *
	 * @var    array
	 * @since  3.5
	 */
	protected $data = array();

	/**
	 * Debug information messages
	 *
	 * @var    array
	 * @since  3.2
	 */
	protected $debugMessages = array();

	/**
	 * Set the options
	 *
	 * @param   array|Registry  $options  Array / Registry object with the options to load
	 *
	 * @return  BaseLayout  Instance of $this to allow chaining.
	 *
	 * @since   3.2
	 */
	public function setOptions($options = null)
	{
		// Received Registry
		if ($options instanceof Registry)
		{
			$this->options = $options;
		}
		// Received array
		elseif (is_array($options))
		{
			$this->options = new Registry($options);
		}
		else
		{
			$this->options = new Registry;
		}

		return $this;
	}

	/**
	 * Get the options
	 *
	 * @return  Registry  Object with the options
	 *
	 * @since   3.2
	 */
	public function getOptions()
	{
		// Always return a Registry instance
		if (!($this->options instanceof Registry))
		{
			$this->resetOptions();
		}

		return $this->options;
	}

	/**
	 * Function to empty all the options
	 *
	 * @return  BaseLayout  Instance of $this to allow chaining.
	 *
	 * @since   3.2
	 */
	public function resetOptions()
	{
		return $this->setOptions(null);
	}

	/**
	 * Method to escape output.
	 *
	 * @param   string  $output  The output to escape.
	 *
	 * @return  string  The escaped output.
	 *
	 * @note the ENT_COMPAT flag will be replaced by ENT_QUOTES in Joomla 4.0 to also escape single quotes
	 *
	 * @since   3.0
	 */
	public function escape($output)
	{
		return htmlspecialchars($output, ENT_COMPAT, 'UTF-8');
	}

	/**
	 * Get the debug messages array
	 *
	 * @return  array
	 *
	 * @since   3.2
	 */
	public function getDebugMessages()
	{
		return $this->debugMessages;
	}

	/**
	 * Method to render the layout.
	 *
	 * @param   array  $displayData  Array of properties available for use inside the layout file to build the displayed output
	 *
	 * @return  string  The necessary HTML to display the layout
	 *
	 * @since   3.0
	 */
	public function render($displayData)
	{
		// Automatically merge any previously data set if $displayData is an array
		if (is_array($displayData))
		{
			$displayData = array_merge($this->data, $displayData);
		}

		return '';
	}

	/**
	 * Render the list of debug messages
	 *
	 * @return  string  Output text/HTML code
	 *
	 * @since   3.2
	 */
	public function renderDebugMessages()
	{
		return implode("\n", $this->debugMessages);
	}

	/**
	 * Add a debug message to the debug messages array
	 *
	 * @param   string  $message  Message to save
	 *
	 * @return  self
	 *
	 * @since   3.2
	 */
	public function addDebugMessage($message)
	{
		$this->debugMessages[] = $message;

		return $this;
	}

	/**
	 * Clear the debug messages array
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function clearDebugMessages()
	{
		$this->debugMessages = array();

		return $this;
	}

	/**
	 * Render a layout with debug info
	 *
	 * @param   mixed  $data  Data passed to the layout
	 *
	 * @return  string
	 *
	 * @since    3.5
	 */
	public function debug($data = array())
	{
		$this->setDebug(true);

		$output = $this->render($data);

		$this->setDebug(false);

		return $output;
	}

	/**
	 * Method to get the value from the data array
	 *
	 * @param   string  $key           Key to search for in the data array
	 * @param   mixed   $defaultValue  Default value to return if the key is not set
	 *
	 * @return  mixed   Value from the data array | defaultValue if doesn't exist
	 *
	 * @since   3.5
	 */
	public function get($key, $defaultValue = null)
	{
		return isset($this->data[$key]) ? $this->data[$key] : $defaultValue;
	}

	/**
	 * Get the data being rendered
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	public function getData()
	{
		return $this->data;
	}

	/**
	 * Check if debug mode is enabled
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	public function isDebugEnabled()
	{
		return $this->getOptions()->get('debug', false) === true;
	}

	/**
	 * Method to set a value in the data array. Example: $layout->set('items', $items);
	 *
	 * @param   string  $key    Key for the data array
	 * @param   mixed   $value  Value to assign to the key
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function set($key, $value)
	{
		$this->data[(string) $key] = $value;

		return $this;
	}

	/**
	 * Set the the data passed the layout
	 *
	 * @param   array  $data  Array with the data for the layout
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function setData(array $data)
	{
		$this->data = $data;

		return $this;
	}

	/**
	 * Change the debug mode
	 *
	 * @param   boolean  $debug  Enable / Disable debug
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function setDebug($debug)
	{
		$this->options->set('debug', (boolean) $debug);

		return $this;
	}
}
FileLayout.php000064400000033277152344344400007351 0ustar00<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\CMS\Layout;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;

/**
 * Base class for rendering a display layout
 * loaded from from a layout file
 *
 * @link   https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout
 * @since  3.0
 */
class FileLayout extends BaseLayout
{
	/**
	 * Cached layout paths
	 *
	 * @var    array
	 * @since  3.5
	 */
	protected static $cache = array();

	/**
	 * Dot separated path to the layout file, relative to base path
	 *
	 * @var    string
	 * @since  3.0
	 */
	protected $layoutId = '';

	/**
	 * Base path to use when loading layout files
	 *
	 * @var    string
	 * @since  3.0
	 */
	protected $basePath = null;

	/**
	 * Full path to actual layout files, after possible template override check
	 *
	 * @var    string
	 * @since  3.0.3
	 */
	protected $fullPath = null;

	/**
	 * Paths to search for layouts
	 *
	 * @var    array
	 * @since  3.2
	 */
	protected $includePaths = array();

	/**
	 * Method to instantiate the file-based layout.
	 *
	 * @param   string  $layoutId  Dot separated path to the layout file, relative to base path
	 * @param   string  $basePath  Base path to use when loading layout files
	 * @param   mixed   $options   Optional custom options to load. Registry or array format [@since 3.2]
	 *
	 * @since   3.0
	 */
	public function __construct($layoutId, $basePath = null, $options = null)
	{
		// Initialise / Load options
		$this->setOptions($options);

		// Main properties
		$this->setLayout($layoutId);
		$this->basePath = $basePath;

		// Init Environment
		$this->setComponent($this->options->get('component', 'auto'));
		$this->setClient($this->options->get('client', 'auto'));
	}

	/**
	 * Method to render the layout.
	 *
	 * @param   array  $displayData  Array of properties available for use inside the layout file to build the displayed output
	 *
	 * @return  string  The necessary HTML to display the layout
	 *
	 * @since   3.0
	 */
	public function render($displayData = array())
	{
		$this->clearDebugMessages();

		// Inherit base output from parent class
		$layoutOutput = '';

		// Automatically merge any previously data set if $displayData is an array
		if (is_array($displayData))
		{
			$displayData = array_merge($this->data, $displayData);
		}

		// Check possible overrides, and build the full path to layout file
		$path = $this->getPath();

		if ($this->isDebugEnabled())
		{
			echo '<pre>' . $this->renderDebugMessages() . '</pre>';
		}

		// Nothing to show
		if (empty($path))
		{
			return $layoutOutput;
		}

		ob_start();
		include $path;
		$layoutOutput .= ob_get_contents();
		ob_end_clean();

		return $layoutOutput;
	}

	/**
	 * 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 (!empty(static::$cache[$layoutId][$hash]))
		{
			$this->addDebugMessage('<strong>Cached path:</strong> ' . static::$cache[$layoutId][$hash]);

			return static::$cache[$layoutId][$hash];
		}

		$this->addDebugMessage('<strong>Include Paths:</strong> ' . print_r($includePaths, true));

		// Search for suffixed versions. Example: tags.j31.php
		if ($suffixes)
		{
			$this->addDebugMessage('<strong>Suffixes:</strong> ' . print_r($suffixes, true));

			foreach ($suffixes as $suffix)
			{
				$rawPath  = str_replace('.', '/', $this->layoutId) . '.' . $suffix . '.php';
				$this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath);

				if ($foundLayout = \JPath::find($this->includePaths, $rawPath))
				{
					$this->addDebugMessage('<strong>Found layout:</strong> ' . $this->fullPath);

					static::$cache[$layoutId][$hash] = $foundLayout;

					return static::$cache[$layoutId][$hash];
				}
			}
		}

		// Standard version
		$rawPath  = str_replace('.', '/', $this->layoutId) . '.php';
		$this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath);

		$foundLayout = \JPath::find($this->includePaths, $rawPath);

		if (!$foundLayout)
		{
			$this->addDebugMessage('<strong>Unable to find layout: </strong> ' . $layoutId);

			return;
		}

		$this->addDebugMessage('<strong>Found layout:</strong> ' . $foundLayout);

		static::$cache[$layoutId][$hash] = $foundLayout;

		return static::$cache[$layoutId][$hash];
	}

	/**
	 * Add one path to include in layout search. Proxy of addIncludePaths()
	 *
	 * @param   string  $path  The path to search for layouts
	 *
	 * @return  self
	 *
	 * @since   3.2
	 */
	public function addIncludePath($path)
	{
		$this->addIncludePaths($path);

		return $this;
	}

	/**
	 * Add one or more paths to include in layout search
	 *
	 * @param   string|string[]  $paths  The path or array of paths to search for layouts
	 *
	 * @return  self
	 *
	 * @since   3.2
	 */
	public function addIncludePaths($paths)
	{
		if (empty($paths))
		{
			return $this;
		}

		$includePaths = $this->getIncludePaths();

		if (is_array($paths))
		{
			$includePaths = array_unique(array_merge($paths, $includePaths));
		}
		else
		{
			array_unshift($includePaths, $paths);
		}

		$this->setIncludePaths($includePaths);

		return $this;
	}

	/**
	 * Clear the include paths
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function clearIncludePaths()
	{
		$this->includePaths = array();

		return $this;
	}

	/**
	 * Get the active include paths
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	public function getIncludePaths()
	{
		if (empty($this->includePaths))
		{
			$this->includePaths = $this->getDefaultIncludePaths();
		}

		return $this->includePaths;
	}

	/**
	 * Get the active layout id
	 *
	 * @return  string
	 *
	 * @since   3.5
	 */
	public function getLayoutId()
	{
		return $this->layoutId;
	}

	/**
	 * Get the active suffixes
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	public function getSuffixes()
	{
		return $this->getOptions()->get('suffixes', array());
	}

	/**
	 * Load the automatically generated language suffixes.
	 * Example: array('es-ES', 'es', 'ltr')
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function loadLanguageSuffixes()
	{
		$lang = \JFactory::getLanguage();

		$langTag = $lang->getTag();
		$langParts = explode('-', $langTag);

		$suffixes = array($langTag, $langParts[0]);
		$suffixes[] = $lang->isRTL() ? 'rtl' : 'ltr';

		$this->setSuffixes($suffixes);

		return $this;
	}

	/**
	 * Load the automatically generated version suffixes.
	 * Example: array('j311', 'j31', 'j3')
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function loadVersionSuffixes()
	{
		$cmsVersion = new \JVersion;

		// Example j311
		$fullVersion = 'j' . str_replace('.', '', $cmsVersion->getShortVersion());

		// Create suffixes like array('j311', 'j31', 'j3')
		$suffixes = array(
			$fullVersion,
			substr($fullVersion, 0, 3),
			substr($fullVersion, 0, 2),
		);

		$this->setSuffixes(array_unique($suffixes));

		return $this;
	}

	/**
	 * Remove one path from the layout search
	 *
	 * @param   string  $path  The path to remove from the layout search
	 *
	 * @return  self
	 *
	 * @since   3.2
	 */
	public function removeIncludePath($path)
	{
		$this->removeIncludePaths($path);

		return $this;
	}

	/**
	 * Remove one or more paths to exclude in layout search
	 *
	 * @param   string  $paths  The path or array of paths to remove for the layout search
	 *
	 * @return  self
	 *
	 * @since   3.2
	 */
	public function removeIncludePaths($paths)
	{
		if (!empty($paths))
		{
			$paths = (array) $paths;

			$this->includePaths = array_diff($this->includePaths, $paths);
		}

		return $this;
	}

	/**
	 * Validate that the active component is valid
	 *
	 * @param   string  $option  URL Option of the component. Example: com_content
	 *
	 * @return  boolean
	 *
	 * @since   3.2
	 */
	protected function validComponent($option = null)
	{
		// By default we will validate the active component
		$component = ($option !== null) ? $option : $this->options->get('component', null);

		// Valid option format
		if (!empty($component) && substr_count($component, 'com_'))
		{
			// Latest check: component exists and is enabled
			return ComponentHelper::isEnabled($component);
		}

		return false;
	}

	/**
	 * Method to change the component where search for layouts
	 *
	 * @param   string  $option  URL Option of the component. Example: com_content
	 *
	 * @return  mixed  Component option string | null for none
	 *
	 * @since   3.2
	 */
	public function setComponent($option)
	{
		$component = null;

		switch ((string) $option)
		{
			case 'none':
				$component = null;
				break;

			case 'auto':
				$component = ApplicationHelper::getComponentName();
				break;

			default:
				$component = $option;
				break;
		}

		// Extra checks
		if (!$this->validComponent($component))
		{
			$component = null;
		}

		$this->options->set('component', $component);

		// Refresh include paths
		$this->refreshIncludePaths();
	}

	/**
	 * Function to initialise the application client
	 *
	 * @param   mixed  $client  Frontend: 'site' or 0 | Backend: 'admin' or 1
	 *
	 * @return  void
	 *
	 * @since   3.2
	 */
	public function setClient($client)
	{
		// Force string conversion to avoid unexpected states
		switch ((string) $client)
		{
			case 'site':
			case '0':
				$client = 0;
				break;

			case 'admin':
			case '1':
				$client = 1;
				break;

			default:
				$client = (int) \JFactory::getApplication()->isClient('administrator');
				break;
		}

		$this->options->set('client', $client);

		// Refresh include paths
		$this->refreshIncludePaths();
	}

	/**
	 * Change the layout
	 *
	 * @param   string  $layoutId  Layout to render
	 *
	 * @return  self
	 *
	 * @since   3.2
	 *
	 * @deprecated  3.5  Use setLayoutId()
	 */
	public function setLayout($layoutId)
	{
		// Log usage of deprecated function
		\JLog::add(__METHOD__ . '() is deprecated, use FileLayout::setLayoutId() instead.', \JLog::WARNING, 'deprecated');

		return $this->setLayoutId($layoutId);
	}

	/**
	 * Set the active layout id
	 *
	 * @param   string  $layoutId  Layout identifier
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function setLayoutId($layoutId)
	{
		$this->layoutId = $layoutId;
		$this->fullPath = null;

		return $this;
	}

	/**
	 * Refresh the list of include paths
	 *
	 * @return  self
	 *
	 * @since   3.2
	 *
	 * @deprecated  3.5  Use FileLayout::clearIncludePaths()
	 */
	protected function refreshIncludePaths()
	{
		// Log usage of deprecated function
		\JLog::add(__METHOD__ . '() is deprecated, use FileLayout::clearIncludePaths() instead.', \JLog::WARNING, 'deprecated');

		$this->clearIncludePaths();

		return $this;
	}

	/**
	 * Get the default array of include paths
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	public function getDefaultIncludePaths()
	{
		// Reset includePaths
		$paths = array();

		// (1 - highest priority) Received a custom high priority path
		if ($this->basePath !== null)
		{
			$paths[] = rtrim($this->basePath, DIRECTORY_SEPARATOR);
		}

		// Component layouts & overrides if exist
		$component = $this->options->get('component', null);

		if (!empty($component))
		{
			// (2) Component template overrides path
			$paths[] = JPATH_THEMES . '/' . \JFactory::getApplication()->getTemplate() . '/html/layouts/' . $component;

			// (3) Component path
			if ($this->options->get('client') == 0)
			{
				$paths[] = JPATH_SITE . '/components/' . $component . '/layouts';
			}
			else
			{
				$paths[] = JPATH_ADMINISTRATOR . '/components/' . $component . '/layouts';
			}
		}

		// (4) Standard Joomla! layouts overridden
		$paths[] = JPATH_THEMES . '/' . \JFactory::getApplication()->getTemplate() . '/html/layouts';

		// (5 - lower priority) Frontend base layouts
		$paths[] = JPATH_ROOT . '/layouts';

		return $paths;
	}

	/**
	 * Set the include paths to search for layouts
	 *
	 * @param   array  $paths  Array with paths to search in
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function setIncludePaths($paths)
	{
		$this->includePaths = (array) $paths;

		return $this;
	}

	/**
	 * Set suffixes to search layouts
	 *
	 * @param   mixed  $suffixes  String with a single suffix or 'auto' | 'none' or array of suffixes
	 *
	 * @return  self
	 *
	 * @since   3.5
	 */
	public function setSuffixes(array $suffixes)
	{
		$this->options->set('suffixes', $suffixes);

		return $this;
	}

	/**
	 * Render a layout with the same include paths & options
	 *
	 * @param   string  $layoutId     The identifier for the sublayout to be searched in a subfolder with the name of the current layout
	 * @param   mixed   $displayData  Data to be rendered
	 *
	 * @return  string  The necessary HTML to display the layout
	 *
	 * @since   3.2
	 */
	public function sublayout($layoutId, $displayData)
	{
		// Sublayouts are searched in a subfolder with the name of the current layout
		if (!empty($this->layoutId))
		{
			$layoutId = $this->layoutId . '.' . $layoutId;
		}

		$sublayout = new static($layoutId, $this->basePath, $this->options);
		$sublayout->includePaths = $this->includePaths;

		return $sublayout->render($displayData);
	}
}
LayoutHelper.php000064400000002627152344344400007704 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Layout;

use FOF30\Container\Container;

defined('_JEXEC') or die;

class LayoutHelper
{
	/**
	 * A default base path that will be used if none is provided when calling the render method.
	 * Note that JLayoutFile itself will defaults to JPATH_ROOT . '/layouts' if no basePath is supplied at all
	 *
	 * @var    string
	 */
	public static $defaultBasePath = '';

	/**
	 * Method to render the layout.
	 *
	 * @param   Container  $container    The container of your component
	 * @param   string     $layoutFile   Dot separated path to the layout file, relative to base path
	 * @param   object     $displayData  Object which properties are used inside the layout file to build displayed output
	 * @param   string     $basePath     Base path to use when loading layout files
	 *
	 * @return  string
	 */
	public static function render(Container $container, $layoutFile, $displayData = null, $basePath = '')
	{
		$basePath = empty($basePath) ? self::$defaultBasePath : $basePath;

		// Make sure we send null to LayoutFile if no path set
		$basePath = empty($basePath) ? null : $basePath;
		$layout = new LayoutFile($layoutFile, $basePath);
		$layout->container = $container;
		$renderedLayout = $layout->render($displayData);

		return $renderedLayout;
	}

}
LayoutInterface.php000064400000001667152344344400010370 0ustar00<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\CMS\Layout;

defined('JPATH_PLATFORM') or die;

/**
 * Interface to handle display layout
 *
 * @link   https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout
 * @since  3.0
 */
interface LayoutInterface
{
	/**
	 * Method to escape output.
	 *
	 * @param   string  $output  The output to escape.
	 *
	 * @return  string  The escaped output.
	 *
	 * @since   3.0
	 */
	public function escape($output);

	/**
	 * Method to render the layout.
	 *
	 * @param   array  $displayData  Array of properties available for use inside the layout file to build the displayed output
	 *
	 * @return  string  The rendered layout.
	 *
	 * @since   3.0
	 */
	public function render($displayData);
}
LayoutFile.php000064400000004230152345113640007335 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Layout;

use FOF30\Container\Container;
use JLayoutFile;

defined('_JEXEC') or die;

/**
 * Base class for rendering a display layout
 * loaded from from a layout file
 *
 * This class searches for Joomla! version override Layouts. For example,
 * if you have run this under Joomla! 3.0 and you try to load
 * mylayout.default it will automatically search for the
 * layout files default.j30.php, default.j3.php and default.php, in this
 * order.
 *
 * @package  FrameworkOnFramework
 */
class LayoutFile extends JLayoutFile
{
	/** @var  Container  The component container */
	public $container = null;

	/**
	 * Method to finds the full real file path, checking possible overrides
	 *
	 * @return  string  The full path to the layout file
	 */
	protected function getPath()
	{
		if (is_null($this->container))
		{
			$component       = $this->options->get('component');
			$this->container = Container::getInstance($component);
		}

		$filesystem = $this->container->filesystem;

		if (is_null($this->fullPath) && !empty($this->layoutId))
		{
			$parts = explode('.', $this->layoutId);
			$file  = array_pop($parts);

			$filePath = implode('/', $parts);
			$suffixes = $this->container->platform->getTemplateSuffixes();

			foreach ($suffixes as $suffix)
			{
				$files[] = $file . $suffix . '.php';
			}

			$files[] = $file . '.php';

			$platformDirs = $this->container->platform->getPlatformBaseDirs();
			$prefix       = $this->container->platform->isBackend() ? $platformDirs['admin'] : $platformDirs['root'];

			$possiblePaths = array(
				$prefix . '/templates/' . $this->container->platform->getTemplate() . '/html/layouts/' . $filePath,
				$this->basePath . '/' . $filePath,
				$platformDirs['root'] . '/layouts/' . $filePath,
			);

			reset($files);

			foreach ($files as $fileName)
			{
				if (!is_null($this->fullPath))
				{
					break;
				}

				$r              = $filesystem->pathFind($possiblePaths, $fileName);
				$this->fullPath = $r === false ? null : $r;
			}
		}

		return $this->fullPath;
	}
}
BaseErector.php000064400000014671152422227230007465 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

use FOF30\Model\DataModel;

defined('_JEXEC') or die;

/**
 * Class BaseErector
 * @package FOF30\Factory\Scaffolding\Layout
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
class BaseErector implements ErectorInterface
{
	/**
	 * The Builder which called us
	 *
	 * @var \FOF30\Factory\Scaffolding\Layout\Builder
	 */
	protected $builder = null;

	/**
	 * The Model attached to the view we're building
	 *
	 * @var \FOF30\Model\DataModel
	 */
	protected $model = null;

	/**
	 * The name of our view
	 *
	 * @var string
	 */
	protected $viewName = null;

	/**
	 * The XML document we're constructing
	 *
	 * @var  \SimpleXMLElement
	 */
	protected $xml;

	/**
	 * The common language key prefix, e.g. COM_EXAMPLE_MYVIEW_
	 *
	 * @var null
	 */
	private $langKeyPrefix = null;

	/**
	 * Strings to add to the language definition
	 *
	 * @var array
	 */
	private $strings = array();

	/**
	 * Construct the erector object
	 *
	 * @param   \FOF30\Factory\Scaffolding\Layout\Builder $parent   The parent builder
	 * @param   \FOF30\Model\DataModel             $model    The model we're erecting a scaffold against
	 * @param   string                             $viewName The view name for this model
	 */
	public function __construct(Builder $parent, DataModel $model, $viewName)
	{
		$this->builder = $parent;
		$this->model = $model;
		$this->viewName = $viewName;

		$this->xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><form></form>');
	}

	/**
	 * Erects a scaffold. It then uses the parent's setXml and setStrings to assign the erected scaffold and the
	 * additional language strings to the parent which will decide what to do with that.
	 *
	 * @return  void
	 *
	 * @throws  \LogicException  Because it's not implemented
	 */
	public function build()
	{
		throw new \LogicException('You need to implement build() in your Erector class');
	}

	/**
	 * Returns the common language key prefix, something like "COM_EXAMPLE_MYVIEW_"
	 *
	 * @return string
	 */
	protected function getLangKeyPrefix()
	{
		if (empty($this->langKeyPrefix))
		{
			$prefix = $key = $this->builder->getContainer()->componentName . '_'
				. $this->viewName . '_';
			$this->langKeyPrefix = strtoupper($prefix);
		}

		return $this->langKeyPrefix;
	}

	/**
	 * Returns the language definition for a field. The hashed array has two keys, label and desc, each one containing
	 * the language definition for the label and description of the field. Each definition has the keys key and value
	 * with the language key and actual language string.
	 *
	 * @param   string  $fieldName
	 *
	 * @return array
	 */
	protected function getFieldLabel($fieldName)
	{
		$fieldNameForKey = strtoupper($fieldName);

		$definition = array(
			'label' => array(
				'key' => $this->getLangKeyPrefix() . $fieldNameForKey . '_LABEL',
				'value' => ucfirst($fieldName),
			),
			'desc' => array(
				'key' => $this->getLangKeyPrefix() . $fieldNameForKey . '_DESC',
				'value' => 'Description for ' . ucfirst($fieldName),
			)
		);

		return $definition;
	}

	/**
	 * Convert the database type into something we can use
	 *
	 * @param   string  $type  The type of the database field
	 *
	 * @return  array
	 */
	public static function getFieldType($type)
	{
		if (empty($type))
		{
			return null;
		}

		// Remove parentheses, indicating field options / size (they don't matter in type detection)
		if (strpos($type, '(') === false)
		{
			$type .= '()';
		}

		list($type, $parameters) = explode('(', $type);

		$detectedType = null;
		$detectedParameters = null;

		$type = strtolower($type);

		switch (trim($type))
		{
			case 'varchar':
			case 'text':
			case 'char':
			case 'character varying':
			case 'nvarchar':
			case 'nchar':
				$detectedType = 'Text';
				break;

			case 'smalltext':
			case 'longtext':
			case 'mediumtext':
				$detectedType = 'Text';
				break;

			case 'date':
			case 'datetime':
			case 'time':
			case 'year':
			case 'timestamp':
			case 'timestamp without time zone':
			case 'timestamp with time zone':
				$detectedType = 'Calendar';
				break;

			case 'tinyint':
			case 'smallint':
				$detectedType = 'Checkbox';
				break;

			case 'int':
			case 'integer':
			case 'bigint':
				// Because the Integer field is rendered in Joomla! as a drop-down list. Ugh!!!
				$detectedType = 'Number';
				break;

			case 'float':
			case 'double':
			case 'currency':
				$detectedType = 'Number';
				break;

			case 'enum':
				$detectedType = 'GenericList';
				$parameters = trim($parameters, "\t\n\r\0\x0B )");
				$detectedParameters = explode(',', $parameters);
				$detectedParameters = array_map(function ($x) { return trim($x, "'\n\r\t\0\x0B"); }, $detectedParameters);
				$temp = array();
				foreach ($detectedParameters as $v)
				{
					$temp[$v] = $v;
				}
				$detectedParameters = $temp;
				break;
		}

		// Sometimes we have character types followed by a space and some cruft. Let's handle them.
		if (is_null($detectedType) && !empty($type))
		{
			list ($type, ) = explode(' ', $type);

			switch (trim($type))
			{
				case 'varchar':
				case 'text':
				case 'char':
				case 'character varying':
				case 'nvarchar':
				case 'nchar':
					$detectedType = 'Text';
					break;

				case 'smalltext':
				case 'longtext':
				case 'mediumtext':
					$detectedType = 'Text';
					break;

				case 'date':
				case 'datetime':
				case 'time':
				case 'year':
				case 'timestamp':
					$detectedType = 'Calendar';
					break;

				case 'tinyint':
				case 'smallint':
					$detectedType = 'Checkbox';
					break;

				default:
					$detectedType = 'Integer';
					break;
			}
		}

		// If all else fails assume it's a Text and hope for the best
		if (empty($detectedType))
		{
			$detectedType = 'Text';
		}

		return array('type' => $detectedType, 'params' => $detectedParameters);
	}

	/**
	 * Adds a language string definition as long as it doesn't exist in the existing language file.
	 *
	 * @param   string  $key    The language string key
	 * @param   string  $value  The language string
	 */
	protected function addString($key, $value)
	{
		if (\JText::_($key) != $key)
		{
			return;
		}

		$this->strings[$key] = $value;
	}

	/**
	 * Push the form and strings to the builder
	 */
	protected function pushResults()
	{
		$this->builder->setStrings($this->strings);
		$this->builder->setXml($this->xml);
	}
}
BrowseErector.php000064400000052571152422227300010053 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

use FOF30\Model\DataModel;

defined('_JEXEC') or die;

/**
 * Erects a scaffolding XML for browse views
 *
 * @package FOF30\Factory\Scaffolding
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
class BrowseErector extends BaseErector implements ErectorInterface
{
	public function build()
	{
		// Get a reference to the model
		$model = $this->model;

		// Create the "no records" language string
		$noRowsKey = strtoupper($this->builder->getContainer()->componentName) . '_COMMON_NORECORDS';
		$this->addString($noRowsKey, 'There are no records to display');

		// Create the attributes of the form's base element
		$this->xml->addAttribute('type', 'browse');
		$this->xml->addAttribute('show_header', "1");
		$this->xml->addAttribute('show_filters', "1");
		$this->xml->addAttribute('show_pagination', "1");
		$this->xml->addAttribute('norows_placeholder', $noRowsKey);

		// Create the headerset and fieldset sections of the form file
		$headerSet = $this->xml->addChild('headerset');
		$fieldSet = $this->xml->addChild('fieldset');
		$fieldSet->addAttribute('name', 'items');

		// Get the database fields
		$allFields = $model->getTableFields();

		// Ordering must go first
		if ($model->hasField('ordering'))
		{
			$this->applyOrderingField($model, $headerSet, $fieldSet, $allFields);

		}

		// Primary key field goes next
		$this->applyPrimaryKeyField($model, $headerSet, $fieldSet, $allFields);

		// Get a list of "do not display" fields
		$doNotShow = $this->getDoNotShow();

		foreach ($allFields as $fieldName => $fieldDefinition)
		{
			// Skip the fields which shouldn't be displayed
			if (in_array($fieldName, $doNotShow))
			{
				continue;
			}

			// Get the lowercase field name and prepare to handle specially named fields
			$lowercaseFieldName = strtolower($fieldName);

			// access => AccessLevel
			if ($model->getFieldAlias('access') == $fieldName)
			{
				$this->applyAccessLevelField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// title => Title
			if ($model->getFieldAlias('title') == $fieldName)
			{
				$this->applyTitleField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// slug => Hide if there is a title field as well
			if ($model->getFieldAlias('slug') == $fieldName)
			{
				$titleField = $model->getFieldAlias('title');

				if (array_key_exists($titleField, $allFields))
				{
					continue;
				}
			}

			// tag => Tag
			if ($model->getFieldAlias('tag') == $fieldName)
			{
				$this->applyTagField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// enabled => Actions
			if ($model->getFieldAlias('enabled') == $fieldName)
			{
				$this->applyActionsField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// cache_handler => CacheHandler
			if ($lowercaseFieldName == 'cache_handler')
			{
				$this->applyCacheHandlerField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// component_id => Components
			if ($lowercaseFieldName == 'component_id')
			{
				$this->applyComponentsField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// body, introtext, fulltext => Editor
			if (in_array($lowercaseFieldName, array('body', 'introtext', 'fulltext', 'description')))
			{
				$this->applyEditorField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}


			// email, *_email => Email
			if (($lowercaseFieldName == 'email') || (substr($lowercaseFieldName, -6) == 'email'))
			{
				$this->applyEmailField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// image, media, *_image => Media
			if (
				in_array($lowercaseFieldName, array('image', 'media'))
				|| (substr($lowercaseFieldName, -6) == '_image')
			)
			{
				$this->applyMediaField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// language, lang, lang_id => Language
			if (in_array($lowercaseFieldName, array('language', 'lang', 'lang_id')))
			{
				$this->applyLanguageField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// password, passwd, pass => Password
			if (in_array($lowercaseFieldName, array('password', 'passwd', 'pass')))
			{
				$this->applyPasswordField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// plugin_id => Plugins
			if ($lowercaseFieldName == 'plugin_id')
			{
				$this->applyPluginsField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// asset_id => Rules (not applicable here)
			if ($lowercaseFieldName == 'asset_id')
			{
				continue;
			}

			// session_handler => SessionHandler
			if ($lowercaseFieldName == 'session_handler')
			{
				$this->applySessionHandlerField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// tel, telephone, phone => Tel
			if (in_array($lowercaseFieldName, array('tel', 'telephone', 'phone')))
			{
				$this->applyTelField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// timezone, tz, time_zone => Timezone
			if (in_array($lowercaseFieldName, array('timezone', 'tz', 'time_zone')))
			{
				$this->applyTimezoneField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// url, link, href => Url
			if (in_array($lowercaseFieldName, array('url', 'link', 'href')))
			{
				$this->applyUrlField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// user, user_id, userid, uid => User
			if (in_array($lowercaseFieldName, array('user', 'user_id', 'userid', 'uid')))
			{
				$this->applyUserField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// group, group_id, groupid, gid => UserGroup
			if (in_array($lowercaseFieldName, array('group', 'group_id', 'groupid', 'gid')))
			{
				$this->applyUserGroupField($model, $headerSet, $fieldSet, $fieldName);

				continue;
			}

			// Special handling for myComponent_whatever_id fields
			$myComponentPrefix = $this->builder->getContainer()->bareComponentName . '_';

			if ((strpos($fieldName, $myComponentPrefix) === 0) && (substr($fieldName, -3) == '_id'))
			{
				$parts = explode('_', $fieldName);
				array_pop($parts);
				array_shift($parts);

				// myComponent_something_id => Relation or Model
				if (count($parts) == 1)
				{
					$foreignName = array_shift($parts);
				}
				// myComponent_something_another_id => Relation
				else
				{
					$foreignName1 = array_shift($parts);
					$foreignName1 = $this->model->getContainer()->inflector->pluralize($foreignName1);
					$foreignName2 = array_shift($parts);
					$foreignName2 = $this->model->getContainer()->inflector->pluralize($foreignName2);
					$modelName = $model->getName();
					$modelName = $this->model->getContainer()->inflector->pluralize($modelName);

					$foreignName = ($foreignName1 == $modelName) ? $foreignName2 : $foreignName1;
				}

				try
				{
					$model->getRelations()->getRelation($foreignName);

					$this->applyRelationField($model, $headerSet, $fieldSet, $fieldName);

					continue;
				}
				catch (DataModel\Relation\Exception\RelationNotFound $e)
				{
					$foreignName = $this->model->getContainer()->inflector->pluralize($foreignName);

					try
					{
						$this->applyModelField($model, $headerSet, $fieldSet, $fieldName, $foreignName);

						continue;
					}
					catch (\Exception $e)
					{
					}
				}
			}

			// Other fields, use getFieldType
			$typeDef = $this->getFieldType($fieldDefinition->Type);

			switch ($typeDef['type'])
			{
				case 'Text':
					$this->applyTextField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'Editor':
					$this->applyEditorField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'Calendar':
					$this->applyCalendarField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'Checkbox':
					$this->applyCheckboxField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'Integer':
					$this->applyIntegerField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'Number':
					$this->applyNumberField($model, $headerSet, $fieldSet, $fieldName);
					break;

				case 'GenericList':
					$this->applyGenericListField($model, $headerSet, $fieldSet, $fieldName, $typeDef['params']);
					break;
			}
		}

		$this->pushResults();
	}

	/**
	 * Apply the ordering field
	 *
	 * @param \FOF30\Model\DataModel $model
	 * @param \SimpleXMLElement      $headerSet
	 * @param \SimpleXMLElement      $fieldSet
	 * @param array                  $allFields
	 */
	private function applyOrderingField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, array &$allFields)
	{
		$langDefs = $this->getFieldLabel('ordering');
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$fieldName = $model->getFieldAlias('ordering');

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $fieldName);
		$header->addAttribute('type', 'Ordering');
		$header->addAttribute('label', $langDefs['label']['key']);
		$header->addAttribute('sortable', 'true');
		$header->addAttribute('tdwidth', '1%');

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'Ordering');
		$field->addAttribute('class', 'input-mini input-sm');

		unset($allFields[$fieldName]);
	}

	/**
	 * Apply the ordering field
	 *
	 * @param \FOF30\Model\DataModel $model
	 * @param \SimpleXMLElement      $headerSet
	 * @param \SimpleXMLElement      $fieldSet
	 * @param array                  $allFields
	 */
	private function applyPrimaryKeyField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, array &$allFields)
	{
		$keyField = $model->getKeyName();

		$langDefs = $this->getFieldLabel($keyField);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $keyField);
		$header->addAttribute('type', 'RowSelect');
		$header->addAttribute('label', $langDefs['label']['key']);
		$header->addAttribute('sortable', 'true');
		$header->addAttribute('tdwidth', '20');

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $keyField);
		$field->addAttribute('type', 'SelectRow');

		unset($allFields[$keyField]);
	}

	private function applyFieldOfType(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName, $fieldTypeHeader, $fieldTypeField, array $headerAttributes = array())
	{
		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $fieldName);
		$header->addAttribute('type', $fieldTypeHeader);
		$header->addAttribute('label', $langDefs['label']['key']);

		if (!empty($headerAttributes))
		{
			foreach ($headerAttributes as $k => $v)
			{
				$header->addAttribute($k, $v);
			}
		}

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', $fieldTypeField);
	}

	/**
	 * Apply an access level field
	 *
	 * @param \FOF30\Model\DataModel $model
	 * @param \SimpleXMLElement      $headerSet
	 * @param \SimpleXMLElement      $fieldSet
	 * @param string                 $fieldName
	 */
	private function applyAccessLevelField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'AccessLevel', 'AccessLevel', array(
			'sortable' => 'true'
		));
	}

	private function applyActionsField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Published', 'Actions', array(
			'sortable' => 'true'
		));
	}

	private function applyCacheHandlerField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'CacheHandler', array(
			'sortable' => 'true'
		));
	}

	private function applyCalendarField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Date', 'Calendar', array(
			'sortable' => 'true'
		));
	}

	private function applyCheckboxField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Checkbox', array(
			'sortable' => 'true'
		));
	}

	private function applyComponentsField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Components', array(
			'sortable' => 'true'
		));
	}

	private function applyEditorField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Editor', array(
			'sortable' => 'true'
		));
	}

	private function applyEmailField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Email', array(
			'sortable' => 'true'
		));
	}

	private function applyIntegerField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Integer', array(
			'sortable' => 'true'
		));
	}

	private function applyNumberField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Number', array(
			'sortable' => 'true'
		));
	}

	private function applyMediaField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Media');
	}

	private function applyLanguageField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Language', 'Language', array(
			'sortable' => 'true'
		));
	}

	private function applyPasswordField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Password', array(
			'sortable' => 'true'
		));
	}

	private function applyPluginsField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Plugins', array(
			'sortable' => 'true'
		));
	}

	private function applySessionHandlerField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'SessionHandler', array(
			'sortable' => 'true'
		));
	}

	private function applyTelField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Tel', array(
			'sortable' => 'true'
		));
	}

	private function applyTextField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Text', array(
			'sortable' => 'true'
		));
	}

	private function applyTimezoneField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Timezone', array(
			'sortable' => 'true'
		));
	}

	private function applyUrlField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'Url', array(
			'sortable' => 'true'
		));
	}

	private function applyUserField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'User', array(
			'sortable' => 'true'
		));
	}

	private function applyUserGroupField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Searchable', 'UserGroup', array(
			'sortable' => 'true'
		));
	}

	private function applyRelationField($model, $headerSet, $fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Relation', array(
			'sortable' => 'true'
		));
	}

	private function applyTagField($model, $headerSet, $fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $headerSet, $fieldSet, $fieldName, 'Field', 'Tag', array(
			'sortable' => 'true'
		));
	}

	private function applyTitleField($model, \SimpleXMLElement $headerSet, \SimpleXMLElement $fieldSet, $fieldName)
	{
		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $fieldName);
		$header->addAttribute('type', 'Searchable');
		$header->addAttribute('label', $langDefs['label']['key']);

		if (!empty($headerAttributes))
		{
			foreach ($headerAttributes as $k => $v)
			{
				$header->addAttribute($k, $v);
			}
		}

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'Sortable');
		$field->addAttribute('url', 'index.php?option=' .
			$this->builder->getContainer()->componentName . '&view=' . $this->model->getContainer()->inflector->singularize($this->viewName) . '&id=[ITEM:ID]&[TOKEN]=1'
		);
	}

	private function applyModelField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName, $modelName)
	{
		// This will fail if the model is invalid, e.g. we have example_foobar_id but no #__example_foobars table. The
		// error will balloon up the stack and the field will be rendered as simple number field instead of a Model
		// field.
		/** @var DataModel $foreignModel */
		$foreignModel = $model->getContainer()->factory->model($modelName);

		$value_field = $foreignModel->getKeyName();

		if ($foreignModel->hasField('title'))
		{
			$value_field = $foreignModel->getFieldAlias('title');
		}

		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $fieldName);
		$header->addAttribute('type', 'Model');
		$header->addAttribute('model', $modelName);
		$header->addAttribute('key_field', $foreignModel->getKeyName());
		$header->addAttribute('value_field', $value_field);
		$header->addAttribute('label', $langDefs['label']['key']);
		$header->addAttribute('sortable', 'true');

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'Model');
		$field->addAttribute('model', $modelName);
		$field->addAttribute('key_field', $foreignModel->getKeyName());
		$field->addAttribute('value_field', $value_field);
	}

	private function applyGenericListField(DataModel $model, \SimpleXMLElement &$headerSet, \SimpleXMLElement &$fieldSet, $fieldName, $options)
	{
		$displayOptions = array();

		foreach ($options as $k => $v)
		{
			$langKey = $this->builder->getContainer()->componentName . '_' . $this->viewName . '_' . $fieldName .
				'_OPT_' . $k;
			$this->addString($langKey, $v);
			$displayOptions[$k] = $langKey;
		}

		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$header = $headerSet->addChild('header');
		$header->addAttribute('name', $fieldName);
		$header->addAttribute('type', 'Selectable');
		$header->addAttribute('label', $langDefs['label']['key']);
		$header->addAttribute('sortable', 'true');

		foreach ($displayOptions as $k => $v)
		{
			$header->addChild('option', $v)->addAttribute('value', $k);
		}

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'GenericList');

		foreach ($displayOptions as $k => $v)
		{
			$field->addChild('option', $v)->addAttribute('value', $k);
		}
	}

	/**
	 * Create a list of fields which should not be shown in the form. These are fields like created/modified/locked
	 * user and time and other internal fields which should not be part of the form output.
	 *
	 * @return  array
	 */
	private function getDoNotShow()
	{
		$return = array();
		$checkFields = array('created_by', 'created_on', 'modified_by', 'modified_on', 'locked_by', 'locked_on');

		foreach ($checkFields as $checkField)
		{
			$return[] = $this->model->getFieldAlias($checkField);
		}

		return $return;
	}
}
Builder.php000064400000015517152422227540006661 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

use FOF30\Container\Container;
use SimpleXMLElement;

defined('_JEXEC') or die;

/**
 * Scaffolding Builder
 *
 * Creates an automatic XML form definition to render a view based on the database fields you've got in the model. This
 * is not designed for production; it's designed to give you a way to quickly add some test data to your component
 * and get started really fast with FOF development.
 *
 * @package FOF30\Factory\Scaffolding
 *
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
class Builder
{
	/** @var  \FOF30\Container\Container  The container we belong to */
	protected $container = null;

	/** @var  bool  Should I save the scaffolding results? */
	protected $saveScaffolding = false;

	/** @var  SimpleXMLElement  The form we will be returning to the caller */
	protected $xml;

	/** @var  array  Language string definitions we need to add to the component's language file */
	protected $strings = array();

	/**
	 * Create the scaffolding builder instance
	 *
	 * @param \FOF30\Container\Container $c
	 */
	public function __construct(Container $c)
	{
		$this->container = $c;

		$this->saveScaffolding = $this->container->factory->isSaveScaffolding();
	}

	/**
	 * Make a new scaffolding document
	 *
	 * @param   string  $requestedFilename  The requested filename, e.g. form.default.xml
	 * @param   string  $viewName           The name of the view this form will be used to render
	 *
	 * @return  string|null  The XML source or null if we can't make a scaffolding XML
	 */
	public function make($requestedFilename, $viewName)
	{
		// Initialise
		$this->xml = null;
		$this->strings = array();

		// The requested filename should be in the format "form.SOMETHING.xml"
		if (substr($requestedFilename, 0, 5) !== 'form.')
		{
			return null;
		}

		// Get the requested form type
		$formType = substr($requestedFilename, 5);

		// Make sure the requested form type is supported by this builder
		if (!in_array($formType, array('default', 'form', 'item')))
		{
			return null;
		}

		switch ($formType)
		{
			default:
			case 'default':
				$builderType = 'Browse';
				break;

			case 'form':
				$builderType = 'Form';
				break;

			case 'item':
				$builderType = 'Item';
				break;
		}

		// Get the model
		$model = $this->container->factory->model($viewName);

		// Create the scaffolding object and build the XML file
		$className = 'FOF30\\Factory\\Scaffolding\\Layout\\' . $builderType . 'Erector';

		/** @var ErectorInterface $erector */
		$erector = new $className($this, $model, $viewName);
		$erector->build();

		if ($this->saveScaffolding)
		{
			$this->saveXml($requestedFilename, $viewName);
			$this->saveStrings();
		}

		$this->applyStrings();

		return $this->xml->asXML();
	}

	/**
	 * Set the XML form document
	 *
	 * @param   SimpleXMLElement  $xml  The XML document to set
	 */
	public function setXml(SimpleXMLElement $xml)
	{
		$this->xml = $xml;
	}

	/**
	 * Set the additional strings array
	 *
	 * @param   array  $strings  The strings array to set
	 */
	public function setStrings(array $strings)
	{
		$this->strings = $strings;
	}

	/**
	 * Load the strings array in Joomla!'s JLanguage object
	 */
	protected function applyStrings()
	{
		// If we don't have language strings there's no point continuing
		if (empty($this->strings))
		{
			return;
		}

		// Get a temporary filename
		$baseDirs = $this->container->platform->getPlatformBaseDirs();
		$tempDir = $baseDirs['tmp'];
		$filename = tempnam($tempDir, 'fof');

		if ($filename === false)
		{
			return;
		}

		// Save the strings to a temporary file
		$this->saveStrings($filename);

		// Load the temporary file
		$lang = $this->container->platform->getLanguage();
		$langReflection = new \ReflectionObject($lang);
		$loadLangReflection = $langReflection->getMethod('loadLanguage');
		$loadLangReflection->setAccessible(true);
		$loadLangReflection->invoke($lang, $filename, $this->container->componentName);

		// Delete temporary filename
		@unlink($filename);
	}

	/**
	 * Gets the container this builder belongs to
	 *
	 * @return Container
	 */
	public function getContainer()
	{
		return $this->container;
	}

	/**
	 * Save the XML form as a file
	 *
	 * @param   string  $requestedFilename  The requested filename, e.g. form.default.xml
	 * @param   string  $viewName           The name of the view this form will be used to render
	 */
	protected function saveXml($requestedFilename, $viewName)
	{
		$path = $this->container->frontEndPath;

		if ($this->container->platform->isBackend())
		{
			$path = $this->container->backEndPath;
		}

		$targetFilename = $path . '/View/' . $viewName . '/tmpl/' . $requestedFilename;

		$directory = dirname($targetFilename);

		if (!is_dir($directory))
		{
			$createdDirectory = @mkdir($directory, 0755, true);

			if (!@$createdDirectory)
			{
				\JLoader::import('joomla.filesystem.folder');
				\JFolder::create($directory, 0755);
			}
		}

		$xml = $this->xml->asXML();

		$domDocument = new \DOMDocument('1.0');
		$domDocument->loadXML($xml);
		$domDocument->preserveWhiteSpace = false;
		$domDocument->formatOutput = true;
		$xml = $domDocument->saveXML();

		$saveResult = @file_put_contents($targetFilename . '.xml', $xml);

		if ($saveResult === false)
		{
			\JLoader::import('joomla.filesystem.file');
			\JFile::write($targetFilename, $xml);
		}
	}

	/**
	 * Saves the language strings, merged with any old ones, to a Joomla! INI language file
	 *
	 * @param   string  $targetFilename  The full path to the INI file, leave blank for auto-detection
	 */
	protected function saveStrings($targetFilename = null)
	{
		// If no filename is defined, get the component's language definition filename
		if (empty($targetFilename))
		{
			$jLang = $this->container->platform->getLanguage();
			$basePath = $this->container->platform->isBackend() ? JPATH_ADMINISTRATOR : JPATH_SITE;

			$lang = $jLang->setLanguage('en-GB');
			$jLang->setLanguage($lang);

			$path = $jLang->getLanguagePath($basePath, $lang);

			$targetFilename = $path . '/' . $lang . '.' . $this->container->componentName . '.ini';
		}

		// Try to load the existing language file
		$strings = array();

		if (@file_exists($targetFilename))
		{
			$contents = file_get_contents($targetFilename);
			$contents = str_replace('_QQ_', '"\""', $contents);
			$strings = @parse_ini_string($contents);
		}

		$strings = array_merge($strings, $this->strings);

		// Create the INI file
		$iniFile = '';

		foreach ($strings as $k => $v)
		{
			$iniFile .= strtoupper($k) . '="' . str_replace('"', '"_QQ_"', $v) . "\"\n";
		}

		// Save it
		$saveResult = @file_put_contents($targetFilename, $iniFile);

		if ($saveResult === false)
		{
			\JLoader::import('joomla.filesystem.file');
			\JFile::write($targetFilename, $iniFile);
		}
	}
}
ErectorInterface.php000064400000002106152422227610010503 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

use FOF30\Model\DataModel;

defined('_JEXEC') or die;

/**
 * Interface ErectorInterface
 * @package FOF30\Factory\Scaffolding\Layout
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
interface ErectorInterface
{
	/**
	 * Construct the erector object
	 *
	 * @param   \FOF30\Factory\Scaffolding\Layout\Builder  $parent    The parent builder
	 * @param   \FOF30\Model\DataModel              $model     The model we're erecting a scaffold against
	 * @param   string                              $viewName  The view name for this model
	 */
	public function __construct(Builder $parent, DataModel $model, $viewName);

	/**
	 * Erects a scaffold. It then uses the parent's setXml and setStrings to assign the erected scaffold and the
	 * additional language strings to the parent which will decide what to do with that.
	 *
	 * @return  void
	 */
	public function build();
}
FormErector.php000064400000040337152422227660007523 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

use FOF30\Model\DataModel;

defined('_JEXEC') or die;

/**
 * Erects a scaffolding XML for edit views
 *
 * @package FOF30\Factory\Scaffolding
 *
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
class FormErector extends BaseErector implements ErectorInterface
{
	protected $addDescriptions = true;

	public function build()
	{
		// Get a reference to the model
		$model = $this->model;

		// Create the attributes of the form's base element
		$this->xml->addAttribute('validate', 'true');


		// Create the fieldset sections of the form file
		$labelKey = $this->getLangKeyPrefix() . 'GROUP_BASIC';
		$this->addString($labelKey, 'Basic');

		$fieldSet = $this->xml->addChild('fieldset');
		$fieldSet->addAttribute('name', 'scaffolding');
		$fieldSet->addAttribute('label', $labelKey);

		// Get the database fields
		$allFields = $model->getTableFields();

		// Ordering is not included
		if ($model->hasField('ordering'))
		{
			$fieldName = $model->getFieldAlias('ordering');
			unset($allFields[$fieldName]);
		}

		// Primary key is not inclided
		$primaryKeyField = $model->getKeyName();
		unset($allFields[$primaryKeyField]);

		// Get a list of "do not display" fields
		$doNotShow = $this->getDoNotShow();

		foreach ($allFields as $fieldName => $fieldDefinition)
		{
			// Skip the fields which shouldn't be displayed
			if (in_array($fieldName, $doNotShow))
			{
				continue;
			}

			// Get the lowercase field name and prepare to handle specially named fields
			$lowercaseFieldName = strtolower($fieldName);

			// access => AccessLevel
			if ($model->getFieldAlias('access') == $fieldName)
			{
				$this->applyAccessLevelField($model, $fieldSet, $fieldName);

				continue;
			}

			// tag => Tag
			if ($model->getFieldAlias('tag') == $fieldName)
			{
				$this->applyTagField($model, $fieldSet, $fieldName);

				continue;
			}

			// enabled => Published
			if ($model->getFieldAlias('enabled') == $fieldName)
			{
				$this->applyPublishedField($model, $fieldSet, $fieldName);

				continue;
			}

			// cache_handler => CacheHandler
			if ($lowercaseFieldName == 'cache_handler')
			{
				$this->applyCacheHandlerField($model, $fieldSet, $fieldName);

				continue;
			}

			// component_id => Components
			if ($lowercaseFieldName == 'component_id')
			{
				$this->applyComponentsField($model, $fieldSet, $fieldName);

				continue;
			}

			// body, introtext, fulltext, description => Editor
			if (in_array($lowercaseFieldName, array('body', 'introtext', 'fulltext', 'description')))
			{
				$this->applyEditorField($model, $fieldSet, $fieldName);

				continue;
			}


			// email, *_email => Email
			if (($lowercaseFieldName == 'email') || (substr($lowercaseFieldName, -6) == 'email'))
			{
				$this->applyEmailField($model, $fieldSet, $fieldName);

				continue;
			}

			// image, media, *_image => Media
			if (
				in_array($lowercaseFieldName, array('image', 'media'))
				|| (substr($lowercaseFieldName, -6) == '_image')
			)
			{
				$this->applyMediaField($model, $fieldSet, $fieldName);

				continue;
			}

			// language, lang, lang_id => Language
			if (in_array($lowercaseFieldName, array('language', 'lang', 'lang_id')))
			{
				$this->applyLanguageField($model, $fieldSet, $fieldName);

				continue;
			}

			// password, passwd, pass => Password
			if (in_array($lowercaseFieldName, array('password', 'passwd', 'pass')))
			{
				$this->applyPasswordField($model, $fieldSet, $fieldName);

				continue;
			}

			// plugin_id => Plugins
			if ($lowercaseFieldName == 'plugin_id')
			{
				$this->applyPluginsField($model, $fieldSet, $fieldName);

				continue;
			}

			// asset_id => Rules (new tab)
			if ($lowercaseFieldName == 'asset_id')
			{
				// Do not show the rules tab in read views
				if (!$this->addDescriptions)
				{
					continue;
				}

				$this->xml->addAttribute('tabbed', 1);
				$fieldSet->addAttribute('class', 'tab-pane active');
				$rulesSet = $this->xml->addChild('fieldset');

				$baseKey = $this->getLangKeyPrefix() . 'GROUP_PERMISSIONS';
				$this->addString($baseKey, 'Permissions');
				$this->addString($baseKey . '_DESC', 'Permissions for ' . $this->model->getContainer()->inflector->singularize($this->viewName));

				$rulesSet->addAttribute('name', 'rules');
				$rulesSet->addAttribute('class', 'tab-pane');
				$rulesSet->addAttribute('label', $baseKey);
				if ($this->addDescriptions)
				{
					$rulesSet->addAttribute('description', $baseKey . '_DESC');
				}

				$field = $rulesSet->addChild('field');
				$field->addAttribute('type', 'Hidden');
				$field->addAttribute('emptylabel', 'true');
				$field->addAttribute('filter', 'unset');
				$field->addAttribute('name', $model->getFieldAlias('asset_id'));

				$field = $rulesSet->addChild('field');
				$field->addAttribute('name', 'rules');
				$field->addAttribute('type', 'Rules');
				$field->addAttribute('emptylabel', 'true');
				$field->addAttribute('translate_label', 'false');
				$field->addAttribute('filter', 'rules');
				$field->addAttribute('validate', 'rules');
				$field->addAttribute('section', 'component');
				$field->addAttribute('component', $this->builder->getContainer()->componentName);

				continue;
			}

			// session_handler => SessionHandler
			if ($lowercaseFieldName == 'session_handler')
			{
				$this->applySessionHandlerField($model, $fieldSet, $fieldName);

				continue;
			}

			// tel, telephone, phone => Tel
			if (in_array($lowercaseFieldName, array('tel', 'telephone', 'phone')))
			{
				$this->applyTelField($model, $fieldSet, $fieldName);

				continue;
			}

			// timezone, tz, time_zone => Timezone
			if (in_array($lowercaseFieldName, array('timezone', 'tz', 'time_zone')))
			{
				$this->applyTimezoneField($model, $fieldSet, $fieldName);

				continue;
			}

			// url, link, href => Url
			if (in_array($lowercaseFieldName, array('url', 'link', 'href')))
			{
				$this->applyUrlField($model, $fieldSet, $fieldName);

				continue;
			}

			// user, user_id, userid, uid => User
			if (in_array($lowercaseFieldName, array('user', 'user_id', 'userid', 'uid')))
			{
				$this->applyUserField($model, $fieldSet, $fieldName);

				continue;
			}

			// group, group_id, groupid, gid => UserGroup
			if (in_array($lowercaseFieldName, array('group', 'group_id', 'groupid', 'gid')))
			{
				$this->applyUserGroupField($model, $fieldSet, $fieldName);

				continue;
			}

			// Special handling for myComponent_whatever_id fields
			$myComponentPrefix = $this->builder->getContainer()->bareComponentName . '_';

			if ((strpos($fieldName, $myComponentPrefix) === 0) && (substr($fieldName, -3) == '_id'))
			{
				$parts = explode('_', $fieldName);
				array_pop($parts);
				array_shift($parts);

				// myComponent_something_id => Relation or Model
				if (count($parts) == 1)
				{
					$foreignName = array_shift($parts);
				}
				// myComponent_something_another_id => Relation
				else
				{
					$foreignName1 = array_shift($parts);
					$foreignName1 = $this->model->getContainer()->inflector->pluralize($foreignName1);
					$foreignName2 = array_shift($parts);
					$foreignName2 = $this->model->getContainer()->inflector->pluralize($foreignName2);
					$modelName = $model->getName();
					$modelName = $this->model->getContainer()->inflector->pluralize($modelName);

					$foreignName = ($foreignName1 == $modelName) ? $foreignName2 : $foreignName1;
				}

				try
				{
					if (empty($parts))
					{
						throw new DataModel\Relation\Exception\RelationNotFound;
					}

					$model->getRelations()->getRelation($parts[0]);

					$this->applyRelationField($model, $fieldSet, $fieldName);

					continue;
				}
				catch (DataModel\Relation\Exception\RelationNotFound $e)
				{
					$foreignName = $this->model->getContainer()->inflector->pluralize($foreignName);

					try
					{
						$this->applyModelField($model, $fieldSet, $fieldName, $foreignName);

						continue;
					}
					catch (\Exception $e)
					{
					}
				}
			}

			// Other fields, use getFieldType
			$typeDef = $this->getFieldType($fieldDefinition->Type);
			switch ($typeDef['type'])
			{
				case 'Text':
					$this->applyTextField($model, $fieldSet, $fieldName);
					break;

				case 'Editor':
					$this->applyEditorField($model, $fieldSet, $fieldName);
					break;

				case 'Calendar':
					$this->applyCalendarField($model, $fieldSet, $fieldName);
					break;

				case 'Checkbox':
					$this->applyCheckboxField($model, $fieldSet, $fieldName);
					break;

				case 'Integer':
					$this->applyIntegerField($model, $fieldSet, $fieldName);
					break;

				case 'Number':
					$this->applyNumberField($model, $fieldSet, $fieldName);
					break;

				case 'GenericList':
					$this->applyGenericListField($model, $fieldSet, $fieldName, $typeDef['params']);
					break;
			}
		}

		$this->pushResults();
	}

	private function applyFieldOfType(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName, $fieldTypeField)
	{
		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', $fieldTypeField);
		$field->addAttribute('label', $langDefs['label']['key']);
		if ($this->addDescriptions)
		{
			$field->addAttribute('description', $langDefs['desc']['key']);
		}
	}

	/**
	 * Apply an access level field
	 *
	 * @param \FOF30\Model\DataModel $model
	 * @param \SimpleXMLElement      $headerSet
	 * @param \SimpleXMLElement      $fieldSet
	 * @param string                 $fieldName
	 */
	private function applyAccessLevelField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'AccessLevel');
	}

	private function applyPublishedField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Published');
	}

	private function applyCacheHandlerField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'CacheHandler');
	}

	private function applyCalendarField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Calendar');
	}

	private function applyCheckboxField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Checkbox');
	}

	private function applyComponentsField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Components');
	}

	private function applyEditorField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Editor');
	}

	private function applyEmailField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Email');
	}

	private function applyIntegerField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Text');
	}

	private function applyNumberField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Number');
	}

	private function applyMediaField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Media');
	}

	private function applyLanguageField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Language');
	}

	private function applyPasswordField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Password');
	}

	private function applyPluginsField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Plugins');
	}

	private function applySessionHandlerField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'SessionHandler');
	}

	private function applyTelField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Tel');
	}

	private function applyTextField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Text');
	}

	private function applyTimezoneField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Timezone');
	}

	private function applyUrlField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Url');
	}

	private function applyUserField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'User');
	}

	private function applyUserGroupField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'UserGroup');
	}

	private function applyRelationField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Relation');
	}

	private function applyTagField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName)
	{
		$this->applyFieldOfType($model, $fieldSet, $fieldName, 'Tag');
	}

	private function applyModelField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName, $modelName)
	{
		// This will fail if the model is invalid, e.g. we have example_foobar_id but no #__example_foobars table. The
		// error will balloon up the stack and the field will be rendered as simple number field instead of a Model
		// field.
		/** @var DataModel $foreignModel */
		$foreignModel = $model->getContainer()->factory->model($modelName);

		$value_field = $foreignModel->getKeyName();

		if ($foreignModel->hasField('title'))
		{
			$value_field = $foreignModel->getFieldAlias('title');
		}

		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'Model');
		$field->addAttribute('model', $modelName);
		$field->addAttribute('key_field', $foreignModel->getKeyName());
		$field->addAttribute('value_field', $value_field);
		$field->addAttribute('label', $langDefs['label']['key']);

		if ($this->addDescriptions)
		{
			$field->addAttribute('description', $langDefs['desc']['key']);
		}
	}

	private function applyGenericListField(DataModel $model, \SimpleXMLElement &$fieldSet, $fieldName, $options)
	{
		$displayOptions = array();

		foreach ($options as $k => $v)
		{
			$langKey = $this->builder->getContainer()->componentName . '_' . $this->viewName . '_' . $fieldName .
				'_OPT_' . $k;
			$this->addString($langKey, $v);
			$displayOptions[$k] = $langKey;
		}

		$langDefs = $this->getFieldLabel($fieldName);
		$this->addString($langDefs['label']['key'], $langDefs['label']['value']);
		$this->addString($langDefs['desc']['key'], $langDefs['desc']['value']);

		$field = $fieldSet->addChild('field');
		$field->addAttribute('name', $fieldName);
		$field->addAttribute('type', 'GenericList');
		$field->addAttribute('label', $langDefs['label']['key']);
		if ($this->addDescriptions)
		{
			$field->addAttribute('description', $langDefs['desc']['key']);
		}

		foreach ($displayOptions as $k => $v)
		{
			$field->addChild('option', $v)->addAttribute('value', $k);
		}
	}

	/**
	 * Create a list of fields which should not be shown in the form. These are fields like created/modified/locked
	 * user and time and other internal fields which should not be part of the form output.
	 *
	 * @return  array
	 */
	private function getDoNotShow()
	{
		$return = array();
		$checkFields = array('created_by', 'created_on', 'modified_by', 'modified_on', 'locked_by', 'locked_on');

		foreach ($checkFields as $checkField)
		{
			$return[] = $this->model->getFieldAlias($checkField);
		}

		return $return;
	}
}
ItemErector.php000064400000001147152422230060007476 0ustar00<?php
/**
 * @package     FOF
 * @copyright   Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license     GNU GPL version 2 or later
 */

namespace FOF30\Factory\Scaffolding\Layout;

defined('_JEXEC') or die;

/**
 * Erects a scaffolding XML for read views
 *
 * @package FOF30\Factory\Scaffolding
 *
 * @deprecated 3.1  Support for XML forms will be removed in FOF 4
 */
class ItemErector extends FormErector implements ErectorInterface
{
	public function build()
	{
		$this->addDescriptions = false;

		parent::build();

		$this->xml->addAttribute('type', 'read');

		$this->pushResults();
	}
}