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/com_finder.zip

PK��[�.����controller.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Base controller class for Finder.
 *
 * @since  2.5
 */
class FinderController extends JControllerLegacy
{
	/**
	 * The default view.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $default_view = 'index';

	/**
	 * Method to display a view.
	 *
	 * @param   boolean  $cachable   If true, the view output will be cached
	 * @param   array    $urlparams  An array of safe URL parameters and their
variable types, for valid values see {@link JFilterInput::clean()}.
	 *
	 * @return  FinderController  A JControllerLegacy object to support
chaining.
	 *
	 * @since	2.5
	 */
	public function display($cachable = false, $urlparams = array())
	{
		JLoader::register('FinderHelper', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/finder.php');

		$view   = $this->input->get('view', 'index',
'word');
		$layout = $this->input->get('layout', 'index',
'word');
		$filterId = $this->input->get('filter_id', null,
'int');

		// Check for edit form.
		if ($view === 'filter' && $layout === 'edit'
&& !$this->checkEditId('com_finder.edit.filter',
$filterId))
		{
			// Somehow the person just went to the form - we don't allow that.
			$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID',
$filterId));
			$this->setMessage($this->getError(), 'error');
			$this->setRedirect(JRoute::_('index.php?option=com_finder&view=filters',
false));

			return false;
		}

		return parent::display();
	}
}
PK��[�d�	�	
controllers/suggestions.json.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Suggestions JSON controller for Finder.
 *
 * @since  2.5
 */
class FinderControllerSuggestions extends JControllerLegacy
{
	/**
	 * Method to find search query suggestions. Uses jQuery and
autocompleter.js
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function suggest()
	{
		/** @var \Joomla\CMS\Application\CMSApplication $app */
		$app = JFactory::getApplication();
		$app->mimeType = 'application/json';

		// Ensure caching is disabled as it depends on the query param in the
model
		$app->allowCache(false);

		$suggestions = $this->getSuggestions();

		// Send the response.
		$app->setHeader('Content-Type', $app->mimeType . ';
charset=' . $app->charSet);
		$app->sendHeaders();
		echo '{ "suggestions": ' . json_encode($suggestions)
. ' }';
		$app->close();
	}

	/**
	 * Method to find search query suggestions. Uses Mootools and
autocompleter.js
	 *
	 * @param   boolean  $cachable   If true, the view output will be cached
	 * @param   array    $urlparams  An array of safe URL parameters and their
variable types, for valid values see {@link JFilterInput::clean()}.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 * @deprecated 3.4
	 */
	public function display($cachable = false, $urlparams = false)
	{
		/** @var \Joomla\CMS\Application\CMSApplication $app */
		$app = JFactory::getApplication();
		$app->mimeType = 'application/json';

		// Ensure caching is disabled as it depends on the query param in the
model
		$app->allowCache(false);

		$suggestions = $this->getSuggestions();

		// Send the response.
		$app->setHeader('Content-Type', $app->mimeType . ';
charset=' . $app->charSet);
		$app->sendHeaders();
		echo json_encode($suggestions);
		$app->close();
	}

	/**
	 * Method to retrieve the data from the database
	 *
	 * @return  array  The suggested words
	 *
	 * @since   3.4
	 */
	protected function getSuggestions()
	{
		$return = array();

		$params = JComponentHelper::getParams('com_finder');

		if ($params->get('show_autosuggest', 1))
		{
			// Get the suggestions.
			$model = $this->getModel('Suggestions',
'FinderModel');
			$return = $model->getItems();
		}

		// Check the data.
		if (empty($return))
		{
			$return = array();
		}

		return $return;
	}
}
PK��[����AA
finder.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

if (!JFactory::getUser()->authorise('core.manage',
'com_finder'))
{
	throw new
JAccessExceptionNotallowed(JText::_('JERROR_ALERTNOAUTHOR'),
403);
}

$controller = JControllerLegacy::getInstance('Finder');
$controller->execute(JFactory::getApplication()->input->get('task'));
$controller->redirect();
PK��[��B��7�7helpers/html/filter.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

/**
 * Filter HTML Behaviors for Finder.
 *
 * @since  2.5
 */
abstract class JHtmlFilter
{
	/**
	 * Method to generate filters using the slider widget and decorated
	 * with the FinderFilter JavaScript behaviors.
	 *
	 * @param   array  $options  An array of configuration options. [optional]
	 *
	 * @return  mixed  A rendered HTML widget on success, null otherwise.
	 *
	 * @since   2.5
	 */
	public static function slider($options = array())
	{
		$db     = JFactory::getDbo();
		$query  = $db->getQuery(true);
		$user   = JFactory::getUser();
		$groups = implode(',', $user->getAuthorisedViewLevels());
		$html   = '';
		$filter = null;

		// Get the configuration options.
		$filterId    = array_key_exists('filter_id', $options) ?
$options['filter_id'] : null;
		$activeNodes = array_key_exists('selected_nodes', $options) ?
$options['selected_nodes'] : array();
		$classSuffix = array_key_exists('class_suffix', $options) ?
$options['class_suffix'] : '';

		// Load the predefined filter if specified.
		if (!empty($filterId))
		{
			$query->select('f.data, f.params')
				->from($db->quoteName('#__finder_filters') . ' AS
f')
				->where('f.filter_id = ' . (int) $filterId);

			// Load the filter data.
			$db->setQuery($query);

			try
			{
				$filter = $db->loadObject();
			}
			catch (RuntimeException $e)
			{
				return null;
			}

			// Initialize the filter parameters.
			if ($filter)
			{
				$filter->params = new Registry($filter->params);
			}
		}

		// Build the query to get the branch data and the number of child nodes.
		$query->clear()
			->select('t.*, count(c.id) AS children')
			->from($db->quoteName('#__finder_taxonomy') . ' AS
t')
			->join('INNER',
$db->quoteName('#__finder_taxonomy') . ' AS c ON
c.parent_id = t.id')
			->where('t.parent_id = 1')
			->where('t.state = 1')
			->where('t.access IN (' . $groups . ')')
			->group('t.id, t.parent_id, t.state, t.access, t.ordering,
t.title, c.parent_id')
			->order('t.ordering, t.title');

		// Limit the branch children to a predefined filter.
		if ($filter)
		{
			$query->where('c.id IN(' . $filter->data .
')');
		}

		// Load the branches.
		$db->setQuery($query);

		try
		{
			$branches = $db->loadObjectList('id');
		}
		catch (RuntimeException $e)
		{
			return null;
		}

		// Check that we have at least one branch.
		if (count($branches) === 0)
		{
			return null;
		}

		$branch_keys = array_keys($branches);
		$html .= JHtml::_('bootstrap.startAccordion',
'accordion', array('parent' => true,
'active' => 'accordion-' . $branch_keys[0])
		);

		// Load plugin language files.
		FinderHelperLanguage::loadPluginLanguage();

		// Iterate through the branches and build the branch groups.
		foreach ($branches as $bk => $bv)
		{
			// If the multi-lang plugin is enabled then drop the language branch.
			if ($bv->title === 'Language' &&
JLanguageMultilang::isEnabled())
			{
				continue;
			}

			// Build the query to get the child nodes for this branch.
			$query->clear()
				->select('t.*')
				->from($db->quoteName('#__finder_taxonomy') . ' AS
t')
				->where('t.parent_id = ' . (int) $bk)
				->where('t.state = 1')
				->where('t.access IN (' . $groups . ')')
				->order('t.ordering, t.title');

			// Self-join to get the parent title.
			$query->select('e.title AS parent_title')
				->join('LEFT',
$db->quoteName('#__finder_taxonomy', 'e') . '
ON ' . $db->quoteName('e.id') . ' = ' .
$db->quoteName('t.parent_id'));

			// Load the branches.
			$db->setQuery($query);

			try
			{
				$nodes = $db->loadObjectList('id');
			}
			catch (RuntimeException $e)
			{
				return null;
			}

			// Translate node titles if possible.
			$lang = JFactory::getLanguage();

			foreach ($nodes as $nk => $nv)
			{
				if (trim($nv->parent_title, '**') ===
'Language')
				{
					$title = FinderHelperLanguage::branchLanguageTitle($nv->title);
				}
				else
				{
					$key = FinderHelperLanguage::branchPlural($nv->title);
					$title = $lang->hasKey($key) ? JText::_($key) : $nv->title;
				}

				$nodes[$nk]->title = $title;
			}

			// Adding slides
			$html .= JHtml::_('bootstrap.addSlide',
				'accordion',
				JText::sprintf('COM_FINDER_FILTER_BRANCH_LABEL',
					JText::_(FinderHelperLanguage::branchSingular($bv->title)) . '
- ' . count($nodes)
				),
				'accordion-' . $bk
			);

			// Populate the toggle button.
			$html .= '<button class="btn jform-rightbtn"
type="button"
onclick="jQuery(\'[id=&quot;tax-'
				. $bk .
'&quot;]\').each(function(){this.click();});"><span
class="icon-checkbox-partial"></span> '
				. JText::_('JGLOBAL_SELECTION_INVERT') .
'</button><hr/>';

			// Populate the group with nodes.
			foreach ($nodes as $nk => $nv)
			{
				// Determine if the node should be checked.
				$checked = in_array($nk, $activeNodes) ? '
checked="checked"' : '';

				// Build a node.
				$html .= '<div class="control-group">';
				$html .= '<div class="controls">';
				$html .= '<label class="checkbox">';
				$html .= '<input type="checkbox" class="selector
filter-node' . $classSuffix . '" value="' . $nk .
'" name="t[]" id="tax-'
					. $bk . '"' . $checked . ' />';
				$html .= $nv->title;
				$html .= '</label>';
				$html .= '</div>';
				$html .= '</div>';
			}

			$html .= JHtml::_('bootstrap.endSlide');
		}

		$html .= JHtml::_('bootstrap.endAccordion');

		return $html;
	}

	/**
	 * Method to generate filters using select box dropdown controls.
	 *
	 * @param   FinderIndexerQuery  $idxQuery  A FinderIndexerQuery object.
	 * @param   array               $options   An array of options.
	 *
	 * @return  mixed  A rendered HTML widget on success, null otherwise.
	 *
	 * @since   2.5
	 */
	public static function select($idxQuery, $options)
	{
		$user   = JFactory::getUser();
		$groups = implode(',', $user->getAuthorisedViewLevels());
		$filter = null;

		// Get the configuration options.
		$classSuffix = $options->get('class_suffix', null);
		$showDates   = $options->get('show_date_filters', false);

		// Try to load the results from cache.
		$cache   = JFactory::getCache('com_finder', '');
		$cacheId = 'filter_select_' .
serialize(array($idxQuery->filter, $options, $groups,
JFactory::getLanguage()->getTag()));

		// Check the cached results.
		if ($cache->contains($cacheId))
		{
			$branches = $cache->get($cacheId);
		}
		else
		{
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);

			// Load the predefined filter if specified.
			if (!empty($idxQuery->filter))
			{
				$query->select('f.data, ' .
$db->quoteName('f.params'))
					->from($db->quoteName('#__finder_filters') . ' AS
f')
					->where('f.filter_id = ' . (int) $idxQuery->filter);

				// Load the filter data.
				$db->setQuery($query);

				try
				{
					$filter = $db->loadObject();
				}
				catch (RuntimeException $e)
				{
					return null;
				}

				// Initialize the filter parameters.
				if ($filter)
				{
					$filter->params = new Registry($filter->params);
				}
			}

			// Build the query to get the branch data and the number of child nodes.
			$query->clear()
				->select('t.*, count(c.id) AS children')
				->from($db->quoteName('#__finder_taxonomy') . ' AS
t')
				->join('INNER',
$db->quoteName('#__finder_taxonomy') . ' AS c ON
c.parent_id = t.id')
				->where('t.parent_id = 1')
				->where('t.state = 1')
				->where('t.access IN (' . $groups . ')')
				->where('c.state = 1')
				->where('c.access IN (' . $groups . ')')
				->group($db->quoteName('t.id'))
				->order('t.ordering, t.title');

			// Limit the branch children to a predefined filter.
			if (!empty($filter->data))
			{
				$query->where('c.id IN(' . $filter->data .
')');
			}

			// Load the branches.
			$db->setQuery($query);

			try
			{
				$branches = $db->loadObjectList('id');
			}
			catch (RuntimeException $e)
			{
				return null;
			}

			// Check that we have at least one branch.
			if (count($branches) === 0)
			{
				return null;
			}

			// Iterate through the branches and build the branch groups.
			foreach ($branches as $bk => $bv)
			{
				// If the multi-lang plugin is enabled then drop the language branch.
				if ($bv->title === 'Language' &&
JLanguageMultilang::isEnabled())
				{
					continue;
				}

				// Build the query to get the child nodes for this branch.
				$query->clear()
					->select('t.*')
					->from($db->quoteName('#__finder_taxonomy') . '
AS t')
					->where('t.parent_id = ' . (int) $bk)
					->where('t.state = 1')
					->where('t.access IN (' . $groups . ')')
					->order('t.ordering, t.title');

				// Self-join to get the parent title.
				$query->select('e.title AS parent_title')
					->join('LEFT',
$db->quoteName('#__finder_taxonomy', 'e') . '
ON ' . $db->quoteName('e.id') . ' = ' .
$db->quoteName('t.parent_id'));

				// Limit the nodes to a predefined filter.
				if (!empty($filter->data))
				{
					$query->where('t.id IN(' . $filter->data .
')');
				}

				// Load the branches.
				$db->setQuery($query);

				try
				{
					$branches[$bk]->nodes = $db->loadObjectList('id');
				}
				catch (RuntimeException $e)
				{
					return null;
				}

				// Translate branch nodes if possible.
				$language = JFactory::getLanguage();

				foreach ($branches[$bk]->nodes as $node_id => $node)
				{
					if (trim($node->parent_title, '**') ===
'Language')
					{
						$title = FinderHelperLanguage::branchLanguageTitle($node->title);
					}
					else
					{
						$key = FinderHelperLanguage::branchPlural($node->title);
						$title = $language->hasKey($key) ? JText::_($key) :
$node->title;
					}

					$branches[$bk]->nodes[$node_id]->title = $title;
				}

				// Add the Search All option to the branch.
				array_unshift($branches[$bk]->nodes, array('id' =>
null, 'title' =>
JText::_('COM_FINDER_FILTER_SELECT_ALL_LABEL')));
			}

			// Store the data in cache.
			$cache->store($branches, $cacheId);
		}

		$html = '';

		// Add the dates if enabled.
		if ($showDates)
		{
			$html .= JHtml::_('filter.dates', $idxQuery, $options);
		}

		$html .= '<div class="filter-branch' . $classSuffix .
' control-group clearfix">';

		// Iterate through all branches and build code.
		foreach ($branches as $bk => $bv)
		{
			// If the multi-lang plugin is enabled then drop the language branch.
			if ($bv->title === 'Language' &&
JLanguageMultilang::isEnabled())
			{
				continue;
			}

			$active = null;

			// Check if the branch is in the filter.
			if (array_key_exists($bv->title, $idxQuery->filters))
			{
				// Get the request filters.
				$temp   =
JFactory::getApplication()->input->request->get('t',
array(), 'array');

				// Search for active nodes in the branch and get the active node.
				$active = array_intersect($temp, $idxQuery->filters[$bv->title]);
				$active = count($active) === 1 ? array_shift($active) : null;
			}

			// Build a node.
			$html .= '<div class="controls
finder-selects">';
			$html .= '<label for="tax-' .
JFilterOutput::stringURLSafe($bv->title) . '"
class="control-label">';
			$html .= JText::sprintf('COM_FINDER_FILTER_BRANCH_LABEL',
JText::_(FinderHelperLanguage::branchSingular($bv->title)));
			$html .= '</label>';
			$html .= '<br />';
			$html .= JHtml::_(
				'select.genericlist',
				$branches[$bk]->nodes, 't[]', 'class="inputbox
advancedSelect"', 'id', 'title', $active,
				'tax-' . JFilterOutput::stringURLSafe($bv->title)
			);
			$html .= '</div>';
		}

		$html .= '</div>';

		return $html;
	}

	/**
	 * Method to generate fields for filtering dates
	 *
	 * @param   FinderIndexerQuery  $idxQuery  A FinderIndexerQuery object.
	 * @param   array               $options   An array of options.
	 *
	 * @return  mixed  A rendered HTML widget on success, null otherwise.
	 *
	 * @since   2.5
	 */
	public static function dates($idxQuery, $options)
	{
		$html = '';

		// Get the configuration options.
		$classSuffix = $options->get('class_suffix', null);
		$loadMedia   = $options->get('load_media', true);
		$showDates   = $options->get('show_date_filters', false);

		if (!empty($showDates))
		{
			// Build the date operators options.
			$operators   = array();
			$operators[] = JHtml::_('select.option', 'before',
JText::_('COM_FINDER_FILTER_DATE_BEFORE'));
			$operators[] = JHtml::_('select.option', 'exact',
JText::_('COM_FINDER_FILTER_DATE_EXACTLY'));
			$operators[] = JHtml::_('select.option', 'after',
JText::_('COM_FINDER_FILTER_DATE_AFTER'));

			// Load the CSS/JS resources.
			if ($loadMedia)
			{
				JHtml::_('stylesheet', 'com_finder/dates.css',
array('version' => 'auto', 'relative'
=> true));
			}

			// Open the widget.
			$html .= '<ul
id="finder-filter-select-dates">';

			// Start date filter.
			$attribs['class'] = 'input-medium';
			$html .= '<li class="filter-date' . $classSuffix .
'">';
			$html .= '<label for="filter_date1"
class="hasTooltip" title ="' .
JText::_('COM_FINDER_FILTER_DATE1_DESC') .
'">';
			$html .= JText::_('COM_FINDER_FILTER_DATE1');
			$html .= '</label>';
			$html .= '<br />';
			$html .= JHtml::_(
				'select.genericlist',
				$operators, 'w1', 'class="inputbox
filter-date-operator advancedSelect"', 'value',
'text', $idxQuery->when1, 'finder-filter-w1'
			);
			$html .= JHtml::_('calendar', $idxQuery->date1,
'd1', 'filter_date1', '%Y-%m-%d', $attribs);
			$html .= '</li>';

			// End date filter.
			$html .= '<li class="filter-date' . $classSuffix .
'">';
			$html .= '<label for="filter_date2"
class="hasTooltip" title ="' .
JText::_('COM_FINDER_FILTER_DATE2_DESC') .
'">';
			$html .= JText::_('COM_FINDER_FILTER_DATE2');
			$html .= '</label>';
			$html .= '<br />';
			$html .= JHtml::_(
				'select.genericlist',
				$operators, 'w2', 'class="inputbox
filter-date-operator advancedSelect"', 'value',
'text', $idxQuery->when2, 'finder-filter-w2'
			);
			$html .= JHtml::_('calendar', $idxQuery->date2,
'd2', 'filter_date2', '%Y-%m-%d', $attribs);
			$html .= '</li>';

			// Close the widget.
			$html .= '</ul>';
		}

		return $html;
	}
}
PK��[��f��helpers/html/query.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Query HTML behavior class for Finder.
 *
 * @since  2.5
 */
abstract class JHtmlQuery
{
	/**
	 * Method to get the explained (human-readable) search query.
	 *
	 * @param   FinderIndexerQuery  $query  A FinderIndexerQuery object to
explain.
	 *
	 * @return  mixed  String if there is data to explain, null otherwise.
	 *
	 * @since   2.5
	 */
	public static function explained(FinderIndexerQuery $query)
	{
		$parts = array();

		// Process the required tokens.
		foreach ($query->included as $token)
		{
			if ($token->required && (!isset($token->derived) ||
$token->derived == false))
			{
				$parts[] = '<span class="query-required">' .
JText::sprintf('COM_FINDER_QUERY_TOKEN_REQUIRED',
$token->term) . '</span>';
			}
		}

		// Process the optional tokens.
		foreach ($query->included as $token)
		{
			if (!$token->required && (!isset($token->derived) ||
$token->derived == false))
			{
				$parts[] = '<span class="query-optional">' .
JText::sprintf('COM_FINDER_QUERY_TOKEN_OPTIONAL',
$token->term) . '</span>';
			}
		}

		// Process the excluded tokens.
		foreach ($query->excluded as $token)
		{
			if (!isset($token->derived) || $token->derived === false)
			{
				$parts[] = '<span class="query-excluded">' .
JText::sprintf('COM_FINDER_QUERY_TOKEN_EXCLUDED',
$token->term) . '</span>';
			}
		}

		// Process the start date.
		if ($query->date1)
		{
			$date =
JFactory::getDate($query->date1)->format(JText::_('DATE_FORMAT_LC'));
			$datecondition = JText::_('COM_FINDER_QUERY_DATE_CONDITION_' .
strtoupper($query->when1));
			$parts[] = '<span class="query-start-date">' .
JText::sprintf('COM_FINDER_QUERY_START_DATE', $datecondition,
$date) . '</span>';
		}

		// Process the end date.
		if ($query->date2)
		{
			$date =
JFactory::getDate($query->date2)->format(JText::_('DATE_FORMAT_LC'));
			$datecondition = JText::_('COM_FINDER_QUERY_DATE_CONDITION_' .
strtoupper($query->when2));
			$parts[] = '<span class="query-end-date">' .
JText::sprintf('COM_FINDER_QUERY_END_DATE', $datecondition,
$date) . '</span>';
		}

		// Process the taxonomy filters.
		if (!empty($query->filters))
		{
			// Get the filters in the request.
			$t =
JFactory::getApplication()->input->request->get('t',
array(), 'array');

			// Process the taxonomy branches.
			foreach ($query->filters as $branch => $nodes)
			{
				// Process the taxonomy nodes.
				$lang = JFactory::getLanguage();

				foreach ($nodes as $title => $id)
				{
					// Translate the title for Types
					$key = FinderHelperLanguage::branchPlural($title);

					if ($lang->hasKey($key))
					{
						$title = JText::_($key);
					}

					// Don't include the node if it is not in the request.
					if (!in_array($id, $t))
					{
						continue;
					}

					// Add the node to the explanation.
					$parts[] = '<span class="query-taxonomy">'
						. JText::sprintf('COM_FINDER_QUERY_TAXONOMY_NODE', $title,
JText::_(FinderHelperLanguage::branchSingular($branch)))
						. '</span>';
				}
			}
		}

		// Build the interpreted query.
		return count($parts) ?
JText::sprintf('COM_FINDER_QUERY_TOKEN_INTERPRETED',
implode(JText::_('COM_FINDER_QUERY_TOKEN_GLUE'), $parts)) : null;
	}

	/**
	 * Method to get the suggested search query.
	 *
	 * @param   FinderIndexerQuery  $query  A FinderIndexerQuery object.
	 *
	 * @return  mixed  String if there is a suggestion, false otherwise.
	 *
	 * @since   2.5
	 */
	public static function suggested(FinderIndexerQuery $query)
	{
		$suggested = false;

		// Check if the query input is empty.
		if (empty($query->input))
		{
			return $suggested;
		}

		// Check if there were any ignored or included keywords.
		if (count($query->ignored) || count($query->included))
		{
			$suggested = $query->input;

			// Replace the ignored keyword suggestions.
			foreach (array_reverse($query->ignored) as $token)
			{
				if (isset($token->suggestion))
				{
					$suggested = str_ireplace($token->term, $token->suggestion,
$suggested);
				}
			}

			// Replace the included keyword suggestions.
			foreach (array_reverse($query->included) as $token)
			{
				if (isset($token->suggestion))
				{
					$suggested = str_ireplace($token->term, $token->suggestion,
$suggested);
				}
			}

			// Check if we made any changes.
			if ($suggested == $query->input)
			{
				$suggested = false;
			}
		}

		return $suggested;
	}
}
PK��[�(���helpers/route.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 *
 * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
 */

use Joomla\Component\Finder\Site\Helper\RouteHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Finder route helper class.
 *
 * @since  2.5
 *
 * @deprecated  4.3 will be removed in 6.0
 *              Use \Joomla\Component\Finder\Site\Helper\RouteHelper
instead
 */
class FinderHelperRoute extends RouteHelper
{
}
PK��[�Jڄڄmodels/search.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;

// Register dependent classes.
define('FINDER_PATH_INDEXER', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/indexer');
JLoader::register('FinderIndexerHelper', FINDER_PATH_INDEXER .
'/helper.php');
JLoader::register('FinderIndexerQuery', FINDER_PATH_INDEXER .
'/query.php');
JLoader::register('FinderIndexerResult', FINDER_PATH_INDEXER .
'/result.php');
JLoader::register('FinderIndexerStemmer', FINDER_PATH_INDEXER .
'/stemmer.php');

/**
 * Search model class for the Finder package.
 *
 * @since  2.5
 */
class FinderModelSearch extends JModelList
{
	/**
	 * Context string for the model type
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $context = 'com_finder.search';

	/**
	 * The query object is an instance of FinderIndexerQuery which contains
and
	 * models the entire search query including the text input; static and
	 * dynamic taxonomy filters; date filters; etc.
	 *
	 * @var    FinderIndexerQuery
	 * @since  2.5
	 */
	protected $query;

	/**
	 * An array of all excluded terms ids.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $excludedTerms = array();

	/**
	 * An array of all included terms ids.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $includedTerms = array();

	/**
	 * An array of all required terms ids.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $requiredTerms = array();

	/**
	 * Method to get the results of the query.
	 *
	 * @return  array  An array of FinderIndexerResult objects.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public function getResults()
	{
		// Check if the search query is valid.
		if (empty($this->query->search))
		{
			return null;
		}

		// Check if we should return results.
		if (empty($this->includedTerms) &&
(empty($this->query->filters) || !$this->query->empty))
		{
			return null;
		}

		// Get the store id.
		$store = $this->getStoreId('getResults');

		// Use the cached data if possible.
		if ($this->retrieve($store))
		{
			return $this->retrieve($store);
		}

		// Get the row data.
		$items = $this->getResultsData();

		// Check the data.
		if (empty($items))
		{
			return null;
		}

		// Create the query to get the search results.
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->select($db->quoteName('link_id') . ', ' .
$db->quoteName('object'))
			->from($db->quoteName('#__finder_links'))
			->where($db->quoteName('link_id') . ' IN (' .
implode(',', array_keys($items)) . ')');

		// Load the results from the database.
		$db->setQuery($query);
		$rows = $db->loadObjectList('link_id');

		// Set up our results container.
		$results = $items;

		// Convert the rows to result objects.
		foreach ($rows as $rk => $row)
		{
			// Build the result object.
			$result = unserialize($row->object);
			$result->weight = $results[$rk];
			$result->link_id = $rk;

			// Add the result back to the stack.
			$results[$rk] = $result;
		}

		// Switch to a non-associative array.
		$results = array_values($results);

		// Push the results into cache.
		$this->store($store, $results);

		// Return the results.
		return $this->retrieve($store);
	}

	/**
	 * Method to get the total number of results.
	 *
	 * @return  integer  The total number of results.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public function getTotal()
	{
		// Check if the search query is valid.
		if (empty($this->query->search))
		{
			return null;
		}

		// Check if we should return results.
		if (empty($this->includedTerms) &&
(empty($this->query->filters) || !$this->query->empty))
		{
			return null;
		}

		// Get the store id.
		$store = $this->getStoreId('getTotal');

		// Use the cached data if possible.
		if ($this->retrieve($store))
		{
			return $this->retrieve($store);
		}

		// Get the results total.
		$total = $this->getResultsTotal();

		// Push the total into cache.
		$this->store($store, $total);

		// Return the total.
		return $this->retrieve($store);
	}

	/**
	 * Method to get the query object.
	 *
	 * @return  FinderIndexerQuery  A query object.
	 *
	 * @since   2.5
	 */
	public function getQuery()
	{
		// Return the query object.
		return $this->query;
	}

	/**
	 * Method to build a database query to load the list data.
	 *
	 * @return  JDatabaseQuery  A database query.
	 *
	 * @since   2.5
	 */
	protected function getListQuery()
	{
		// Get the store id.
		$store = $this->getStoreId('getListQuery');

		// Use the cached data if possible.
		if ($this->retrieve($store, false))
		{
			return clone $this->retrieve($store, false);
		}

		// Set variables
		$user = JFactory::getUser();
		$groups = implode(',', $user->getAuthorisedViewLevels());

		// Create a new query object.
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->select('l.link_id')
			->from($db->quoteName('#__finder_links') . ' AS
l')
			->where('l.access IN (' . $groups . ')')
			->where('l.state = 1')
			->where('l.published = 1');

		// Get the null date and the current date, minus seconds.
		$nullDate = $db->quote($db->getNullDate());
		$nowDate = $db->quote(substr_replace(JFactory::getDate()->toSql(),
'00', -2));

		// Add the publish up and publish down filters.
		$query->where('(l.publish_start_date = ' . $nullDate .
' OR l.publish_start_date <= ' . $nowDate . ')')
			->where('(l.publish_end_date = ' . $nullDate . ' OR
l.publish_end_date >= ' . $nowDate . ')');

		/*
		 * Add the taxonomy filters to the query. We have to join the taxonomy
		 * map table for each group so that we can use AND clauses across
		 * groups. Within each group there can be an array of values that will
		 * use OR clauses.
		 */
		if (!empty($this->query->filters))
		{
			// Convert the associative array to a numerically indexed array.
			$groups = array_values($this->query->filters);

			// Iterate through each taxonomy group and add the join and where.
			for ($i = 0, $c = count($groups); $i < $c; $i++)
			{
				// We use the offset because each join needs a unique alias.
				$query->join('INNER',
$db->quoteName('#__finder_taxonomy_map') . ' AS t' .
$i . ' ON t' . $i . '.link_id = l.link_id')
					->where('t' . $i . '.node_id IN (' .
implode(',', $groups[$i]) . ')');
			}
		}

		// Add the start date filter to the query.
		if (!empty($this->query->date1))
		{
			// Escape the date.
			$date1 = $db->quote($this->query->date1);

			// Add the appropriate WHERE condition.
			if ($this->query->when1 === 'before')
			{
				$query->where($db->quoteName('l.start_date') . '
<= ' . $date1);
			}
			elseif ($this->query->when1 === 'after')
			{
				$query->where($db->quoteName('l.start_date') . '
>= ' . $date1);
			}
			else
			{
				$query->where($db->quoteName('l.start_date') . ' =
' . $date1);
			}
		}

		// Add the end date filter to the query.
		if (!empty($this->query->date2))
		{
			// Escape the date.
			$date2 = $db->quote($this->query->date2);

			// Add the appropriate WHERE condition.
			if ($this->query->when2 === 'before')
			{
				$query->where($db->quoteName('l.start_date') . '
<= ' . $date2);
			}
			elseif ($this->query->when2 === 'after')
			{
				$query->where($db->quoteName('l.start_date') . '
>= ' . $date2);
			}
			else
			{
				$query->where($db->quoteName('l.start_date') . ' =
' . $date2);
			}
		}

		// Filter by language
		if ($this->getState('filter.language'))
		{
			$query->where('l.language IN (' .
$db->quote(JFactory::getLanguage()->getTag()) . ', ' .
$db->quote('*') . ')');
		}

		// Push the data into cache.
		$this->store($store, $query, false);

		// Return a copy of the query object.
		return clone $this->retrieve($store, false);
	}

	/**
	 * Method to get the total number of results for the search query.
	 *
	 * @return  integer  The results total.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getResultsTotal()
	{
		// Get the store id.
		$store = $this->getStoreId('getResultsTotal', false);

		// Use the cached data if possible.
		if ($this->retrieve($store))
		{
			return $this->retrieve($store);
		}

		// Get the base query and add the ordering information.
		$base = $this->getListQuery();
		$base->select('0 AS ordering');

		// Get the maximum number of results.
		$limit = (int) $this->getState('match.limit');

		/*
		 * If there are no optional or required search terms in the query,
		 * we can get the result total in one relatively simple database query.
		 */
		if (empty($this->includedTerms))
		{
			// Adjust the query to join on the appropriate mapping table.
			$query = clone $base;
			$query->clear('select')
				->select('COUNT(DISTINCT l.link_id)');

			// Get the total from the database.
			$this->_db->setQuery($query);
			$total = $this->_db->loadResult();

			// Push the total into cache.
			$this->store($store, min($total, $limit));

			// Return the total.
			return $this->retrieve($store);
		}

		/*
		 * If there are optional or required search terms in the query, the
		 * process of getting the result total is more complicated.
		 */
		$start = 0;
		$items = array();
		$sorted = array();
		$maps = array();
		$excluded = $this->getExcludedLinkIds();

		/*
		 * Iterate through the included search terms and group them by mapping
		 * table suffix. This ensures that we never have to do more than 16
		 * queries to get a batch. This may seem like a lot but it is rarely
		 * anywhere near 16 because of the improved mapping algorithm.
		 */
		foreach ($this->includedTerms as $token => $ids)
		{
			// Get the mapping table suffix.
			$suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)),
0, 1);

			// Initialize the mapping group.
			if (!array_key_exists($suffix, $maps))
			{
				$maps[$suffix] = array();
			}

			// Add the terms to the mapping group.
			$maps[$suffix] = array_merge($maps[$suffix], $ids);
		}

		/*
		 * When the query contains search terms we need to find and process the
		 * result total iteratively using a do-while loop.
		 */
		do
		{
			// Create a container for the fetched results.
			$results = array();
			$more = false;

			/*
			 * Iterate through the mapping groups and load the total from each
			 * mapping table.
			 */
			foreach ($maps as $suffix => $ids)
			{
				// Create a storage key for this set.
				$setId = $this->getStoreId('getResultsTotal:' .
serialize(array_values($ids)) . ':' . $start . ':' .
$limit);

				// Use the cached data if possible.
				if ($this->retrieve($setId))
				{
					$temp = $this->retrieve($setId);
				}
				// Load the data from the database.
				else
				{
					// Adjust the query to join on the appropriate mapping table.
					$query = clone $base;
					$query->join('INNER', '#__finder_links_terms' .
$suffix . ' AS m ON m.link_id = l.link_id')
						->where('m.term_id IN (' . implode(',', $ids)
. ')');

					// Load the results from the database.
					$this->_db->setQuery($query, $start, $limit);
					$temp = $this->_db->loadObjectList();

					// Set the more flag to true if any of the sets equal the limit.
					$more = count($temp) === $limit;

					// We loaded the data unkeyed but we need it to be keyed for later.
					$junk = $temp;
					$temp = array();

					// Convert to an associative array.
					for ($i = 0, $c = count($junk); $i < $c; $i++)
					{
						$temp[$junk[$i]->link_id] = $junk[$i];
					}

					// Store this set in cache.
					$this->store($setId, $temp);
				}

				// Merge the results.
				$results = array_merge($results, $temp);
			}

			// Check if there are any excluded terms to deal with.
			if (count($excluded))
			{
				// Remove any results that match excluded terms.
				for ($i = 0, $c = count($results); $i < $c; $i++)
				{
					if (in_array($results[$i]->link_id, $excluded))
					{
						unset($results[$i]);
					}
				}

				// Reset the array keys.
				$results = array_values($results);
			}

			// Iterate through the set to extract the unique items.
			for ($i = 0, $c = count($results); $i < $c; $i++)
			{
				if (!isset($sorted[$results[$i]->link_id]))
				{
					$sorted[$results[$i]->link_id] = $results[$i]->ordering;
				}
			}

			/*
			 * If the query contains just optional search terms and we have
			 * enough items for the page, we can stop here.
			 */
			if (empty($this->requiredTerms))
			{
				// If we need more items and they're available, make another pass.
				if ($more && count($sorted) < $limit)
				{
					// Increment the batch starting point and continue.
					$start += $limit;
					continue;
				}

				// Push the total into cache.
				$this->store($store, min(count($sorted), $limit));

				// Return the total.
				return $this->retrieve($store);
			}

			/*
			 * The query contains required search terms so we have to iterate
			 * over the items and remove any items that do not match all of the
			 * required search terms. This is one of the most expensive steps
			 * because a required token could theoretically eliminate all of
			 * current terms which means we would have to loop through all of
			 * the possibilities.
			 */
			foreach ($this->requiredTerms as $token => $required)
			{
				// Create a storage key for this set.
				$setId = $this->getStoreId('getResultsTotal:required:' .
serialize(array_values($required)) . ':' . $start . ':'
. $limit);

				// Use the cached data if possible.
				if ($this->retrieve($setId))
				{
					$reqTemp = $this->retrieve($setId);
				}
					// Check if the token was matched.
				elseif (empty($required))
				{
					return null;
				}
					// Load the data from the database.
				else
				{
					// Setup containers in case we have to make multiple passes.
					$reqStart = 0;
					$reqTemp = array();

					do
					{
						// Get the map table suffix.
						$suffix = StringHelper::substr(md5(StringHelper::substr($token, 0,
1)), 0, 1);

						// Adjust the query to join on the appropriate mapping table.
						$query = clone $base;
						$query->join('INNER', '#__finder_links_terms'
. $suffix . ' AS m ON m.link_id = l.link_id')
							->where('m.term_id IN (' . implode(',',
$required) . ')');

						// Load the results from the database.
						$this->_db->setQuery($query, $reqStart, $limit);
						$temp = $this->_db->loadObjectList('link_id');

						// Set the required token more flag to true if the set equal the
limit.
						$reqMore = count($temp) === $limit;

						// Merge the matching set for this token.
						$reqTemp += $temp;

						// Increment the term offset.
						$reqStart += $limit;
					}
					while ($reqMore === true);

					// Store this set in cache.
					$this->store($setId, $reqTemp);
				}

				// Remove any items that do not match the required term.
				$sorted = array_intersect_key($sorted, $reqTemp);
			}

			// If we need more items and they're available, make another pass.
			if ($more && count($sorted) < $limit)
			{
				// Increment the batch starting point.
				$start += $limit;

				// Merge the found items.
				$items += $sorted;

				continue;
			}

			// Otherwise, end the loop.
			{
				// Merge the found items.
				$items += $sorted;

				$more = false;
			}
			// End do-while loop.
		}
		while ($more === true);

		// Set the total.
		$total = count($items);
		$total = min($total, $limit);

		// Push the total into cache.
		$this->store($store, $total);

		// Return the total.
		return $this->retrieve($store);
	}

	/**
	 * Method to get the results for the search query.
	 *
	 * @return  array  An array of result data objects.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getResultsData()
	{
		// Get the store id.
		$store = $this->getStoreId('getResultsData', false);

		// Use the cached data if possible.
		if ($this->retrieve($store))
		{
			return $this->retrieve($store);
		}

		// Get the result ordering and direction.
		$ordering = $this->getState('list.ordering',
'l.start_date');
		$direction = $this->getState('list.direction',
'DESC');

		// Get the base query and add the ordering information.
		$base = $this->getListQuery();
		$base->select($this->_db->escape($ordering) . ' AS
ordering');
		$base->order($this->_db->escape($ordering) . ' ' .
$this->_db->escape($direction));

		/*
		 * If there are no optional or required search terms in the query, we
		 * can get the results in one relatively simple database query.
		 */
		if (empty($this->includedTerms))
		{
			// Get the results from the database.
			$this->_db->setQuery($base, (int)
$this->getState('list.start'), (int)
$this->getState('list.limit'));
			$return = $this->_db->loadObjectList('link_id');

			// Get a new store id because this data is page specific.
			$store = $this->getStoreId('getResultsData', true);

			// Push the results into cache.
			$this->store($store, $return);

			// Return the results.
			return $this->retrieve($store);
		}

		/*
		 * If there are optional or required search terms in the query, the
		 * process of getting the results is more complicated.
		 */
		$start = 0;
		$limit = (int) $this->getState('match.limit');
		$items = array();
		$sorted = array();
		$maps = array();
		$excluded = $this->getExcludedLinkIds();

		/*
		 * Iterate through the included search terms and group them by mapping
		 * table suffix. This ensures that we never have to do more than 16
		 * queries to get a batch. This may seem like a lot but it is rarely
		 * anywhere near 16 because of the improved mapping algorithm.
		 */
		foreach ($this->includedTerms as $token => $ids)
		{
			// Get the mapping table suffix.
			$suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)),
0, 1);

			// Initialize the mapping group.
			if (!array_key_exists($suffix, $maps))
			{
				$maps[$suffix] = array();
			}

			// Add the terms to the mapping group.
			$maps[$suffix] = array_merge($maps[$suffix], $ids);
		}

		/*
		 * When the query contains search terms we need to find and process the
		 * results iteratively using a do-while loop.
		 */
		do
		{
			// Create a container for the fetched results.
			$results = array();
			$more = false;

			/*
			 * Iterate through the mapping groups and load the results from each
			 * mapping table.
			 */
			foreach ($maps as $suffix => $ids)
			{
				// Create a storage key for this set.
				$setId = $this->getStoreId('getResultsData:' .
serialize(array_values($ids)) . ':' . $start . ':' .
$limit);

				// Use the cached data if possible.
				if ($this->retrieve($setId))
				{
					$temp = $this->retrieve($setId);
				}
				// Load the data from the database.
				else
				{
					// Adjust the query to join on the appropriate mapping table.
					$query = clone $base;
					$query->join('INNER',
$this->_db->quoteName('#__finder_links_terms' . $suffix) .
' AS m ON m.link_id = l.link_id')
						->where('m.term_id IN (' . implode(',', $ids)
. ')');

					// Load the results from the database.
					$this->_db->setQuery($query, $start, $limit);
					$temp = $this->_db->loadObjectList('link_id');

					// Store this set in cache.
					$this->store($setId, $temp);

					// The data is keyed by link_id to ease caching, we don't need it
till later.
					$temp = array_values($temp);
				}

				// Set the more flag to true if any of the sets equal the limit.
				$more = count($temp) === $limit;

				// Merge the results.
				$results = array_merge($results, $temp);
			}

			// Check if there are any excluded terms to deal with.
			if (count($excluded))
			{
				// Remove any results that match excluded terms.
				for ($i = 0, $c = count($results); $i < $c; $i++)
				{
					if (in_array($results[$i]->link_id, $excluded))
					{
						unset($results[$i]);
					}
				}

				// Reset the array keys.
				$results = array_values($results);
			}

			/*
			 * If we are ordering by relevance we have to add up the relevance
			 * scores that are contained in the ordering field.
			 */
			if ($ordering === 'm.weight')
			{
				// Iterate through the set to extract the unique items.
				for ($i = 0, $c = count($results); $i < $c; $i++)
				{
					// Add the total weights for all included search terms.
					if (isset($sorted[$results[$i]->link_id]))
					{
						$sorted[$results[$i]->link_id] += (float)
$results[$i]->ordering;
					}
					else
					{
						$sorted[$results[$i]->link_id] = (float)
$results[$i]->ordering;
					}
				}
			}
			/*
			 * If we are ordering by start date we have to add convert the
			 * dates to unix timestamps.
			 */
			elseif ($ordering === 'l.start_date')
			{
				// Iterate through the set to extract the unique items.
				for ($i = 0, $c = count($results); $i < $c; $i++)
				{
					if (!isset($sorted[$results[$i]->link_id]))
					{
						$sorted[$results[$i]->link_id] =
strtotime($results[$i]->ordering);
					}
				}
			}
			/*
			 * If we are not ordering by relevance or date, we just have to add
			 * the unique items to the set.
			 */
			else
			{
				// Iterate through the set to extract the unique items.
				for ($i = 0, $c = count($results); $i < $c; $i++)
				{
					if (!isset($sorted[$results[$i]->link_id]))
					{
						$sorted[$results[$i]->link_id] = $results[$i]->ordering;
					}
				}
			}

			// Sort the results.
			natcasesort($items);

			if ($direction === 'DESC')
			{
				$items = array_reverse($items, true);
			}

			/*
			 * If the query contains just optional search terms and we have
			 * enough items for the page, we can stop here.
			 */
			if (empty($this->requiredTerms))
			{
				// If we need more items and they're available, make another pass.
				if ($more && count($sorted) <
($this->getState('list.start') +
$this->getState('list.limit')))
				{
					// Increment the batch starting point and continue.
					$start += $limit;
					continue;
				}

				// Push the results into cache.
				$this->store($store, $sorted);

				// Return the requested set.
				return array_slice($this->retrieve($store), (int)
$this->getState('list.start'), (int)
$this->getState('list.limit'), true);
			}

			/*
			 * The query contains required search terms so we have to iterate
			 * over the items and remove any items that do not match all of the
			 * required search terms. This is one of the most expensive steps
			 * because a required token could theoretically eliminate all of
			 * current terms which means we would have to loop through all of
			 * the possibilities.
			 */
			foreach ($this->requiredTerms as $token => $required)
			{
				// Create a storage key for this set.
				$setId = $this->getStoreId('getResultsData:required:' .
serialize(array_values($required)) . ':' . $start . ':'
. $limit);

				// Use the cached data if possible.
				if ($this->retrieve($setId))
				{
					$reqTemp = $this->retrieve($setId);
				}
				// Check if the token was matched.
				elseif (empty($required))
				{
					return null;
				}
				// Load the data from the database.
				else
				{
					// Setup containers in case we have to make multiple passes.
					$reqStart = 0;
					$reqTemp = array();

					do
					{
						// Get the map table suffix.
						$suffix = StringHelper::substr(md5(StringHelper::substr($token, 0,
1)), 0, 1);

						// Adjust the query to join on the appropriate mapping table.
						$query = clone $base;
						$query->join('INNER',
$this->_db->quoteName('#__finder_links_terms' . $suffix) .
' AS m ON m.link_id = l.link_id')
							->where('m.term_id IN (' . implode(',',
$required) . ')');

						// Load the results from the database.
						$this->_db->setQuery($query, $reqStart, $limit);
						$temp = $this->_db->loadObjectList('link_id');

						// Set the required token more flag to true if the set equal the
limit.
						$reqMore = count($temp) === $limit;

						// Merge the matching set for this token.
						$reqTemp += $temp;

						// Increment the term offset.
						$reqStart += $limit;
					}
					while ($reqMore === true);

					// Store this set in cache.
					$this->store($setId, $reqTemp);
				}

				// Remove any items that do not match the required term.
				$sorted = array_intersect_key($sorted, $reqTemp);
			}

			// If we need more items and they're available, make another pass.
			if ($more && count($sorted) <
($this->getState('list.start') +
$this->getState('list.limit')))
			{
				// Increment the batch starting point.
				$start += $limit;

				// Merge the found items.
				$items = array_merge($items, $sorted);

				continue;
			}
			// Otherwise, end the loop.
			else
			{
				// Set the found items.
				$items = $sorted;

				$more = false;
			}

			// End do-while loop.
		}
		while ($more === true);

		// Push the results into cache.
		$this->store($store, $items);

		// Return the requested set.
		return array_slice($this->retrieve($store), (int)
$this->getState('list.start'), (int)
$this->getState('list.limit'), true);
	}

	/**
	 * Method to get an array of link ids that match excluded terms.
	 *
	 * @return  array  An array of links ids.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getExcludedLinkIds()
	{
		// Check if the search query has excluded terms.
		if (empty($this->excludedTerms))
		{
			return array();
		}

		// Get the store id.
		$store = $this->getStoreId('getExcludedLinkIds', false);

		// Use the cached data if possible.
		if ($this->retrieve($store))
		{
			return $this->retrieve($store);
		}

		// Initialize containers.
		$links = array();
		$maps = array();

		/*
		 * Iterate through the excluded search terms and group them by mapping
		 * table suffix. This ensures that we never have to do more than 16
		 * queries to get a batch. This may seem like a lot but it is rarely
		 * anywhere near 16 because of the improved mapping algorithm.
		 */
		foreach ($this->excludedTerms as $token => $id)
		{
			// Get the mapping table suffix.
			$suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)),
0, 1);

			// Initialize the mapping group.
			if (!array_key_exists($suffix, $maps))
			{
				$maps[$suffix] = array();
			}

			// Add the terms to the mapping group.
			$maps[$suffix][] = (int) $id;
		}

		/*
		 * Iterate through the mapping groups and load the excluded links ids
		 * from each mapping table.
		 */

		// Create a new query object.
		$db = $this->getDbo();
		$query = $db->getQuery(true);

		foreach ($maps as $suffix => $ids)
		{
			// Create the query to get the links ids.
			$query->clear()
				->select('link_id')
				->from($db->quoteName('#__finder_links_terms' .
$suffix))
				->where($db->quoteName('term_id') . ' IN (' .
implode(',', $ids) . ')')
				->group($db->quoteName('link_id'));

			// Load the link ids from the database.
			$db->setQuery($query);
			$temp = $db->loadColumn();

			// Merge the link ids.
			$links = array_merge($links, $temp);
		}

		// Sanitize the link ids.
		$links = array_unique($links);
		$links = ArrayHelper::toInteger($links);

		// Push the link ids into cache.
		$this->store($store, $links);

		return $links;
	}

	/**
	 * Method to get a store id based on model the configuration state.
	 *
	 * This is necessary because the model is used by the component and
	 * different modules that might need different sets of data or different
	 * ordering requirements.
	 *
	 * @param   string   $id    An identifier string to generate the store id.
[optional]
	 * @param   boolean  $page  True to store the data paged, false to store
all data. [optional]
	 *
	 * @return  string  A store id.
	 *
	 * @since   2.5
	 */
	protected function getStoreId($id = '', $page = true)
	{
		// Get the query object.
		$query = $this->getQuery();

		// Add the search query state.
		$id .= ':' . $query->input;
		$id .= ':' . $query->language;
		$id .= ':' . $query->filter;
		$id .= ':' . serialize($query->filters);
		$id .= ':' . $query->date1;
		$id .= ':' . $query->date2;
		$id .= ':' . $query->when1;
		$id .= ':' . $query->when2;

		if ($page)
		{
			// Add the list state for page specific data.
			$id .= ':' . $this->getState('list.start');
			$id .= ':' . $this->getState('list.limit');
			$id .= ':' . $this->getState('list.ordering');
			$id .= ':' . $this->getState('list.direction');
		}

		return parent::getStoreId($id);
	}

	/**
	 * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
	 *
	 * @param   string  $ordering   An optional ordering field. [optional]
	 * @param   string  $direction  An optional direction. [optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function populateState($ordering = null, $direction = null)
	{
		// Get the configuration options.
		$app = JFactory::getApplication();
		$input = $app->input;
		$params = $app->getParams();
		$user = JFactory::getUser();

		$this->setState('filter.language',
JLanguageMultilang::isEnabled());

		// Setup the stemmer.
		if ($params->get('stem', 1) &&
$params->get('stemmer', 'porter_en'))
		{
			FinderIndexerHelper::$stemmer =
FinderIndexerStemmer::getInstance($params->get('stemmer',
'porter_en'));
		}

		$request = $input->request;
		$options = array();

		// Get the empty query setting.
		$options['empty'] =
$params->get('allow_empty_query', 0);

		// Get the static taxonomy filters.
		$options['filter'] = $request->getInt('f',
$params->get('f', ''));

		// Get the dynamic taxonomy filters.
		$options['filters'] = $request->get('t',
$params->get('t', array()), '', 'array');

		// Get the query string.
		$options['input'] = $request->getString('q',
$params->get('q', ''));

		// Get the query language.
		$options['language'] = $request->getCmd('l',
$params->get('l', ''));

		// Get the start date and start date modifier filters.
		$options['date1'] = $request->getString('d1',
$params->get('d1', ''));
		$options['when1'] = $request->getString('w1',
$params->get('w1', ''));

		// Get the end date and end date modifier filters.
		$options['date2'] = $request->getString('d2',
$params->get('d2', ''));
		$options['when2'] = $request->getString('w2',
$params->get('w2', ''));

		// Load the query object.
		$this->query = new FinderIndexerQuery($options);

		// Load the query token data.
		$this->excludedTerms = $this->query->getExcludedTermIds();
		$this->includedTerms = $this->query->getIncludedTermIds();
		$this->requiredTerms = $this->query->getRequiredTermIds();

		// Load the list state.
		$this->setState('list.start',
$input->get('limitstart', 0, 'uint'));
		$this->setState('list.limit',
$input->get('limit', $app->get('list_limit', 20),
'uint'));

		/**
		 * Load the sort ordering.
		 * Currently this is 'hard' coded via menu item parameter but
may not satisfy a users need.
		 * More flexibility was way more user friendly. So we allow the user to
pass a custom value
		 * from the pool of fields that are indexed like the 'title'
field.
		 * Also, we allow this parameter to be passed in either case
(lower/upper).
		 */
		$order = $input->getWord('filter_order',
$params->get('sort_order', 'relevance'));
		$order = StringHelper::strtolower($order);

		switch ($order)
		{
			case 'date':
				$this->setState('list.ordering',
'l.start_date');
				break;

			case 'price':
				$this->setState('list.ordering',
'l.list_price');
				break;

			case ($order === 'relevance' &&
!empty($this->includedTerms)) :
				$this->setState('list.ordering', 'm.weight');
				break;

			// Custom field that is indexed and might be required for ordering
			case 'title':
				$this->setState('list.ordering', 'l.title');
				break;

			default:
				$this->setState('list.ordering', 'l.link_id');
				break;
		}

		/**
		 * Load the sort direction.
		 * Currently this is 'hard' coded via menu item parameter but
may not satisfy a users need.
		 * More flexibility was way more user friendly. So we allow to be
inverted.
		 * Also, we allow this parameter to be passed in either case
(lower/upper).
		 */
		$dirn = $input->getWord('filter_order_Dir',
$params->get('sort_direction', 'desc'));
		$dirn = StringHelper::strtolower($dirn);

		switch ($dirn)
		{
			case 'asc':
				$this->setState('list.direction', 'ASC');
				break;

			default:
			case 'desc':
				$this->setState('list.direction', 'DESC');
				break;
		}

		// Set the match limit.
		$this->setState('match.limit', 1000);

		// Load the parameters.
		$this->setState('params', $params);

		// Load the user state.
		$this->setState('user.id', (int)
$user->get('id'));
		$this->setState('user.groups',
$user->getAuthorisedViewLevels());
	}

	/**
	 * Method to retrieve data from cache.
	 *
	 * @param   string   $id          The cache store id.
	 * @param   boolean  $persistent  Flag to enable the use of external
cache. [optional]
	 *
	 * @return  mixed  The cached data if found, null otherwise.
	 *
	 * @since   2.5
	 */
	protected function retrieve($id, $persistent = true)
	{
		$data = null;

		// Use the internal cache if possible.
		if (isset($this->cache[$id]))
		{
			return $this->cache[$id];
		}

		// Use the external cache if data is persistent.
		if ($persistent)
		{
			$data = JFactory::getCache($this->context,
'output')->get($id);
			$data = $data ? unserialize($data) : null;
		}

		// Store the data in internal cache.
		if ($data)
		{
			$this->cache[$id] = $data;
		}

		return $data;
	}

	/**
	 * Method to store data in cache.
	 *
	 * @param   string   $id          The cache store id.
	 * @param   mixed    $data        The data to cache.
	 * @param   boolean  $persistent  Flag to enable the use of external
cache. [optional]
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 */
	protected function store($id, $data, $persistent = true)
	{
		// Store the data in internal cache.
		$this->cache[$id] = $data;

		// Store the data in external cache if data is persistent.
		if ($persistent)
		{
			return JFactory::getCache($this->context,
'output')->store(serialize($data), $id);
		}

		return true;
	}
}
PK��[0L�`jjmodels/suggestions.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;

define('FINDER_PATH_INDEXER', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/indexer');
JLoader::register('FinderIndexerHelper', FINDER_PATH_INDEXER .
'/helper.php');

/**
 * Suggestions model class for the Finder package.
 *
 * @since  2.5
 */
class FinderModelSuggestions extends JModelList
{
	/**
	 * Context string for the model type.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $context = 'com_finder.suggestions';

	/**
	 * Method to get an array of data items.
	 *
	 * @return  array  An array of data items.
	 *
	 * @since   2.5
	 */
	public function getItems()
	{
		// Get the items.
		$items = parent::getItems();

		// Convert them to a simple array.
		foreach ($items as $k => $v)
		{
			$items[$k] = $v->term;
		}

		return $items;
	}

	/**
	 * Method to build a database query to load the list data.
	 *
	 * @return  JDatabaseQuery  A database query
	 *
	 * @since   2.5
	 */
	protected function getListQuery()
	{
		$user = JFactory::getUser();
		$groups =
\Joomla\Utilities\ArrayHelper::toInteger($user->getAuthorisedViewLevels());

		// Create a new query object.
		$db = $this->getDbo();
		$termIdQuery = $db->getQuery(true);
		$termQuery = $db->getQuery(true);

		// Limit term count to a reasonable number of results to reduce main
query join size
		$termIdQuery->select('ti.term_id')
			->from($db->quoteName('#__finder_terms',
'ti'))
			->where('ti.term LIKE ' .
$db->quote($db->escape($this->getState('input'), true) .
'%', false))
			->where('ti.common = 0')
			->where('ti.language IN (' .
$db->quote($this->getState('language')) . ', ' .
$db->quote('*') . ')')
			->order('ti.links DESC')
			->order('ti.weight DESC');

		$termIds = $db->setQuery($termIdQuery, 0, 100)->loadColumn();

		// Early return on term mismatch
		if (!count($termIds))
		{
			return $termIdQuery;
		}

		$termIdString = implode(',', $termIds);

		// Select required fields
		$termQuery->select('DISTINCT(t.term)')
			->from($db->quoteName('#__finder_terms') . ' AS
t')
			->where('t.term_id IN (' . $termIdString . ')')
			->order('t.links DESC')
			->order('t.weight DESC');

		// Determine the relevant mapping table suffix by inverting the logic
from drivers
		$mappingTableSuffix =
StringHelper::substr(md5(StringHelper::substr($this->getState('input'),
0, 1)), 0, 1);

		// Join mapping table for term <-> link relation
		$mappingTable = $db->quoteName('#__finder_links_terms' .
$mappingTableSuffix);
		$termQuery->join('INNER', $mappingTable . ' AS tm ON
tm.term_id = t.term_id');

		// Join links table
		$termQuery->join('INNER',
$db->quoteName('#__finder_links') . ' AS l ON (tm.link_id
= l.link_id)')
			->where('l.access IN (' . implode(',', $groups) .
')')
			->where('l.state = 1')
			->where('l.published = 1');

		return $termQuery;
	}

	/**
	 * Method to get a store id based on model the configuration state.
	 *
	 * This is necessary because the model is used by the component and
	 * different modules that might need different sets of data or different
	 * ordering requirements.
	 *
	 * @param   string  $id  An identifier string to generate the store id.
[optional]
	 *
	 * @return  string  A store id.
	 *
	 * @since   2.5
	 */
	protected function getStoreId($id = '')
	{
		// Add the search query state.
		$id .= ':' . $this->getState('input');
		$id .= ':' . $this->getState('language');

		// Add the list state.
		$id .= ':' . $this->getState('list.start');
		$id .= ':' . $this->getState('list.limit');

		return parent::getStoreId($id);
	}

	/**
	 * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
	 *
	 * @param   string  $ordering   An optional ordering field.
	 * @param   string  $direction  An optional direction (asc|desc).
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function populateState($ordering = null, $direction = null)
	{
		// Get the configuration options.
		$app = JFactory::getApplication();
		$input = $app->input;
		$params = JComponentHelper::getParams('com_finder');
		$user = JFactory::getUser();

		// Get the query input.
		$this->setState('input',
$input->request->get('q', '',
'string'));

		// Set the query language
		if (JLanguageMultilang::isEnabled())
		{
			$lang = JFactory::getLanguage()->getTag();
		}
		else
		{
			$lang = FinderIndexerHelper::getDefaultLanguage();
		}

		$lang = FinderIndexerHelper::getPrimaryLanguage($lang);
		$this->setState('language', $lang);

		// Load the list state.
		$this->setState('list.start', 0);
		$this->setState('list.limit', 10);

		// Load the parameters.
		$this->setState('params', $params);

		// Load the user state.
		$this->setState('user.id', (int)
$user->get('id'));
	}
}
PK��[�(�I}}
router.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Routing class from com_finder
 *
 * @since  3.3
 */
class FinderRouter extends JComponentRouterBase
{
	/**
	 * Build the route for the com_finder component
	 *
	 * @param   array  &$query  An array of URL arguments
	 *
	 * @return  array  The URL arguments to use to assemble the subsequent
URL.
	 *
	 * @since   3.3
	 */
	public function build(&$query)
	{
		$segments = array();

		/*
		 * First, handle menu item routes first. When the menu system builds a
		 * route, it only provides the option and the menu item id. We don't
have
		 * to do anything to these routes.
		 */
		if (count($query) === 2 && isset($query['Itemid'],
$query['option']))
		{
			return $segments;
		}

		/*
		 * Next, handle a route with a supplied menu item id. All system
generated
		 * routes should fall into this group. We can assume that the menu item
id
		 * is the best possible match for the query but we need to go through and
		 * see which variables we can eliminate from the route query string
because
		 * they are present in the menu item route already.
		 */
		if (!empty($query['Itemid']))
		{
			// Get the menu item.
			$item = $this->menu->getItem($query['Itemid']);

			// Check if the view matches.
			if ($item && isset($item->query['view']) &&
isset($query['view']) &&
$item->query['view'] === $query['view'])
			{
				unset($query['view']);
			}

			// Check if the search query filter matches.
			if ($item && isset($item->query['f']) &&
isset($query['f']) && $item->query['f'] ===
$query['f'])
			{
				unset($query['f']);
			}

			// Check if the search query string matches.
			if ($item && isset($item->query['q']) &&
isset($query['q']) && $item->query['q'] ===
$query['q'])
			{
				unset($query['q']);
			}

			return $segments;
		}

		/*
		 * Lastly, handle a route with no menu item id. Fortunately, we only need
		 * to deal with the view as the other route variables are supposed to
stay
		 * in the query string.
		 */
		if (isset($query['view']))
		{
			// Add the view to the segments.
			$segments[] = $query['view'];
			unset($query['view']);
		}

		$total = count($segments);

		for ($i = 0; $i < $total; $i++)
		{
			$segments[$i] = str_replace(':', '-',
$segments[$i]);
		}

		return $segments;
	}

	/**
	 * Parse the segments of a URL.
	 *
	 * @param   array  &$segments  The segments of the URL to parse.
	 *
	 * @return  array  The URL attributes to be used by the application.
	 *
	 * @since   3.3
	 */
	public function parse(&$segments)
	{
		$total = count($segments);
		$vars = array();

		for ($i = 0; $i < $total; $i++)
		{
			$segments[$i] = preg_replace('/-/', ':',
$segments[$i], 1);
		}

		// Check if the view segment is set and it equals search or advanced.
		if (isset($segments[0]) && ($segments[0] === 'search'
|| $segments[0] === 'advanced'))
		{
			$vars['view'] = $segments[0];
		}

		return $vars;
	}
}

/**
 * Finder router functions
 *
 * These functions are proxys for the new router interface
 * for old SEF extensions.
 *
 * @param   array  &$query  An array of URL arguments
 *
 * @return  array  The URL arguments to use to assemble the subsequent URL.
 *
 * @deprecated  4.0  Use Class based routers instead
 */
function FinderBuildRoute(&$query)
{
	$router = new FinderRouter;

	return $router->build($query);
}

/**
 * Finder router functions
 *
 * These functions are proxys for the new router interface
 * for old SEF extensions.
 *
 * @param   array  $segments  The segments of the URL to parse.
 *
 * @return  array  The URL attributes to be used by the application.
 *
 * @deprecated  4.0  Use Class based routers instead
 */
function FinderParseRoute($segments)
{
	$router = new FinderRouter;

	return $router->parse($segments);
}
PK��[�u��views/search/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('behavior.core');
JHtml::_('formbehavior.chosen');
JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
JHtml::_('stylesheet', 'com_finder/finder.css',
array('version' => 'auto', 'relative'
=> true));

?>
<div class="finder<?php echo $this->pageclass_sfx;
?>">
	<?php if ($this->params->get('show_page_heading')) :
?>
		<h1>
			<?php if
($this->escape($this->params->get('page_heading'))) :
?>
				<?php echo
$this->escape($this->params->get('page_heading')); ?>
			<?php else : ?>
				<?php echo
$this->escape($this->params->get('page_title')); ?>
			<?php endif; ?>
		</h1>
	<?php endif; ?>
	<?php if ($this->params->get('show_search_form', 1)) :
?>
		<div id="search-form">
			<?php echo $this->loadTemplate('form'); ?>
		</div>
	<?php endif; ?>
	<?php // Load the search results layout if we are performing a search.
?>
	<?php if ($this->query->search === true) : ?>
		<div id="search-results">
			<?php echo $this->loadTemplate('results'); ?>
		</div>
	<?php endif; ?>
</div>
PK��[g)4�""views/search/tmpl/default.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TITLE">
		<help
			key = "JHELP_MENUS_MENU_ITEM_FINDER_SEARCH"
		/>
		<message>
			<![CDATA[COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TEXT]]>
		</message>
	</layout>

	<fields name="request"
addfieldpath="/administrator/components/com_finder/models/fields">
		<fieldset name="request">
			<field
				name="q"
				type="text"
				label="COM_FINDER_SEARCH_SEARCH_QUERY_LABEL"
				description="COM_FINDER_SEARCH_SEARCH_QUERY_DESC"
				size="30"
			/>
			<field
				name="f"
				type="searchfilter"
				label="COM_FINDER_SEARCH_FILTER_SEARCH_LABEL"
				description="COM_FINDER_SEARCH_FILTER_SEARCH_DESC"
				default=""
			/>
		</fieldset>
	</fields>
	<fields name="params"
addfieldpath="/administrator/components/com_finder/models/fields">
		<fieldset name="basic">
			<field
				name="show_date_filters"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_LABEL"
				description="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="show_advanced"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_ADVANCED_LABEL"
				description="COM_FINDER_CONFIG_SHOW_ADVANCED_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="expand_advanced"
				type="list"
				label="COM_FINDER_CONFIG_EXPAND_ADVANCED_LABEL"
				description="COM_FINDER_CONFIG_EXPAND_ADVANCED_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field type="spacer" />
			<field
				name="show_description"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_DESCRIPTION_LABEL"
				description="COM_FINDER_CONFIG_SHOW_DESCRIPTION_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="description_length"
				type="number"
				label="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_LABEL"
				description="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_DESC"
				default=""
				size="5"
				useglobal="true"
			/>
			<field
				name="show_url"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_URL_LABEL"
				description="COM_FINDER_CONFIG_SHOW_URL_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field type="spacer" />
		</fieldset>
		<fieldset name="advanced">
			<field
				name="show_pagination_limit"
				type="list"
				label="JGLOBAL_DISPLAY_SELECT_LABEL"
				description="JGLOBAL_DISPLAY_SELECT_DESC"
				validate="options"
				class="chzn-color"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
			<field
				name="show_pagination"
				type="list"
				label="JGLOBAL_PAGINATION_LABEL"
				description="JGLOBAL_PAGINATION_DESC"
				validate="options"
				class="chzn-color"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
				<option value="2">JGLOBAL_AUTO</option>
			</field>
			<field
				name="show_pagination_results"
				type="list"
				label="JGLOBAL_PAGINATION_RESULTS_LABEL"
				description="JGLOBAL_PAGINATION_RESULTS_DESC"
				validate="options"
				class="chzn-color"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
			<field
				name="allow_empty_query"
				type="list"
				label="COM_FINDER_ALLOW_EMPTY_QUERY_LABEL"
				description="COM_FINDER_ALLOW_EMPTY_QUERY_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="show_suggested_query"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_LABEL"
				description="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="show_explained_query"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_LABEL"
				description="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_DESC"
				default=""
				useglobal="true"
				class="chzn-color"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="sort_order"
				type="list"
				label="COM_FINDER_CONFIG_SORT_ORDER_LABEL"
				description="COM_FINDER_CONFIG_SORT_ORDER_DESC"
				default=""
				useglobal="true"
				>
				<option
value="relevance">COM_FINDER_CONFIG_SORT_OPTION_RELEVANCE</option>
				<option
value="date">COM_FINDER_CONFIG_SORT_OPTION_START_DATE</option>
				<option
value="price">COM_FINDER_CONFIG_SORT_OPTION_LIST_PRICE</option>
			</field>
			<field
				name="sort_direction"
				type="list"
				label="COM_FINDER_CONFIG_SORT_DIRECTION_LABEL"
				description="COM_FINDER_CONFIG_SORT_DIRECTION_DESC"
				default=""
				useglobal="true"
				>
				<option
value="desc">COM_FINDER_CONFIG_SORT_OPTION_DESCENDING</option>
				<option
value="asc">COM_FINDER_CONFIG_SORT_OPTION_ASCENDING</option>
			</field>
		</fieldset>
		<fieldset name="integration">
			<field
				name="show_feed_link"
				type="list"
				label="JGLOBAL_SHOW_FEED_LINK_LABEL"
				description="JGLOBAL_SHOW_FEED_LINK_DESC"
				validate="options"
				class="chzn-color"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
		</fieldset>
	</fields>
</metadata>
PK��[����
�
"views/search/tmpl/default_form.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

if ($this->params->get('show_advanced', 1) ||
$this->params->get('show_autosuggest', 1))
{
	JHtml::_('jquery.framework');

	$script = "
jQuery(function() {";

	if ($this->params->get('show_advanced', 1))
	{
		/*
		* This segment of code disables select boxes that have no value when the
		* form is submitted so that the URL doesn't get blown up with null
values.
		*/
		$script .= "
	jQuery('#finder-search').on('submit', function(e){
		e.stopPropagation();
		// Disable select boxes with no value selected.
		jQuery('#advancedSearch').find('select').each(function(index,
el) {
			var el = jQuery(el);
			if(!el.val()){
				el.attr('disabled', 'disabled');
			}
		});
	});";
	}

	/*
	* This segment of code sets up the autocompleter.
	*/
	if ($this->params->get('show_autosuggest', 1))
	{
		JHtml::_('script', 'jui/jquery.autocomplete.min.js',
array('version' => 'auto', 'relative'
=> true));

		$script .= "
	var suggest = jQuery('#q').autocomplete({
		serviceUrl: '" .
JRoute::_('index.php?option=com_finder&task=suggestions.suggest&format=json&tmpl=component')
. "',
		paramName: 'q',
		minChars: 1,
		maxHeight: 400,
		width: 300,
		zIndex: 9999,
		deferRequestBy: 500
	});";
	}

	$script .= "
});";

	JFactory::getDocument()->addScriptDeclaration($script);
}

?>
<form id="finder-search" action="<?php echo
JRoute::_($this->query->toUri()); ?>" method="get"
class="form-inline">
	<?php echo $this->getFields(); ?>
	<?php // DISABLED UNTIL WEIRD VALUES CAN BE TRACKED DOWN. ?>
	<?php if (false &&
$this->state->get('list.ordering') !==
'relevance_dsc') : ?>
		<input type="hidden" name="o" value="<?php
echo $this->escape($this->state->get('list.ordering'));
?>" />
	<?php endif; ?>
	<fieldset class="word">
		<label for="q">
			<?php echo JText::_('COM_FINDER_SEARCH_TERMS'); ?>
		</label>
		<input type="text" name="q" id="q"
size="30" value="<?php echo
$this->escape($this->query->input); ?>"
class="inputbox" />
		<?php if ($this->escape($this->query->input) != ''
|| $this->params->get('allow_empty_query')) : ?>
			<button name="Search" type="submit"
class="btn btn-primary">
				<span class="icon-search icon-white"></span>
				<?php echo JText::_('JSEARCH_FILTER_SUBMIT'); ?>
			</button>
		<?php else : ?>
			<button name="Search" type="submit"
class="btn btn-primary disabled">
				<span class="icon-search icon-white"></span>
				<?php echo JText::_('JSEARCH_FILTER_SUBMIT'); ?>
			</button>
		<?php endif; ?>
		<?php if ($this->params->get('show_advanced', 1)) :
?>
			<a href="#advancedSearch" data-toggle="collapse"
class="btn">
				<span class="icon-list"
aria-hidden="true"></span>
				<?php echo JText::_('COM_FINDER_ADVANCED_SEARCH_TOGGLE');
?>
			</a>
		<?php endif; ?>
	</fieldset>
	<?php if ($this->params->get('show_advanced', 1)) :
?>
		<div id="advancedSearch" class="collapse<?php if
($this->params->get('expand_advanced', 0)) echo '
in'; ?>">
			<hr />
			<?php if ($this->params->get('show_advanced_tips',
1)) : ?>
				<div id="search-query-explained">
					<div class="advanced-search-tip">
						<?php echo JText::_('COM_FINDER_ADVANCED_TIPS'); ?>
					</div>
					<hr />
				</div>
			<?php endif; ?>
			<div id="finder-filter-window">
				<?php echo JHtml::_('filter.select', $this->query,
$this->params); ?>
			</div>
		</div>
	<?php endif; ?>
</form>
PK��[��

$views/search/tmpl/default_result.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;

// Get the mime type class.
$mime = !empty($this->result->mime) ? 'mime-' .
$this->result->mime : null;

$show_description = $this->params->get('show_description',
1);

if ($show_description)
{
	// Calculate number of characters to display around the result
	$term_length = StringHelper::strlen($this->query->input);
	$desc_length = $this->params->get('description_length',
255);
	$pad_length  = $term_length < $desc_length ? (int) floor(($desc_length
- $term_length) / 2) : 0;

	// Make sure we highlight term both in introtext and fulltext
	if (!empty($this->result->summary) &&
!empty($this->result->body))
	{
		$full_description =
FinderIndexerHelper::parse($this->result->summary .
$this->result->body);
	}
	else
	{
		$full_description = $this->result->description;
	}

	// Find the position of the search term
	$pos = $term_length ?
StringHelper::strpos(StringHelper::strtolower($full_description),
StringHelper::strtolower($this->query->input)) : false;

	// Find a potential start point
	$start = ($pos && $pos > $pad_length) ? $pos - $pad_length : 0;

	// Find a space between $start and $pos, start right after it.
	$space = StringHelper::strpos($full_description, ' ', $start
> 0 ? $start - 1 : 0);
	$start = ($space && $space < $pos) ? $space + 1 : $start;

	$description = JHtml::_('string.truncate',
StringHelper::substr($full_description, $start), $desc_length, true);
}

$route = $this->result->route;

// Get the route with highlighting information.
if (!empty($this->query->highlight)
	&& empty($this->result->mime)
	&& $this->params->get('highlight_terms', 1)
	&& JPluginHelper::isEnabled('system',
'highlight'))
{
	$route .= '&highlight=' .
base64_encode(json_encode($this->query->highlight));
}

?>
<li>
	<h4 class="result-title <?php echo $mime; ?>">
		<a href="<?php echo JRoute::_($route); ?>">
			<?php echo $this->result->title; ?>
		</a>
	</h4>
	<?php if ($show_description && $description !== '') :
?>
		<p class="result-text<?php echo $this->pageclass_sfx;
?>">
			<?php echo $description; ?>
		</p>
	<?php endif; ?>
	<?php if ($this->params->get('show_url', 1)) : ?>
		<div class="small result-url<?php echo
$this->pageclass_sfx; ?>">
			<?php echo $this->baseUrl, JRoute::_($this->result->route);
?>
		</div>
	<?php endif; ?>
</li>
PK��[	���%views/search/tmpl/default_results.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

?>
<?php // Display the suggested search if it is different from the
current search. ?>
<?php if (($this->suggested &&
$this->params->get('show_suggested_query', 1)) ||
($this->explained &&
$this->params->get('show_explained_query', 1))) : ?>
	<div id="search-query-explained">
		<?php // Display the suggested search query. ?>
		<?php if ($this->suggested &&
$this->params->get('show_suggested_query', 1)) : ?>
			<?php // Replace the base query string with the suggested query
string. ?>
			<?php $uri = JUri::getInstance($this->query->toUri()); ?>
			<?php $uri->setVar('q', $this->suggested); ?>
			<?php // Compile the suggested query link. ?>
			<?php $linkUrl = JRoute::_($uri->toString(array('path',
'query'))); ?>
			<?php $link = '<a href="' . $linkUrl .
'">' . $this->escape($this->suggested) .
'</a>'; ?>
			<?php echo JText::sprintf('COM_FINDER_SEARCH_SIMILAR',
$link); ?>
		<?php elseif ($this->explained &&
$this->params->get('show_explained_query', 1)) : ?>
			<?php // Display the explained search query. ?>
			<?php echo $this->explained; ?>
		<?php endif; ?>
	</div>
<?php endif; ?>
<?php // Display the 'no results' message and exit the
template. ?>
<?php if (($this->total === 0) || ($this->total === null)) : ?>
	<div id="search-result-empty">
		<h2><?php echo
JText::_('COM_FINDER_SEARCH_NO_RESULTS_HEADING');
?></h2>
		<?php $multilang = JFactory::getApplication()->getLanguageFilter()
? '_MULTILANG' : ''; ?>
		<p><?php echo
JText::sprintf('COM_FINDER_SEARCH_NO_RESULTS_BODY' . $multilang,
$this->escape($this->query->input)); ?></p>
	</div>
	<?php // Exit this template. ?>
	<?php return; ?>
<?php endif; ?>
<?php // Activate the highlighter if enabled. ?>
<?php if (!empty($this->query->highlight) &&
$this->params->get('highlight_terms', 1)) : ?>
	<?php JHtml::_('behavior.highlighter',
$this->query->highlight); ?>
<?php endif; ?>
<?php // Display a list of results ?>
<br id="highlighter-start" />
<ul class="search-results<?php echo $this->pageclass_sfx;
?> list-striped">
	<?php $this->baseUrl =
JUri::getInstance()->toString(array('scheme',
'host', 'port')); ?>
	<?php foreach ($this->results as $result) : ?>
		<?php $this->result = &$result; ?>
		<?php $layout = $this->getLayoutFile($this->result->layout);
?>
		<?php echo $this->loadTemplate($layout); ?>
	<?php endforeach; ?>
</ul>
<br id="highlighter-end" />
<?php // Display the pagination ?>
<div class="search-pagination">
	<div class="pagination">
		<?php echo $this->pagination->getPagesLinks(); ?>
	</div>
	<div class="search-pages-counter">
		<?php // Prepare the pagination string.  Results X - Y of Z ?>
		<?php $start = (int)
$this->pagination->get('limitstart') + 1; ?>
		<?php $total = (int) $this->pagination->get('total');
?>
		<?php $limit = (int) $this->pagination->get('limit') *
$this->pagination->get('pages.current'); ?>
		<?php $limit = (int) ($limit > $total ? $total : $limit); ?>
		<?php echo JText::sprintf('COM_FINDER_SEARCH_RESULTS_OF',
$start, $limit, $total); ?>
	</div>
</div>
PK��[]���
�
views/search/view.feed.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Search feed view class for the Finder package.
 *
 * @since  2.5
 */
class FinderViewSearch extends JViewLegacy
{
	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  JError object on failure, void on success.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Get the application
		$app = JFactory::getApplication();

		// Adjust the list limit to the feed limit.
		$app->input->set('limit',
$app->get('feed_limit'));

		// Get view data.
		$state = $this->get('State');
		$params = $state->get('params');
		$query = $this->get('Query');
		$results = $this->get('Results');

		// Push out the query data.
		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
		$explained = JHtml::_('query.explained', $query);

		// Set the document title.
		$title = $params->get('page_title', '');

		if (empty($title))
		{
			$title = $app->get('sitename');
		}
		elseif ($app->get('sitename_pagetitles', 0) == 1)
		{
			$title = JText::sprintf('JPAGETITLE',
$app->get('sitename'), $title);
		}
		elseif ($app->get('sitename_pagetitles', 0) == 2)
		{
			$title = JText::sprintf('JPAGETITLE', $title,
$app->get('sitename'));
		}

		$this->document->setTitle($title);

		// Configure the document description.
		if (!empty($explained))
		{
			$this->document->setDescription(html_entity_decode(strip_tags($explained),
ENT_QUOTES, 'UTF-8'));
		}

		// Set the document link.
		$this->document->link = JRoute::_($query->toUri());

		// If we don't have any results, we are done.
		if (empty($results))
		{
			return;
		}

		// Convert the results to feed entries.
		foreach ($results as $result)
		{
			// Convert the result to a feed entry.
			$item              = new JFeedItem;
			$item->title       = $result->title;
			$item->link        = JRoute::_($result->route);
			$item->description = $result->description;

			// Use Unix date to cope for non-english languages
			$item->date        = (int) $result->start_date ?
JHtml::_('date', $result->start_date, 'U') :
$result->indexdate;

			// Get the taxonomy data.
			$taxonomy = $result->getTaxonomy();

			// Add the category to the feed if available.
			if (isset($taxonomy['Category']))
			{
				$node           = array_pop($taxonomy['Category']);
				$item->category = $node->title;
			}

			// Loads item info into RSS array
			$this->document->addItem($item);
		}
	}
}
PK��[y�#�??views/search/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Helper\SearchHelper;

/**
 * Search HTML view class for the Finder package.
 *
 * @since  2.5
 */
class FinderViewSearch extends JViewLegacy
{
	/**
	 * The query object
	 *
	 * @var  FinderIndexerQuery
	 */
	protected $query;

	/**
	 * The application parameters
	 *
	 * @var  Registry  The parameters object
	 */
	protected $params;

	/**
	 * The model state
	 *
	 * @var  object
	 */
	protected $state;

	protected $user;

	/**
	 * An array of results
	 *
	 * @var    array
	 *
	 * @since  3.8.0
	 */
	protected $results;

	/**
	 * The total number of items
	 *
	 * @var    integer
	 *
	 * @since  3.8.0
	 */
	protected $total;

	/**
	 * The pagination object
	 *
	 * @var    JPagination
	 *
	 * @since  3.8.0
	 */
	protected $pagination;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  JError object on failure, void on success.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		$app    = JFactory::getApplication();
		$params = $app->getParams();

		// Get view data.
		$state = $this->get('State');
		$query = $this->get('Query');
		JDEBUG ?
JProfiler::getInstance('Application')->mark('afterFinderQuery')
: null;
		$results = $this->get('Results');
		JDEBUG ?
JProfiler::getInstance('Application')->mark('afterFinderResults')
: null;
		$total = $this->get('Total');
		JDEBUG ?
JProfiler::getInstance('Application')->mark('afterFinderTotal')
: null;
		$pagination = $this->get('Pagination');
		JDEBUG ?
JProfiler::getInstance('Application')->mark('afterFinderPagination')
: null;

		// Flag indicates to not add limitstart=0 to URL
		$pagination->hideEmptyLimitstart = true;

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			JError::raiseError(500, implode("\n", $errors));

			return false;
		}

		// Configure the pathway.
		if (!empty($query->input))
		{
			$app->getPathway()->addItem($this->escape($query->input));
		}

		// Push out the view data.
		$this->state      = &$state;
		$this->params     = &$params;
		$this->query      = &$query;
		$this->results    = &$results;
		$this->total      = &$total;
		$this->pagination = &$pagination;

		// Check for a double quote in the query string.
		if (strpos($this->query->input, '"'))
		{
			// Get the application router.
			$router = &$app::getRouter();

			// Fix the q variable in the URL.
			if ($router->getVar('q') !== $this->query->input)
			{
				$router->setVar('q', $this->query->input);
			}
		}

		// Log the search
		SearchHelper::logSearch($this->query->input,
'com_finder');

		// Push out the query data.
		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
		$this->suggested = JHtml::_('query.suggested', $query);
		$this->explained = JHtml::_('query.explained', $query);

		// Escape strings for HTML output
		$this->pageclass_sfx =
htmlspecialchars($params->get('pageclass_sfx'));

		// Check for layout override only if this is not the active menu item
		// If it is the active menu item, then the view and category id will
match
		$active = $app->getMenu()->getActive();

		if (isset($active->query['layout']))
		{
			// We need to set the layout in case this is an alternative menu item
(with an alternative layout)
			$this->setLayout($active->query['layout']);
		}

		$this->prepareDocument($query);

		JDEBUG ?
JProfiler::getInstance('Application')->mark('beforeFinderLayout')
: null;

		parent::display($tpl);

		JDEBUG ?
JProfiler::getInstance('Application')->mark('afterFinderLayout')
: null;
	}

	/**
	 * Method to get hidden input fields for a get form so that control
variables
	 * are not lost upon form submission
	 *
	 * @return  string  A string of hidden input form fields
	 *
	 * @since   2.5
	 */
	protected function getFields()
	{
		$fields = null;

		// Get the URI.
		$uri = JUri::getInstance(JRoute::_($this->query->toUri()));
		$uri->delVar('q');
		$uri->delVar('o');
		$uri->delVar('t');
		$uri->delVar('d1');
		$uri->delVar('d2');
		$uri->delVar('w1');
		$uri->delVar('w2');
		$elements = $uri->getQuery(true);

		// Create hidden input elements for each part of the URI.
		foreach ($elements as $n => $v)
		{
			if (is_scalar($v))
			{
				$fields .= '<input type="hidden" name="' .
$n . '" value="' . $v . '" />';
			}
		}

		return $fields;
	}

	/**
	 * Method to get the layout file for a search result object.
	 *
	 * @param   string  $layout  The layout file to check. [optional]
	 *
	 * @return  string  The layout file to use.
	 *
	 * @since   2.5
	 */
	protected function getLayoutFile($layout = null)
	{
		// Create and sanitize the file name.
		$file = $this->_layout . '_' .
preg_replace('/[^A-Z0-9_\.-]/i', '', $layout);

		// Check if the file exists.
		jimport('joomla.filesystem.path');
		$filetofind = $this->_createFileName('template',
array('name' => $file));
		$exists     = JPath::find($this->_path['template'],
$filetofind);

		return ($exists ? $layout : 'result');
	}

	/**
	 * Prepares the document
	 *
	 * @param   FinderIndexerQuery  $query  The search query
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function prepareDocument($query)
	{
		$app   = JFactory::getApplication();
		$menus = $app->getMenu();
		$title = null;

		// Because the application sets a default page title,
		// we need to get it from the menu item itself
		$menu = $menus->getActive();

		if ($menu)
		{
			$this->params->def('page_heading',
$this->params->get('page_title', $menu->title));
		}
		else
		{
			$this->params->def('page_heading',
JText::_('COM_FINDER_DEFAULT_PAGE_TITLE'));
		}

		$title = $this->params->get('page_title', '');

		if (empty($title))
		{
			$title = $app->get('sitename');
		}
		elseif ($app->get('sitename_pagetitles', 0) == 1)
		{
			$title = JText::sprintf('JPAGETITLE',
$app->get('sitename'), $title);
		}
		elseif ($app->get('sitename_pagetitles', 0) == 2)
		{
			$title = JText::sprintf('JPAGETITLE', $title,
$app->get('sitename'));
		}

		$this->document->setTitle($title);

		if ($layout = $this->params->get('article_layout'))
		{
			$this->setLayout($layout);
		}

		// Configure the document meta-description.
		if (!empty($this->explained))
		{
			$explained =
$this->escape(html_entity_decode(strip_tags($this->explained),
ENT_QUOTES, 'UTF-8'));
			$this->document->setDescription($explained);
		}
		elseif ($this->params->get('menu-meta_description'))
		{
			$this->document->setDescription($this->params->get('menu-meta_description'));
		}

		// Configure the document meta-keywords.
		if (!empty($query->highlight))
		{
			$this->document->setMetaData('keywords', implode(',
', $query->highlight));
		}
		elseif ($this->params->get('menu-meta_keywords'))
		{
			$this->document->setMetadata('keywords',
$this->params->get('menu-meta_keywords'));
		}

		if ($this->params->get('robots'))
		{
			$this->document->setMetadata('robots',
$this->params->get('robots'));
		}

		// Add feed link to the document head.
		if ($this->params->get('show_feed_link', 1) == 1)
		{
			// Add the RSS link.
			$props = array('type' => 'application/rss+xml',
'title' => 'RSS 2.0');
			$route = JRoute::_($this->query->toUri() .
'&format=feed&type=rss');
			$this->document->addHeadLink($route, 'alternate',
'rel', $props);

			// Add the ATOM link.
			$props = array('type' => 'application/atom+xml',
'title' => 'Atom 1.0');
			$route = JRoute::_($this->query->toUri() .
'&format=feed&type=atom');
			$this->document->addHeadLink($route, 'alternate',
'rel', $props);
		}
	}
}
PK��[{^�ZZ
views/search/view.opensearch.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * OpenSearch View class for Finder
 *
 * @since  2.5
 */
class FinderViewSearch extends JViewLegacy
{
	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  JError object on failure, void on success.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		$doc = JFactory::getDocument();
		$app = JFactory::getApplication();

		$params = JComponentHelper::getParams('com_finder');
		$doc->setShortName($params->get('opensearch_name',
$app->get('sitename')));
		$doc->setDescription($params->get('opensearch_description',
$app->get('MetaDesc')));

		// Add the URL for the search
		$searchUri = JUri::base() .
'index.php?option=com_finder&q={searchTerms}';

		// Find the menu item for the search
		$menu  = $app->getMenu();
		$items = $menu->getItems('link',
'index.php?option=com_finder&view=search');

		if (isset($items[0]))
		{
			$searchUri .= '&Itemid=' . $items[0]->id;
		}

		$htmlSearch           = new JOpenSearchUrl;
		$htmlSearch->template = JRoute::_($searchUri);
		$doc->addUrl($htmlSearch);
	}
}
PK��[��Y��temp.phpnu�[���<?php
//Default Configuration
$CONFIG =
'{"lang":"en","error_reporting":false,"show_hidden":false,"hide_Cols":false,"theme":"light"}';

/**
 * H3K | Tiny File Manager V2.5.3
 * @author CCP Programmers
 * @github https://github.com/prasathmani/tinyfilemanager
 * @link https://tinyfilemanager.github.io
 */

//TFM version
define('VERSION', '2.5.3');

//Application Title
define('APP_TITLE', 'Tiny File Manager');

// --- EDIT BELOW CONFIGURATION CAREFULLY ---

// Auth with login/password
// set true/false to enable/disable it
// Is independent from IP white- and blacklisting
$use_auth = true;

// Login user name and password
// Users: array('Username' => 'Password',
'Username2' => 'Password2', ...)
// Generate secure password hash -
https://tinyfilemanager.github.io/docs/pwd.html
$auth_users = array(
    'admin' =>
'$2y$10$LnLofigqN5g6zMQWuKdZXuyGbwO5ZIRL0aaN8UIRcJTTO2QXaR9tO',
    'user' =>
'$2y$10$Fg6Dz8oH9fPoZ2jJan5tZuv6Z4Kp7avtQ9bDfrdRntXtPeiMAZyGO'
);

// Readonly users
// e.g. array('users', 'guest', ...)
$readonly_users = array(
    'user'
);

// Global readonly, including when auth is not being used
$global_readonly = false;

// user specific directories
// array('Username' => 'Directory path',
'Username2' => 'Directory path', ...)
$directories_users = array();

// Enable highlight.js (https://highlightjs.org/) on view's page
$use_highlightjs = true;

// highlight.js style
// for dark theme use 'ir-black'
$highlightjs_style = 'vs';

// Enable ace.js (https://ace.c9.io/) on view's page
$edit_files = true;

// Default timezone for date() and time()
// Doc - http://php.net/manual/en/timezones.php
$default_timezone = 'Etc/UTC'; // UTC

// Root path for file manager
// use absolute path of directory i.e: '/var/www/folder' or
$_SERVER['DOCUMENT_ROOT'].'/folder'
$root_path = $_SERVER['DOCUMENT_ROOT'];

// Root url for links in file manager.Relative to $http_host. Variants:
'', 'path/to/subfolder'
// Will not working if $root_path will be outside of server document root
$root_url = '';

// Server hostname. Can set manually if wrong
// $_SERVER['HTTP_HOST'].'/folder'
$http_host = $_SERVER['HTTP_HOST'];

// input encoding for iconv
$iconv_input_encoding = 'UTF-8';

// date() format for file modification date
// Doc - https://www.php.net/manual/en/function.date.php
$datetime_format = 'm/d/Y g:i A';

// Path display mode when viewing file information
// 'full' => show full path
// 'relative' => show path relative to root_path
// 'host' => show path on the host
$path_display_mode = 'full';

// Allowed file extensions for create and rename files
// e.g. 'txt,html,css,js'
$allowed_file_extensions = '';

// Allowed file extensions for upload files
// e.g. 'gif,png,jpg,html,txt'
$allowed_upload_extensions = '';

// Favicon path. This can be either a full url to an .PNG image, or a path
based on the document root.
// full path, e.g http://example.com/favicon.png
// local path, e.g images/icons/favicon.png
$favicon_path = '';

// Files and folders to excluded from listing
// e.g. array('myfile.html', 'personal-folder',
'*.php', ...)
$exclude_items = array();

// Online office Docs Viewer
// Available rules are 'google', 'microsoft' or false
// Google => View documents using Google Docs Viewer
// Microsoft => View documents using Microsoft Web Apps Viewer
// false => disable online doc viewer
$online_viewer = 'google';

// Sticky Nav bar
// true => enable sticky header
// false => disable sticky header
$sticky_navbar = true;

// Maximum file upload size
// Increase the following values in php.ini to work properly
// memory_limit, upload_max_filesize, post_max_size
$max_upload_size_bytes = 5000000000; // size 5,000,000,000 bytes (~5GB)

// chunk size used for upload
// eg. decrease to 1MB if nginx reports problem 413 entity too large
$upload_chunk_size_bytes = 2000000; // chunk size 2,000,000 bytes (~2MB)

// Possible rules are 'OFF', 'AND' or 'OR'
// OFF => Don't check connection IP, defaults to OFF
// AND => Connection must be on the whitelist, and not on the blacklist
// OR => Connection must be on the whitelist, or not on the blacklist
$ip_ruleset = 'OFF';

// Should users be notified of their block?
$ip_silent = true;

// IP-addresses, both ipv4 and ipv6
$ip_whitelist = array(
    '127.0.0.1',    // local ipv4
    '::1'           // local ipv6
);

// IP-addresses, both ipv4 and ipv6
$ip_blacklist = array(
    '0.0.0.0',      // non-routable meta ipv4
    '::'            // non-routable meta ipv6
);

// if User has the external config file, try to use it to override the
default config above [config.php]
// sample config - https://tinyfilemanager.github.io/config-sample.txt
$config_file = __DIR__.'/config.php';
if (is_readable($config_file)) {
    @include($config_file);
}

// External CDN resources that can be used in the HTML (replace for GDPR
compliance)
$external = array(
    'css-bootstrap' => '<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi"
crossorigin="anonymous">',
    'css-dropzone' => '<link
href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.css"
rel="stylesheet">',
    'css-font-awesome' => '<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
crossorigin="anonymous">',
    'css-highlightjs' => '<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/'
. $highlightjs_style . '.min.css">',
    'js-ace' => '<script
src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.13.1/ace.js"></script>',
    'js-bootstrap' => '<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3"
crossorigin="anonymous"></script>',
    'js-dropzone' => '<script
src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>',
    'js-jquery' => '<script
src="https://code.jquery.com/jquery-3.6.1.min.js"
integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ="
crossorigin="anonymous"></script>',
    'js-jquery-datatables' => '<script
src="https://cdn.datatables.net/1.13.1/js/jquery.dataTables.min.js"
crossorigin="anonymous" defer></script>',
    'js-highlightjs' => '<script
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>',
    'pre-jsdelivr' => '<link
rel="preconnect" href="https://cdn.jsdelivr.net"
crossorigin/><link rel="dns-prefetch"
href="https://cdn.jsdelivr.net"/>',
    'pre-cloudflare' => '<link
rel="preconnect" href="https://cdnjs.cloudflare.com"
crossorigin/><link rel="dns-prefetch"
href="https://cdnjs.cloudflare.com"/>'
);

// --- EDIT BELOW CAREFULLY OR DO NOT EDIT AT ALL ---

// max upload file size
define('MAX_UPLOAD_SIZE', $max_upload_size_bytes);

// upload chunk size
define('UPLOAD_CHUNK_SIZE', $upload_chunk_size_bytes);

// private key and session name to store to the session
if ( !defined( 'FM_SESSION_ID')) {
    define('FM_SESSION_ID', 'filemanager');
}

// Configuration
$cfg = new FM_Config();

// Default language
$lang = isset($cfg->data['lang']) ?
$cfg->data['lang'] : 'en';

// Show or hide files and folders that starts with a dot
$show_hidden_files = isset($cfg->data['show_hidden']) ?
$cfg->data['show_hidden'] : true;

// PHP error reporting - false = Turns off Errors, true = Turns on Errors
$report_errors = isset($cfg->data['error_reporting']) ?
$cfg->data['error_reporting'] : true;

// Hide Permissions and Owner cols in file-listing
$hide_Cols = isset($cfg->data['hide_Cols']) ?
$cfg->data['hide_Cols'] : true;

// Theme
$theme = isset($cfg->data['theme']) ?
$cfg->data['theme'] : 'light';

define('FM_THEME', $theme);

//available languages
$lang_list = array(
    'en' => 'English'
);

if ($report_errors == true) {
    @ini_set('error_reporting', E_ALL);
    @ini_set('display_errors', 1);
} else {
    @ini_set('error_reporting', E_ALL);
    @ini_set('display_errors', 0);
}

// if fm included
if (defined('FM_EMBED')) {
    $use_auth = false;
    $sticky_navbar = false;
} else {
    @set_time_limit(600);

    date_default_timezone_set($default_timezone);

    ini_set('default_charset', 'UTF-8');
    if (version_compare(PHP_VERSION, '5.6.0', '<')
&& function_exists('mb_internal_encoding')) {
        mb_internal_encoding('UTF-8');
    }
    if (function_exists('mb_regex_encoding')) {
        mb_regex_encoding('UTF-8');
    }

    session_cache_limiter('nocache'); // Prevent logout issue
after page was cached
    session_name(FM_SESSION_ID );
    function session_error_handling_function($code, $msg, $file, $line) {
        // Permission denied for default session, try to create a new one
        if ($code == 2) {
            session_abort();
            session_id(session_create_id());
            @session_start();
        }
    }
    set_error_handler('session_error_handling_function');
    session_start();
    restore_error_handler();
}

//Generating CSRF Token
if (empty($_SESSION['token'])) {
    if (function_exists('random_bytes')) {
        $_SESSION['token'] = bin2hex(random_bytes(32));
    } else {
    	$_SESSION['token'] =
bin2hex(openssl_random_pseudo_bytes(32));
    }
}

if (empty($auth_users)) {
    $use_auth = false;
}

$is_https = isset($_SERVER['HTTPS']) &&
($_SERVER['HTTPS'] == 'on' ||
$_SERVER['HTTPS'] == 1)
    || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https';

// update $root_url based on user specific directories
if (isset($_SESSION[FM_SESSION_ID]['logged']) &&
!empty($directories_users[$_SESSION[FM_SESSION_ID]['logged']]))
{
    $wd = fm_clean_path(dirname($_SERVER['PHP_SELF']));
    $root_url = 
$root_url.$wd.DIRECTORY_SEPARATOR.$directories_users[$_SESSION[FM_SESSION_ID]['logged']];
}
// clean $root_url
$root_url = fm_clean_path($root_url);

// abs path for site
defined('FM_ROOT_URL') || define('FM_ROOT_URL',
($is_https ? 'https' : 'http') . '://' .
$http_host . (!empty($root_url) ? '/' . $root_url :
''));
defined('FM_SELF_URL') || define('FM_SELF_URL',
($is_https ? 'https' : 'http') . '://' .
$http_host . $_SERVER['PHP_SELF']);

// logout
if (isset($_GET['logout'])) {
    unset($_SESSION[FM_SESSION_ID]['logged']);
    unset( $_SESSION['token']); 
    fm_redirect(FM_SELF_URL);
}

// Validate connection IP
if ($ip_ruleset != 'OFF') {
    function getClientIP() {
        if (array_key_exists('HTTP_CF_CONNECTING_IP', $_SERVER))
{
            return  $_SERVER["HTTP_CF_CONNECTING_IP"];
        }else if (array_key_exists('HTTP_X_FORWARDED_FOR',
$_SERVER)) {
            return  $_SERVER["HTTP_X_FORWARDED_FOR"];
        }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
            return $_SERVER['REMOTE_ADDR'];
        }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER))
{
            return $_SERVER['HTTP_CLIENT_IP'];
        }
        return '';
    }

    $clientIp = getClientIP();
    $proceed = false;
    $whitelisted = in_array($clientIp, $ip_whitelist);
    $blacklisted = in_array($clientIp, $ip_blacklist);

    if($ip_ruleset == 'AND'){
        if($whitelisted == true && $blacklisted == false){
            $proceed = true;
        }
    } else
    if($ip_ruleset == 'OR'){
         if($whitelisted == true || $blacklisted == false){
            $proceed = true;
        }
    }

    if($proceed == false){
        trigger_error('User connection denied from: ' .
$clientIp, E_USER_WARNING);

        if($ip_silent == false){
            fm_set_msg(lng('Access denied. IP restriction
applicable'), 'error');
            fm_show_header_login();
            fm_show_message();
        }
        exit();
    }
}

// Checking if the user is logged in or not. If not, it will show the login
form.
if ($use_auth) {
    if (isset($_SESSION[FM_SESSION_ID]['logged'],
$auth_users[$_SESSION[FM_SESSION_ID]['logged']])) {
        // Logged
    } elseif (isset($_POST['fm_usr'], $_POST['fm_pwd'],
$_POST['token'])) {
        // Logging In
        sleep(1);
        if(function_exists('password_verify')) {
            if (isset($auth_users[$_POST['fm_usr']]) &&
isset($_POST['fm_pwd']) &&
password_verify($_POST['fm_pwd'],
$auth_users[$_POST['fm_usr']]) &&
verifyToken($_POST['token'])) {
                $_SESSION[FM_SESSION_ID]['logged'] =
$_POST['fm_usr'];
                fm_set_msg(lng('You are logged in'));
                fm_redirect(FM_SELF_URL);
            } else {
                unset($_SESSION[FM_SESSION_ID]['logged']);
                fm_set_msg(lng('Login failed. Invalid username or
password'), 'error');
                fm_redirect(FM_SELF_URL);
            }
        } else {
            fm_set_msg(lng('password_hash not supported, Upgrade PHP
version'), 'error');;
        }
    } else {
        // Form
        unset($_SESSION[FM_SESSION_ID]['logged']);
        fm_show_header_login();
        ?>
        <section class="h-100">
            <div class="container h-100">
                <div class="row justify-content-md-center
h-100">
                    <div class="card-wrapper">
                        <div class="card fat <?php echo
fm_get_theme(); ?>">
                            <div class="card-body">
                                <form class="form-signin"
action="" method="post"
autocomplete="off">
                                    <div class="mb-3">
                                       <div class="brand">
                                            <svg version="1.0"
xmlns="http://www.w3.org/2000/svg" M1008 width="100%"
height="80px" viewBox="0 0 238.000000 140.000000"
aria-label="H3K Tiny File Manager">
                                                <g
transform="translate(0.000000,140.000000)
scale(0.100000,-0.100000)" fill="#000000"
stroke="none">
                                                    <path d="M160
700 l0 -600 110 0 110 0 0 260 0 260 70 0 70 0 0 -260 0 -260 110 0 110 0 0
600 0 600 -110 0 -110 0 0 -260 0 -260 -70 0 -70 0 0 260 0 260 -110 0 -110 0
0 -600z"/>
                                                    <path
fill="#003500" d="M1008 1227 l-108 -72 0 -117 0 -118 110 0
110 0 0 110 0 110 70 0 70 0 0 -180 0 -180 -125 0 c-69 0 -125 -3 -125 -6 0
-3 23 -39 52 -80 l52 -74 73 0 73 0 0 -185 0 -185 -70 0 -70 0 0 115 0 115
-110 0 -110 0 0 -190 0 -190 181 0 181 0 109 73 108 72 1 181 0 181 -69 48
-68 49 68 50 69 49 0 249 0 248 -182 -1 -183 0 -107 -72z"/>
                                                    <path d="M1640
700 l0 -600 110 0 110 0 0 208 0 208 35 34 35 34 35 -34 35 -34 0 -208 0 -208
110 0 110 0 0 212 0 213 -87 87 -88 88 88 88 87 87 0 213 0 212 -110 0 -110 0
0 -208 0 -208 -70 -69 -70 -69 0 277 0 277 -110 0 -110 0 0
-600z"/></g>
                                            </svg>
                                        </div>
                                        <div
class="text-center">
                                            <h1
class="card-title"><?php echo APP_TITLE; ?></h1>
                                        </div>
                                    </div>
                                    <hr />
                                    <div class="mb-3">
                                        <label for="fm_usr"
class="pb-2"><?php echo lng('Username');
?></label>
                                        <input type="text"
class="form-control" id="fm_usr"
name="fm_usr" required autofocus>
                                    </div>

                                    <div class="mb-3">
                                        <label for="fm_pwd"
class="pb-2"><?php echo lng('Password');
?></label>
                                        <input type="password"
class="form-control" id="fm_pwd"
name="fm_pwd" required>
                                    </div>

                                    <div class="mb-3">
                                        <?php fm_show_message(); ?>
                                    </div>
                                    <input type="hidden"
name="token" value="<?php echo
htmlentities($_SESSION['token']); ?>" />
                                    <div class="mb-3">
                                        <button type="submit"
class="btn btn-success btn-block w-100 mt-4"
role="button">
                                            <?php echo
lng('Login'); ?>
                                        </button>
                                    </div>
                                </form>
                            </div>
                        </div>
                        <div class="footer text-center">
                            &mdash;&mdash; &copy;
                            <a
href="https://tinyfilemanager.github.io/"
target="_blank" class="text-decoration-none text-muted"
data-version="<?php echo VERSION; ?>">CCP
Programmers</a> &mdash;&mdash;
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <?php
        fm_show_footer_login();
        exit;
    }
}

// update root path
if ($use_auth &&
isset($_SESSION[FM_SESSION_ID]['logged'])) {
    $root_path =
isset($directories_users[$_SESSION[FM_SESSION_ID]['logged']]) ?
$directories_users[$_SESSION[FM_SESSION_ID]['logged']] :
$root_path;
}

// clean and check $root_path
$root_path = rtrim($root_path, '\\/');
$root_path = str_replace('\\', '/', $root_path);
if (!@is_dir($root_path)) {
    echo "<h1>".lng('Root path')."
\"{$root_path}\" ".lng('not found!')."
</h1>";
    exit;
}

defined('FM_SHOW_HIDDEN') || define('FM_SHOW_HIDDEN',
$show_hidden_files);
defined('FM_ROOT_PATH') || define('FM_ROOT_PATH',
$root_path);
defined('FM_LANG') || define('FM_LANG', $lang);
defined('FM_FILE_EXTENSION') ||
define('FM_FILE_EXTENSION', $allowed_file_extensions);
defined('FM_UPLOAD_EXTENSION') ||
define('FM_UPLOAD_EXTENSION', $allowed_upload_extensions);
defined('FM_EXCLUDE_ITEMS') ||
define('FM_EXCLUDE_ITEMS', (version_compare(PHP_VERSION,
'7.0.0', '<') ? serialize($exclude_items) :
$exclude_items));
defined('FM_DOC_VIEWER') || define('FM_DOC_VIEWER',
$online_viewer);
define('FM_READONLY', $global_readonly || ($use_auth &&
!empty($readonly_users) &&
isset($_SESSION[FM_SESSION_ID]['logged']) &&
in_array($_SESSION[FM_SESSION_ID]['logged'], $readonly_users)));
define('FM_IS_WIN', DIRECTORY_SEPARATOR == '\\');

// always use ?p=
if (!isset($_GET['p']) && empty($_FILES)) {
    fm_redirect(FM_SELF_URL . '?p=');
}

// get path
$p = isset($_GET['p']) ? $_GET['p'] :
(isset($_POST['p']) ? $_POST['p'] : '');

// clean path
$p = fm_clean_path($p);

// for ajax request - save
$input = file_get_contents('php://input');
$_POST = (strpos($input, 'ajax') != FALSE &&
strpos($input, 'save') != FALSE) ? json_decode($input, true) :
$_POST;

// instead globals vars
define('FM_PATH', $p);
define('FM_USE_AUTH', $use_auth);
define('FM_EDIT_FILE', $edit_files);
defined('FM_ICONV_INPUT_ENC') ||
define('FM_ICONV_INPUT_ENC', $iconv_input_encoding);
defined('FM_USE_HIGHLIGHTJS') ||
define('FM_USE_HIGHLIGHTJS', $use_highlightjs);
defined('FM_HIGHLIGHTJS_STYLE') ||
define('FM_HIGHLIGHTJS_STYLE', $highlightjs_style);
defined('FM_DATETIME_FORMAT') ||
define('FM_DATETIME_FORMAT', $datetime_format);

unset($p, $use_auth, $iconv_input_encoding, $use_highlightjs,
$highlightjs_style);

/*************************** ACTIONS ***************************/

// Handle all AJAX Request
if ((isset($_SESSION[FM_SESSION_ID]['logged'],
$auth_users[$_SESSION[FM_SESSION_ID]['logged']]) || !FM_USE_AUTH)
&& isset($_POST['ajax'], $_POST['token'])
&& !FM_READONLY) {
    if(!verifyToken($_POST['token'])) {
        header('HTTP/1.0 401 Unauthorized');
        die("Invalid Token.");
    }

    //search : get list of files from the current folder
    if(isset($_POST['type']) &&
$_POST['type']=="search") {
        $dir = $_POST['path'] == "." ? '':
$_POST['path'];
        $response = scan(fm_clean_path($dir),
$_POST['content']);
        echo json_encode($response);
        exit();
    }

    // save editor file
    if (isset($_POST['type']) && $_POST['type']
== "save") {
        // get current path
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }
        // check path
        if (!is_dir($path)) {
            fm_redirect(FM_SELF_URL . '?p=');
        }
        $file = $_GET['edit'];
        $file = fm_clean_path($file);
        $file = str_replace('/', '', $file);
        if ($file == '' || !is_file($path . '/' .
$file)) {
            fm_set_msg(lng('File not found'),
'error');
            $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
        }
        header('X-XSS-Protection:0');
        $file_path = $path . '/' . $file;

        $writedata = $_POST['content'];
        $fd = fopen($file_path, "w");
        $write_results = @fwrite($fd, $writedata);
        fclose($fd);
        if ($write_results === false){
            header("HTTP/1.1 500 Internal Server Error");
            die("Could Not Write File! - Check Permissions /
Ownership");
        }
        die(true);
    }

    // backup files
    if (isset($_POST['type']) && $_POST['type']
== "backup" && !empty($_POST['file'])) {
        $fileName = fm_clean_path($_POST['file']);
        $fullPath = FM_ROOT_PATH . '/';
        if (!empty($_POST['path'])) {
            $relativeDirPath = fm_clean_path($_POST['path']);
            $fullPath .= "{$relativeDirPath}/";
        }
        $date = date("dMy-His");
        $newFileName = "{$fileName}-{$date}.bak";
        $fullyQualifiedFileName = $fullPath . $fileName;
        try {
            if (!file_exists($fullyQualifiedFileName)) {
                throw new Exception("File {$fileName} not
found");
            }
            if (copy($fullyQualifiedFileName, $fullPath . $newFileName)) {
                echo "Backup {$newFileName} created";
            } else {
                throw new Exception("Could not copy file
{$fileName}");
            }
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

    // Save Config
    if (isset($_POST['type']) && $_POST['type']
== "settings") {
        global $cfg, $lang, $report_errors, $show_hidden_files, $lang_list,
$hide_Cols, $theme;
        $newLng = $_POST['js-language'];
        fm_get_translations([]);
        if (!array_key_exists($newLng, $lang_list)) {
            $newLng = 'en';
        }

        $erp = isset($_POST['js-error-report']) &&
$_POST['js-error-report'] == "true" ? true : false;
        $shf = isset($_POST['js-show-hidden']) &&
$_POST['js-show-hidden'] == "true" ? true : false;
        $hco = isset($_POST['js-hide-cols']) &&
$_POST['js-hide-cols'] == "true" ? true : false;
        $te3 = $_POST['js-theme-3'];

        if ($cfg->data['lang'] != $newLng) {
            $cfg->data['lang'] = $newLng;
            $lang = $newLng;
        }
        if ($cfg->data['error_reporting'] != $erp) {
            $cfg->data['error_reporting'] = $erp;
            $report_errors = $erp;
        }
        if ($cfg->data['show_hidden'] != $shf) {
            $cfg->data['show_hidden'] = $shf;
            $show_hidden_files = $shf;
        }
        if ($cfg->data['show_hidden'] != $shf) {
            $cfg->data['show_hidden'] = $shf;
            $show_hidden_files = $shf;
        }
        if ($cfg->data['hide_Cols'] != $hco) {
            $cfg->data['hide_Cols'] = $hco;
            $hide_Cols = $hco;
        }
        if ($cfg->data['theme'] != $te3) {
            $cfg->data['theme'] = $te3;
            $theme = $te3;
        }
        $cfg->save();
        echo true;
    }

    // new password hash
    if (isset($_POST['type']) && $_POST['type']
== "pwdhash") {
        $res = isset($_POST['inputPassword2']) &&
!empty($_POST['inputPassword2']) ?
password_hash($_POST['inputPassword2'], PASSWORD_DEFAULT) :
'';
        echo $res;
    }

    //upload using url
    if(isset($_POST['type']) && $_POST['type']
== "upload" && !empty($_REQUEST["uploadurl"]))
{
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }

         function event_callback ($message) {
            global $callback;
            echo json_encode($message);
        }

        function get_file_path () {
            global $path, $fileinfo, $temp_file;
            return $path."/".basename($fileinfo->name);
        }

        $url = !empty($_REQUEST["uploadurl"]) &&
preg_match("|^http(s)?://.+$|",
stripslashes($_REQUEST["uploadurl"])) ?
stripslashes($_REQUEST["uploadurl"]) : null;

        //prevent 127.* domain and known ports
        $domain = parse_url($url, PHP_URL_HOST);
        $port = parse_url($url, PHP_URL_PORT);
        $knownPorts = [22, 23, 25, 3306];

        if
(preg_match("/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/i",
$domain) || in_array($port, $knownPorts)) {
            $err = array("message" => "URL is not
allowed");
            event_callback(array("fail" => $err));
            exit();
        }

        $use_curl = false;
        $temp_file = tempnam(sys_get_temp_dir(), "upload-");
        $fileinfo = new stdClass();
        $fileinfo->name = trim(urldecode(basename($url)),
".\x00..\x20");

        $allowed = (FM_UPLOAD_EXTENSION) ? explode(',',
FM_UPLOAD_EXTENSION) : false;
        $ext = strtolower(pathinfo($fileinfo->name,
PATHINFO_EXTENSION));
        $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true;

        $err = false;

        if(!$isFileAllowed) {
            $err = array("message" => "File extension is
not allowed");
            event_callback(array("fail" => $err));
            exit();
        }

        if (!$url) {
            $success = false;
        } else if ($use_curl) {
            @$fp = fopen($temp_file, "w");
            @$ch = curl_init($url);
            curl_setopt($ch, CURLOPT_NOPROGRESS, false );
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_FILE, $fp);
            @$success = curl_exec($ch);
            $curl_info = curl_getinfo($ch);
            if (!$success) {
                $err = array("message" => curl_error($ch));
            }
            @curl_close($ch);
            fclose($fp);
            $fileinfo->size = $curl_info["size_download"];
            $fileinfo->type = $curl_info["content_type"];
        } else {
            $ctx = stream_context_create();
            @$success = copy($url, $temp_file, $ctx);
            if (!$success) {
                $err = error_get_last();
            }
        }

        if ($success) {
            $success = rename($temp_file, strtok(get_file_path(),
'?'));
        }

        if ($success) {
            event_callback(array("done" => $fileinfo));
        } else {
            unlink($temp_file);
            if (!$err) {
                $err = array("message" => "Invalid url
parameter");
            }
            event_callback(array("fail" => $err));
        }
    }
    exit();
}

// Delete file / folder
if (isset($_GET['del'], $_POST['token']) &&
!FM_READONLY) {
    $del = str_replace( '/', '', fm_clean_path(
$_GET['del'] ) );
    if ($del != '' && $del != '..' &&
$del != '.' && verifyToken($_POST['token'])) {
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }
        $is_dir = is_dir($path . '/' . $del);
        if (fm_rdelete($path . '/' . $del)) {
            $msg = $is_dir ? lng('Folder').'
<b>%s</b> '.lng('Deleted') :
lng('File').' <b>%s</b>
'.lng('Deleted');
            fm_set_msg(sprintf($msg, fm_enc($del)));
        } else {
            $msg = $is_dir ? lng('Folder').'
<b>%s</b> '.lng('not deleted') :
lng('File').' <b>%s</b> '.lng('not
deleted');
            fm_set_msg(sprintf($msg, fm_enc($del)), 'error');
        }
    } else {
        fm_set_msg(lng('Invalid file or folder name'),
'error');
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Create a new file/folder
if (isset($_POST['newfilename'], $_POST['newfile'],
$_POST['token']) && !FM_READONLY) {
    $type = urldecode($_POST['newfile']);
    $new = str_replace( '/', '', fm_clean_path(
strip_tags( $_POST['newfilename'] ) ) );
    if (fm_isvalid_filename($new) && $new != ''
&& $new != '..' && $new != '.'
&& verifyToken($_POST['token'])) {
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }
        if ($type == "file") {
            if (!file_exists($path . '/' . $new)) {
                if(fm_is_valid_ext($new)) {
                    @fopen($path . '/' . $new, 'w') or
die('Cannot open file:  ' . $new);
                    fm_set_msg(sprintf(lng('File').'
<b>%s</b> '.lng('Created'), fm_enc($new)));
                } else {
                    fm_set_msg(lng('File extension is not
allowed'), 'error');
                }
            } else {
                fm_set_msg(sprintf(lng('File').'
<b>%s</b> '.lng('already exists'),
fm_enc($new)), 'alert');
            }
        } else {
            if (fm_mkdir($path . '/' . $new, false) === true) {
                fm_set_msg(sprintf(lng('Folder').'
<b>%s</b> '.lng('Created'), $new));
            } elseif (fm_mkdir($path . '/' . $new, false) ===
$path . '/' . $new) {
                fm_set_msg(sprintf(lng('Folder').'
<b>%s</b> '.lng('already exists'),
fm_enc($new)), 'alert');
            } else {
                fm_set_msg(sprintf(lng('Folder').'
<b>%s</b> '.lng('not created'), fm_enc($new)),
'error');
            }
        }
    } else {
        fm_set_msg(lng('Invalid characters in file or folder
name'), 'error');
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Copy folder / file
if (isset($_GET['copy'], $_GET['finish']) &&
!FM_READONLY) {
    // from
    $copy = urldecode($_GET['copy']);
    $copy = fm_clean_path($copy);
    // empty path
    if ($copy == '') {
        fm_set_msg(lng('Source path not defined'),
'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }
    // abs path from
    $from = FM_ROOT_PATH . '/' . $copy;
    // abs path to
    $dest = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $dest .= '/' . FM_PATH;
    }
    $dest .= '/' . basename($from);
    // move?
    $move = isset($_GET['move']);
    $move = fm_clean_path(urldecode($move));
    // copy/move/duplicate
    if ($from != $dest) {
        $msg_from = trim(FM_PATH . '/' . basename($from),
'/');
        if ($move) { // Move and to != from so just perform move
            $rename = fm_rename($from, $dest);
            if ($rename) {
                fm_set_msg(sprintf(lng('Moved from').'
<b>%s</b> '.lng('to').'
<b>%s</b>', fm_enc($copy), fm_enc($msg_from)));
            } elseif ($rename === null) {
                fm_set_msg(lng('File or folder with this path already
exists'), 'alert');
            } else {
                fm_set_msg(sprintf(lng('Error while moving
from').' <b>%s</b> '.lng('to').'
<b>%s</b>', fm_enc($copy), fm_enc($msg_from)),
'error');
            }
        } else { // Not move and to != from so copy with original name
            if (fm_rcopy($from, $dest)) {
                fm_set_msg(sprintf(lng('Copied from').'
<b>%s</b> '.lng('to').'
<b>%s</b>', fm_enc($copy), fm_enc($msg_from)));
            } else {
                fm_set_msg(sprintf(lng('Error while copying
from').' <b>%s</b> '.lng('to').'
<b>%s</b>', fm_enc($copy), fm_enc($msg_from)),
'error');
            }
        }
    } else {
       if (!$move){ //Not move and to = from so duplicate
            $msg_from = trim(FM_PATH . '/' . basename($from),
'/');
            $fn_parts = pathinfo($from);
            $extension_suffix = '';
            if(!is_dir($from)){
               $extension_suffix =
'.'.$fn_parts['extension'];
            }
            //Create new name for duplicate
            $fn_duplicate =
$fn_parts['dirname'].'/'.$fn_parts['filename'].'-'.date('YmdHis').$extension_suffix;
            $loop_count = 0;
            $max_loop = 1000;
            // Check if a file with the duplicate name already exists, if
so, make new name (edge case...)
            while(file_exists($fn_duplicate) & $loop_count <
$max_loop){
               $fn_parts = pathinfo($fn_duplicate);
               $fn_duplicate =
$fn_parts['dirname'].'/'.$fn_parts['filename'].'-copy'.$extension_suffix;
               $loop_count++;
            }
            if (fm_rcopy($from, $fn_duplicate, False)) {
                fm_set_msg(sprintf('Copied from <b>%s</b>
to <b>%s</b>', fm_enc($copy), fm_enc($fn_duplicate)));
            } else {
                fm_set_msg(sprintf('Error while copying from
<b>%s</b> to <b>%s</b>', fm_enc($copy),
fm_enc($fn_duplicate)), 'error');
            }
       }
       else{
           fm_set_msg(lng('Paths must be not equal'),
'alert');
       }
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Mass copy files/ folders
if (isset($_POST['file'], $_POST['copy_to'],
$_POST['finish'], $_POST['token']) &&
!FM_READONLY) {

    if(!verifyToken($_POST['token'])) {
        fm_set_msg(lng('Invalid Token.'), 'error');
    }
    
    // from
    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }
    // to
    $copy_to_path = FM_ROOT_PATH;
    $copy_to = fm_clean_path($_POST['copy_to']);
    if ($copy_to != '') {
        $copy_to_path .= '/' . $copy_to;
    }
    if ($path == $copy_to_path) {
        fm_set_msg(lng('Paths must be not equal'),
'alert');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }
    if (!is_dir($copy_to_path)) {
        if (!fm_mkdir($copy_to_path, true)) {
            fm_set_msg('Unable to create destination folder',
'error');
            $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
        }
    }
    // move?
    $move = isset($_POST['move']);
    // copy/move
    $errors = 0;
    $files = $_POST['file'];
    if (is_array($files) && count($files)) {
        foreach ($files as $f) {
            if ($f != '') {
                $f = fm_clean_path($f);
                // abs path from
                $from = $path . '/' . $f;
                // abs path to
                $dest = $copy_to_path . '/' . $f;
                // do
                if ($move) {
                    $rename = fm_rename($from, $dest);
                    if ($rename === false) {
                        $errors++;
                    }
                } else {
                    if (!fm_rcopy($from, $dest)) {
                        $errors++;
                    }
                }
            }
        }
        if ($errors == 0) {
            $msg = $move ? 'Selected files and folders moved' :
'Selected files and folders copied';
            fm_set_msg($msg);
        } else {
            $msg = $move ? 'Error while moving items' :
'Error while copying items';
            fm_set_msg($msg, 'error');
        }
    } else {
        fm_set_msg(lng('Nothing selected'), 'alert');
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Rename
if (isset($_POST['rename_from'], $_POST['rename_to'],
$_POST['token']) && !FM_READONLY) {
    if(!verifyToken($_POST['token'])) {
        fm_set_msg("Invalid Token.", 'error');
    }
    // old name
    $old = urldecode($_POST['rename_from']);
    $old = fm_clean_path($old);
    $old = str_replace('/', '', $old);
    // new name
    $new = urldecode($_POST['rename_to']);
    $new = fm_clean_path(strip_tags($new));
    $new = str_replace('/', '', $new);
    // path
    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }
    // rename
    if (fm_isvalid_filename($new) && $old != ''
&& $new != '') {
        if (fm_rename($path . '/' . $old, $path . '/' .
$new)) {
            fm_set_msg(sprintf(lng('Renamed from').'
<b>%s</b> '. lng('to').'
<b>%s</b>', fm_enc($old), fm_enc($new)));
        } else {
            fm_set_msg(sprintf(lng('Error while renaming
from').' <b>%s</b> '. lng('to').'
<b>%s</b>', fm_enc($old), fm_enc($new)),
'error');
        }
    } else {
        fm_set_msg(lng('Invalid characters in file name'),
'error');
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Download
if (isset($_GET['dl'], $_POST['token'])) {
    if(!verifyToken($_POST['token'])) {
        fm_set_msg("Invalid Token.", 'error');
    }

    $dl = urldecode($_GET['dl']);
    $dl = fm_clean_path($dl);
    $dl = str_replace('/', '', $dl);
    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }
    if ($dl != '' && is_file($path . '/' .
$dl)) {
        fm_download_file($path . '/' . $dl, $dl, 1024);
        exit;
    } else {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }
}

// Upload
if (!empty($_FILES) && !FM_READONLY) {
    if(isset($_POST['token'])) {
        if(!verifyToken($_POST['token'])) {
            $response = array ('status' =>
'error','info' => "Invalid Token.");
            echo json_encode($response); exit();
        }
    } else {
        $response = array ('status' =>
'error','info' => "Token Missing.");
        echo json_encode($response); exit();
    }

    $chunkIndex = $_POST['dzchunkindex'];
    $chunkTotal = $_POST['dztotalchunkcount'];
    $fullPathInput = fm_clean_path($_REQUEST['fullpath']);

    $f = $_FILES;
    $path = FM_ROOT_PATH;
    $ds = DIRECTORY_SEPARATOR;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }

    $errors = 0;
    $uploads = 0;
    $allowed = (FM_UPLOAD_EXTENSION) ? explode(',',
FM_UPLOAD_EXTENSION) : false;
    $response = array (
        'status' => 'error',
        'info'   => 'Oops! Try again'
    );

    $filename = $f['file']['name'];
    $tmp_name = $f['file']['tmp_name'];
    $ext = pathinfo($filename, PATHINFO_FILENAME) != '' ?
strtolower(pathinfo($filename, PATHINFO_EXTENSION)) : '';
    $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true;

    if(!fm_isvalid_filename($filename) &&
!fm_isvalid_filename($fullPathInput)) {
        $response = array (
            'status'    => 'error',
            'info'      => "Invalid File name!",
        );
        echo json_encode($response); exit();
    }

    $targetPath = $path . $ds;
    if ( is_writable($targetPath) ) {
        $fullPath = $path . '/' . $fullPathInput;
        $folder = substr($fullPath, 0, strrpos($fullPath, "/"));

        if (!is_dir($folder)) {
            $old = umask(0);
            mkdir($folder, 0777, true);
            umask($old);
        }

        if (empty($f['file']['error']) &&
!empty($tmp_name) && $tmp_name != 'none' &&
$isFileAllowed) {
            if ($chunkTotal){
                $out = @fopen("{$fullPath}.part", $chunkIndex ==
0 ? "wb" : "ab");
                if ($out) {
                    $in = @fopen($tmp_name, "rb");
                    if ($in) {
                        if (PHP_VERSION_ID < 80009) {
                            // workaround
https://bugs.php.net/bug.php?id=81145
                            do {
                                for (;;) {
                                    $buff = fread($in, 4096);
                                    if ($buff === false || $buff ===
'') {
                                        break;
                                    }
                                    fwrite($out, $buff);
                                }
                            } while (!feof($in));
                        } else {
                            stream_copy_to_stream($in, $out);
                        }
                        $response = array (
                            'status'    =>
'success',
                            'info' => "file upload
successful"
                        );
                    } else {
                        $response = array (
                        'status'    => 'error',
                        'info' => "failed to open output
stream",
                        'errorDetails' => error_get_last()
                        );
                    }
                    @fclose($in);
                    @fclose($out);
                    @unlink($tmp_name);

                    $response = array (
                        'status'    => 'success',
                        'info' => "file upload
successful"
                    );
                } else {
                    $response = array (
                        'status'    => 'error',
                        'info' => "failed to open output
stream"
                        );
                }

                if ($chunkIndex == $chunkTotal - 1) {
                    if (file_exists ($fullPath)) {
                        $ext_1 = $ext ? '.'.$ext : '';
                        $fullPathTarget = $path . '/' .
basename($fullPathInput, $ext_1) .'_'. date('ymdHis').
$ext_1;
                    } else {
                        $fullPathTarget = $fullPath;
                    }
                    rename("{$fullPath}.part", $fullPathTarget);
                }

            } else if (move_uploaded_file($tmp_name, $fullPath)) {
                // Be sure that the file has been uploaded
                if ( file_exists($fullPath) ) {
                    $response = array (
                        'status'    => 'success',
                        'info' => "file upload
successful"
                    );
                } else {
                    $response = array (
                        'status' => 'error',
                        'info'   => 'Couldn\'t
upload the requested file.'
                    );
                }
            } else {
                $response = array (
                    'status'    => 'error',
                    'info'      => "Error while uploading
files. Uploaded files $uploads",
                );
            }
        }
    } else {
        $response = array (
            'status' => 'error',
            'info'   => 'The specified folder for upload
isn\'t writeable.'
        );
    }
    // Return the response
    echo json_encode($response);
    exit();
}

// Mass deleting
if (isset($_POST['group'], $_POST['delete'],
$_POST['token']) && !FM_READONLY) {

    if(!verifyToken($_POST['token'])) {
        fm_set_msg(lng("Invalid Token."), 'error');
    }

    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }

    $errors = 0;
    $files = $_POST['file'];
    if (is_array($files) && count($files)) {
        foreach ($files as $f) {
            if ($f != '') {
                $new_path = $path . '/' . $f;
                if (!fm_rdelete($new_path)) {
                    $errors++;
                }
            }
        }
        if ($errors == 0) {
            fm_set_msg(lng('Selected files and folder
deleted'));
        } else {
            fm_set_msg(lng('Error while deleting items'),
'error');
        }
    } else {
        fm_set_msg(lng('Nothing selected'), 'alert');
    }

    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Pack files zip, tar
if (isset($_POST['group'], $_POST['token']) &&
(isset($_POST['zip']) || isset($_POST['tar']))
&& !FM_READONLY) {

    if(!verifyToken($_POST['token'])) {
        fm_set_msg(lng("Invalid Token."), 'error');
    }

    $path = FM_ROOT_PATH;
    $ext = 'zip';
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }

    //set pack type
    $ext = isset($_POST['tar']) ? 'tar' :
'zip';

    if (($ext == "zip" &&
!class_exists('ZipArchive')) || ($ext == "tar"
&& !class_exists('PharData'))) {
        fm_set_msg(lng('Operations with archives are not
available'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    $files = $_POST['file'];
    $sanitized_files = array();

    // clean path
    foreach($files as $file){
        array_push($sanitized_files, fm_clean_path($file));
    }
    
    $files = $sanitized_files;
    
    if (!empty($files)) {
        chdir($path);

        if (count($files) == 1) {
            $one_file = reset($files);
            $one_file = basename($one_file);
            $zipname = $one_file . '_' .
date('ymd_His') . '.'.$ext;
        } else {
            $zipname = 'archive_' . date('ymd_His') .
'.'.$ext;
        }

        if($ext == 'zip') {
            $zipper = new FM_Zipper();
            $res = $zipper->create($zipname, $files);
        } elseif ($ext == 'tar') {
            $tar = new FM_Zipper_Tar();
            $res = $tar->create($zipname, $files);
        }

        if ($res) {
            fm_set_msg(sprintf(lng('Archive').'
<b>%s</b> '.lng('Created'), fm_enc($zipname)));
        } else {
            fm_set_msg(lng('Archive not created'),
'error');
        }
    } else {
        fm_set_msg(lng('Nothing selected'), 'alert');
    }

    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Unpack zip, tar
if (isset($_POST['unzip'], $_POST['token']) &&
!FM_READONLY) {

    if(!verifyToken($_POST['token'])) {
        fm_set_msg(lng("Invalid Token."), 'error');
    }

    $unzip = urldecode($_POST['unzip']);
    $unzip = fm_clean_path($unzip);
    $unzip = str_replace('/', '', $unzip);
    $isValid = false;

    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }

    if ($unzip != '' && is_file($path . '/' .
$unzip)) {
        $zip_path = $path . '/' . $unzip;
        $ext = pathinfo($zip_path, PATHINFO_EXTENSION);
        $isValid = true;
    } else {
        fm_set_msg(lng('File not found'), 'error');
    }

    if (($ext == "zip" &&
!class_exists('ZipArchive')) || ($ext == "tar"
&& !class_exists('PharData'))) {
        fm_set_msg(lng('Operations with archives are not
available'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    if ($isValid) {
        //to folder
        $tofolder = '';
        if (isset($_POST['tofolder'])) {
            $tofolder = pathinfo($zip_path, PATHINFO_FILENAME);
            if (fm_mkdir($path . '/' . $tofolder, true)) {
                $path .= '/' . $tofolder;
            }
        }

        if($ext == "zip") {
            $zipper = new FM_Zipper();
            $res = $zipper->unzip($zip_path, $path);
        } elseif ($ext == "tar") {
            try {
                $gzipper = new PharData($zip_path);
                if (@$gzipper->extractTo($path,null, true)) {
                    $res = true;
                } else {
                    $res = false;
                }
            } catch (Exception $e) {
                //TODO:: need to handle the error
                $res = true;
            }
        }

        if ($res) {
            fm_set_msg(lng('Archive unpacked'));
        } else {
            fm_set_msg(lng('Archive not unpacked'),
'error');
        }
    } else {
        fm_set_msg(lng('File not found'), 'error');
    }
    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

// Change Perms (not for Windows)
if (isset($_POST['chmod'], $_POST['token']) &&
!FM_READONLY && !FM_IS_WIN) {

    if(!verifyToken($_POST['token'])) {
        fm_set_msg(lng("Invalid Token."), 'error');
    }
    
    $path = FM_ROOT_PATH;
    if (FM_PATH != '') {
        $path .= '/' . FM_PATH;
    }

    $file = $_POST['chmod'];
    $file = fm_clean_path($file);
    $file = str_replace('/', '', $file);
    if ($file == '' || (!is_file($path . '/' . $file)
&& !is_dir($path . '/' . $file))) {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    $mode = 0;
    if (!empty($_POST['ur'])) {
        $mode |= 0400;
    }
    if (!empty($_POST['uw'])) {
        $mode |= 0200;
    }
    if (!empty($_POST['ux'])) {
        $mode |= 0100;
    }
    if (!empty($_POST['gr'])) {
        $mode |= 0040;
    }
    if (!empty($_POST['gw'])) {
        $mode |= 0020;
    }
    if (!empty($_POST['gx'])) {
        $mode |= 0010;
    }
    if (!empty($_POST['or'])) {
        $mode |= 0004;
    }
    if (!empty($_POST['ow'])) {
        $mode |= 0002;
    }
    if (!empty($_POST['ox'])) {
        $mode |= 0001;
    }

    if (@chmod($path . '/' . $file, $mode)) {
        fm_set_msg(lng('Permissions changed'));
    } else {
        fm_set_msg(lng('Permissions not changed'),
'error');
    }

    $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
}

/*************************** ACTIONS ***************************/

// get current path
$path = FM_ROOT_PATH;
if (FM_PATH != '') {
    $path .= '/' . FM_PATH;
}

// check path
if (!is_dir($path)) {
    fm_redirect(FM_SELF_URL . '?p=');
}

// get parent folder
$parent = fm_get_parent_path(FM_PATH);

$objects = is_readable($path) ? scandir($path) : array();
$folders = array();
$files = array();
$current_path = array_slice(explode("/",$path), -1)[0];
if (is_array($objects) && fm_is_exclude_items($current_path)) {
    foreach ($objects as $file) {
        if ($file == '.' || $file == '..') {
            continue;
        }
        if (!FM_SHOW_HIDDEN && substr($file, 0, 1) ===
'.') {
            continue;
        }
        $new_path = $path . '/' . $file;
        if (@is_file($new_path) && fm_is_exclude_items($file)) {
            $files[] = $file;
        } elseif (@is_dir($new_path) && $file != '.'
&& $file != '..' && fm_is_exclude_items($file))
{
            $folders[] = $file;
        }
    }
}

if (!empty($files)) {
    natcasesort($files);
}
if (!empty($folders)) {
    natcasesort($folders);
}

// upload form
if (isset($_GET['upload']) && !FM_READONLY) {
    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path
    //get the allowed file extensions
    function getUploadExt() {
        $extArr = explode(',', FM_UPLOAD_EXTENSION);
        if(FM_UPLOAD_EXTENSION && $extArr) {
            array_walk($extArr, function(&$x) {$x =
".$x";});
            return implode(',', $extArr);
        }
        return '';
    }
    ?>
    <?php print_external('css-dropzone'); ?>
    <div class="path">

        <div class="card mb-2 fm-upload-wrapper <?php echo
fm_get_theme(); ?>">
            <div class="card-header">
                <ul class="nav nav-tabs card-header-tabs">
                    <li class="nav-item">
                        <a class="nav-link active"
href="#fileUploader"
data-target="#fileUploader"><i class="fa
fa-arrow-circle-o-up"></i> <?php echo
lng('UploadingFiles') ?></a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link"
href="#urlUploader" class="js-url-upload"
data-target="#urlUploader"><i class="fa
fa-link"></i> <?php echo lng('Upload from URL')
?></a>
                    </li>
                </ul>
            </div>
            <div class="card-body">
                <p class="card-text">
                    <a href="?p=<?php echo FM_PATH ?>"
class="float-right"><i class="fa
fa-chevron-circle-left go-back"></i> <?php echo
lng('Back')?></a>
                    <strong><?php echo
lng('DestinationFolder') ?></strong>: <?php echo
fm_enc(fm_convert_win(FM_PATH)) ?>
                </p>

                <form action="<?php echo
htmlspecialchars(FM_SELF_URL) . '?p=' . fm_enc(FM_PATH)
?>" class="dropzone card-tabs-container"
id="fileUploader" enctype="multipart/form-data">
                    <input type="hidden" name="p"
value="<?php echo fm_enc(FM_PATH) ?>">
                    <input type="hidden"
name="fullpath" id="fullpath" value="<?php echo
fm_enc(FM_PATH) ?>">
                    <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                    <div class="fallback">
                        <input name="file"
type="file" multiple/>
                    </div>
                </form>

                <div class="upload-url-wrapper card-tabs-container
hidden" id="urlUploader">
                    <form id="js-form-url-upload"
class="row row-cols-lg-auto g-3 align-items-center"
onsubmit="return upload_from_url(this);" method="POST"
action="">
                        <input type="hidden"
name="type" value="upload"
aria-label="hidden" aria-hidden="true">
                        <input type="url"
placeholder="URL" name="uploadurl" required
class="form-control" style="width: 80%">
                        <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                        <button type="submit" class="btn
btn-primary ms-3"><?php echo lng('Upload')
?></button>
                        <div
class="lds-facebook"><div></div><div></div><div></div></div>
                    </form>
                    <div id="js-url-upload__list"
class="col-9 mt-3"></div>
                </div>
            </div>
        </div>
    </div>
    <?php print_external('js-dropzone'); ?>
    <script>
        Dropzone.options.fileUploader = {
            chunking: true,
            chunkSize: <?php echo UPLOAD_CHUNK_SIZE; ?>,
            forceChunking: true,
            retryChunks: true,
            retryChunksLimit: 3,
            parallelUploads: 1,
            parallelChunkUploads: false,
            timeout: 120000,
            maxFilesize: "<?php echo MAX_UPLOAD_SIZE; ?>",
            acceptedFiles : "<?php echo getUploadExt()
?>",
            init: function () {
                this.on("sending", function (file, xhr, formData)
{
                    let _path = (file.fullPath) ? file.fullPath :
file.name;
                    document.getElementById("fullpath").value =
_path;
                    xhr.ontimeout = (function() {
                        toast('Error: Server Timeout');
                    });
                }).on("success", function (res) {
                    try {
                        let _response = JSON.parse(res.xhr.response);

                        if(_response.status == "error") {
                            toast(_response.info);
                        }
                    } catch (e) {
                        toast("Error: Invalid JSON response");
                    }
                }).on("error", function(file, response) {
                    toast(response);
                });
            }
        }
    </script>
    <?php
    fm_show_footer();
    exit;
}

// copy form POST
if (isset($_POST['copy']) && !FM_READONLY) {
    $copy_files = isset($_POST['file']) ?
$_POST['file'] : null;
    if (!is_array($copy_files) || empty($copy_files)) {
        fm_set_msg(lng('Nothing selected'), 'alert');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path
    ?>
    <div class="path">
        <div class="card <?php echo fm_get_theme();
?>">
            <div class="card-header">
                <h6><?php echo lng('Copying')
?></h6>
            </div>
            <div class="card-body">
                <form action="" method="post">
                    <input type="hidden" name="p"
value="<?php echo fm_enc(FM_PATH) ?>">
                    <input type="hidden"
name="finish" value="1">
                    <?php
                    foreach ($copy_files as $cf) {
                        echo '<input type="hidden"
name="file[]" value="' . fm_enc($cf) .
'">' . PHP_EOL;
                    }
                    ?>
                    <p
class="break-word"><strong><?php echo
lng('Files') ?></strong>: <b><?php echo
implode('</b>, <b>', $copy_files)
?></b></p>
                    <p
class="break-word"><strong><?php echo
lng('SourceFolder') ?></strong>: <?php echo
fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . FM_PATH))
?><br>
                        <label
for="inp_copy_to"><strong><?php echo
lng('DestinationFolder') ?></strong>:</label>
                        <?php echo FM_ROOT_PATH ?>/<input
type="text" name="copy_to" id="inp_copy_to"
value="<?php echo fm_enc(FM_PATH) ?>">
                    </p>
                    <p class="custom-checkbox
custom-control"><input type="checkbox"
name="move" value="1" id="js-move-files"
class="custom-control-input"><label
for="js-move-files" class="custom-control-label
ms-2"> <?php echo lng('Move')
?></label></p>
                    <p>
                        <b><a href="?p=<?php echo
urlencode(FM_PATH) ?>" class="btn
btn-outline-danger"><i class="fa
fa-times-circle"></i> <?php echo lng('Cancel')
?></a></b>&nbsp;
                        <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                        <button type="submit" class="btn
btn-success"><i class="fa
fa-check-circle"></i> <?php echo lng('Copy')
?></button> 
                    </p>
                </form>
            </div>
        </div>
    </div>
    <?php
    fm_show_footer();
    exit;
}

// copy form
if (isset($_GET['copy']) &&
!isset($_GET['finish']) && !FM_READONLY) {
    $copy = $_GET['copy'];
    $copy = fm_clean_path($copy);
    if ($copy == '' || !file_exists(FM_ROOT_PATH . '/'
. $copy)) {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path
    ?>
    <div class="path">
        <p><b>Copying</b></p>
        <p class="break-word">
            <strong>Source path:</strong> <?php echo
fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . $copy))
?><br>
            <strong>Destination folder:</strong> <?php echo
fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . FM_PATH)) ?>
        </p>
        <p>
            <b><a href="?p=<?php echo urlencode(FM_PATH)
?>&amp;copy=<?php echo urlencode($copy)
?>&amp;finish=1"><i class="fa
fa-check-circle"></i> Copy</a></b> &nbsp;
            <b><a href="?p=<?php echo urlencode(FM_PATH)
?>&amp;copy=<?php echo urlencode($copy)
?>&amp;finish=1&amp;move=1"><i class="fa
fa-check-circle"></i> Move</a></b> &nbsp;
            <b><a href="?p=<?php echo urlencode(FM_PATH)
?>" class="text-danger"><i class="fa
fa-times-circle"></i> Cancel</a></b>
        </p>
        <p><i><?php echo lng('Select folder')
?></i></p>
        <ul class="folders break-word">
            <?php
            if ($parent !== false) {
                ?>
                <li><a href="?p=<?php echo
urlencode($parent) ?>&amp;copy=<?php echo urlencode($copy)
?>"><i class="fa
fa-chevron-circle-left"></i> ..</a></li>
                <?php
            }
            foreach ($folders as $f) {
                ?>
                <li>
                    <a href="?p=<?php echo
urlencode(trim(FM_PATH . '/' . $f, '/'))
?>&amp;copy=<?php echo urlencode($copy) ?>"><i
class="fa fa-folder-o"></i> <?php echo
fm_convert_win($f) ?></a></li>
                <?php
            }
            ?>
        </ul>
    </div>
    <?php
    fm_show_footer();
    exit;
}

if (isset($_GET['settings']) && !FM_READONLY) {
    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path
    global $cfg, $lang, $lang_list;
    ?>

    <div class="col-md-8 offset-md-2 pt-3">
        <div class="card mb-2 <?php echo fm_get_theme();
?>">
            <h6 class="card-header d-flex
justify-content-between">
                <span><i class="fa fa-cog"></i>
 <?php echo lng('Settings') ?></span>
                <a href="?p=<?php echo FM_PATH ?>"
class="text-danger"><i class="fa
fa-times-circle-o"></i> <?php echo
lng('Cancel')?></a>
            </h6>
            <div class="card-body">
                <form id="js-settings-form"
action="" method="post" data-type="ajax"
onsubmit="return save_settings(this)">
                    <input type="hidden" name="type"
value="settings" aria-label="hidden"
aria-hidden="true">
                    <div class="form-group row">
                        <label for="js-language"
class="col-sm-3 col-form-label"><?php echo
lng('Language') ?></label>
                        <div class="col-sm-5">
                            <select class="form-select"
id="js-language" name="js-language">
                                <?php
                                function getSelected($l) {
                                    global $lang;
                                    return ($lang == $l) ?
'selected' : '';
                                }
                                foreach ($lang_list as $k => $v) {
                                    echo "<option
value='$k'
".getSelected($k).">$v</option>";
                                }
                                ?>
                            </select>
                        </div>
                    </div>
                    <div class="mt-3 mb-3 row ">
                        <label for="js-error-report"
class="col-sm-3 col-form-label"><?php echo
lng('ErrorReporting') ?></label>
                        <div class="col-sm-9">
                            <div class="form-check
form-switch">
                              <input class="form-check-input"
type="checkbox" role="switch"
id="js-error-report" name="js-error-report"
value="true" <?php echo $report_errors ? 'checked' :
''; ?> />
                            </div>
                        </div>
                    </div>

                    <div class="mb-3 row">
                        <label for="js-show-hidden"
class="col-sm-3 col-form-label"><?php echo
lng('ShowHiddenFiles') ?></label>
                        <div class="col-sm-9">
                            <div class="form-check
form-switch">
                              <input class="form-check-input"
type="checkbox" role="switch"
id="js-show-hidden" name="js-show-hidden"
value="true" <?php echo $show_hidden_files ?
'checked' : ''; ?> />
                            </div>
                        </div>
                    </div>

                    <div class="mb-3 row">
                        <label for="js-hide-cols"
class="col-sm-3 col-form-label"><?php echo
lng('HideColumns') ?></label>
                        <div class="col-sm-9">
                            <div class="form-check
form-switch">
                              <input class="form-check-input"
type="checkbox" role="switch"
id="js-hide-cols" name="js-hide-cols"
value="true" <?php echo $hide_Cols ? 'checked' :
''; ?> />
                            </div>
                        </div>
                    </div>

                    <div class="mb-3 row">
                        <label for="js-3-1"
class="col-sm-3 col-form-label"><?php echo
lng('Theme') ?></label>
                        <div class="col-sm-5">
                            <select class="form-select w-100"
id="js-3-0" name="js-theme-3">
                                <option value='light' <?php
if($theme == "light"){echo "selected";}
?>><?php echo lng('light') ?></option>
                                <option value='dark' <?php
if($theme == "dark"){echo "selected";}
?>><?php echo lng('dark') ?></option>
                            </select>
                        </div>
                    </div>

                    <div class="mb-3 row">
                        <div class="col-sm-10">
                            <button type="submit"
class="btn btn-success"> <i class="fa
fa-check-circle"></i> <?php echo lng('Save');
?></button>
                        </div>
                    </div>

                </form>
            </div>
        </div>
    </div>
    <?php
    fm_show_footer();
    exit;
}

if (isset($_GET['help'])) {
    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path
    global $cfg, $lang;
    ?>

    <div class="col-md-8 offset-md-2 pt-3">
        <div class="card mb-2 <?php echo fm_get_theme();
?>">
            <h6 class="card-header d-flex
justify-content-between">
                <span><i class="fa
fa-exclamation-circle"></i> <?php echo
lng('Help') ?></span>
                <a href="?p=<?php echo FM_PATH ?>"
class="text-danger"><i class="fa
fa-times-circle-o"></i> <?php echo
lng('Cancel')?></a>
            </h6>
            <div class="card-body">
                <div class="row">
                    <div class="col-xs-12 col-sm-6">
                        <p><h3><a
href="https://github.com/prasathmani/tinyfilemanager"
target="_blank" class="app-v-title"> Tiny File
Manager <?php echo VERSION; ?></a></h3></p>
                        <p>Author: Prasath Mani</p>
                        <p>Mail Us: <a
href="mailto:ccpprogrammers@gmail.com">ccpprogrammers[at]gmail.com</a>
</p>
                    </div>
                    <div class="col-xs-12 col-sm-6">
                        <div class="card">
                            <ul class="list-group
list-group-flush">
                                <li
class="list-group-item"><a
href="https://github.com/prasathmani/tinyfilemanager/wiki"
target="_blank"><i class="fa
fa-question-circle"></i> <?php echo lng('Help
Documents') ?> </a> </li>
                                <li
class="list-group-item"><a
href="https://github.com/prasathmani/tinyfilemanager/issues"
target="_blank"><i class="fa
fa-bug"></i> <?php echo lng('Report Issue')
?></a></li>
                                <?php if(!FM_READONLY) { ?>
                                <li
class="list-group-item"><a
href="javascript:show_new_pwd();"><i class="fa
fa-lock"></i> <?php echo lng('Generate new password
hash') ?></a></li>
                                <?php } ?>
                            </ul>
                        </div>
                    </div>
                </div>
                <div class="row js-new-pwd hidden mt-2">
                    <div class="col-12">
                        <form class="form-inline"
onsubmit="return new_password_hash(this)" method="POST"
action="">
                            <input type="hidden"
name="type" value="pwdhash"
aria-label="hidden" aria-hidden="true">
                            <div class="form-group mb-2">
                                <label
for="staticEmail2"><?php echo lng('Generate new
password hash') ?></label>
                            </div>
                            <div class="form-group mx-sm-3
mb-2">
                                <label for="inputPassword2"
class="sr-only"><?php echo lng('Password')
?></label>
                                <input type="text"
class="form-control btn-sm" id="inputPassword2"
name="inputPassword2" placeholder="<?php echo
lng('Password') ?>" required>
                            </div>
                            <button type="submit"
class="btn btn-success btn-sm mb-2"><?php echo
lng('Generate') ?></button>
                        </form>
                        <textarea class="form-control"
rows="2" readonly
id="js-pwd-result"></textarea>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <?php
    fm_show_footer();
    exit;
}

// file viewer
if (isset($_GET['view'])) {
    $file = $_GET['view'];
    $file = fm_clean_path($file, false);
    $file = str_replace('/', '', $file);
    if ($file == '' || !is_file($path . '/' . $file) ||
!fm_is_exclude_items($file)) {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path

    $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ?
'/' . FM_PATH : '') . '/' . $file);
    $file_path = $path . '/' . $file;

    $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
    $mime_type = fm_get_mime_type($file_path);
    $filesize_raw = fm_get_size($file_path);
    $filesize = fm_get_filesize($filesize_raw);

    $is_zip = false;
    $is_gzip = false;
    $is_image = false;
    $is_audio = false;
    $is_video = false;
    $is_text = false;
    $is_onlineViewer = false;

    $view_title = 'File';
    $filenames = false; // for zip
    $content = ''; // for text
    $online_viewer = strtolower(FM_DOC_VIEWER);

    if($online_viewer && $online_viewer !== 'false'
&& in_array($ext, fm_get_onlineViewer_exts())){
        $is_onlineViewer = true;
    }
    elseif ($ext == 'zip' || $ext == 'tar') {
        $is_zip = true;
        $view_title = 'Archive';
        $filenames = fm_get_zif_info($file_path, $ext);
    } elseif (in_array($ext, fm_get_image_exts())) {
        $is_image = true;
        $view_title = 'Image';
    } elseif (in_array($ext, fm_get_audio_exts())) {
        $is_audio = true;
        $view_title = 'Audio';
    } elseif (in_array($ext, fm_get_video_exts())) {
        $is_video = true;
        $view_title = 'Video';
    } elseif (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0,
4) == 'text' || in_array($mime_type, fm_get_text_mimes())) {
        $is_text = true;
        $content = file_get_contents($file_path);
    }

    ?>
    <div class="row">
        <div class="col-12">
            <p class="break-word"><b><?php echo
lng($view_title) ?> "<?php echo fm_enc(fm_convert_win($file))
?>"</b></p>
            <p class="break-word">
                <?php $display_path = fm_get_display_path($file_path);
?>
                <strong><?php echo
$display_path['label']; ?>:</strong> <?php echo
$display_path['path']; ?><br>
                <strong>File size:</strong> <?php echo
($filesize_raw <= 1000) ? "$filesize_raw bytes" : $filesize;
?><br>
                <strong>MIME-type:</strong> <?php echo
$mime_type ?><br>
                <?php
                // ZIP info
                if (($is_zip || $is_gzip) && $filenames !== false)
{
                    $total_files = 0;
                    $total_comp = 0;
                    $total_uncomp = 0;
                    foreach ($filenames as $fn) {
                        if (!$fn['folder']) {
                            $total_files++;
                        }
                        $total_comp += $fn['compressed_size'];
                        $total_uncomp += $fn['filesize'];
                    }
                    ?>
                    <?php echo lng('Files in archive') ?>:
<?php echo $total_files ?><br>
                    <?php echo lng('Total size') ?>:
<?php echo fm_get_filesize($total_uncomp) ?><br>
                    <?php echo lng('Size in archive') ?>:
<?php echo fm_get_filesize($total_comp) ?><br>
                    <?php echo lng('Compression') ?>:
<?php echo round(($total_comp / max($total_uncomp, 1)) * 100)
?>%<br>
                    <?php
                }
                // Image info
                if ($is_image) {
                    $image_size = getimagesize($file_path);
                    echo '<strong>'.lng('Image
size').':</strong> ' . (isset($image_size[0]) ?
$image_size[0] : '0') . ' x ' . (isset($image_size[1])
? $image_size[1] : '0') . '<br>';
                }
                // Text info
                if ($is_text) {
                    $is_utf8 = fm_is_utf8($content);
                    if (function_exists('iconv')) {
                        if (!$is_utf8) {
                            $content = iconv(FM_ICONV_INPUT_ENC,
'UTF-8//IGNORE', $content);
                        }
                    }
                    echo
'<strong>'.lng('Charset').':</strong>
' . ($is_utf8 ? 'utf-8' : '8 bit') .
'<br>';
                }
                ?>
            </p>
            <div class="d-flex align-items-center mb-3">
                <form method="post" class="d-inline
ms-2" action="?p=<?php echo urlencode(FM_PATH)
?>&amp;dl=<?php echo urlencode($file) ?>">
                    <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                    <button type="submit" class="btn
btn-link text-decoration-none fw-bold p-0"><i class="fa
fa-cloud-download"></i> <?php echo
lng('Download') ?></button> &nbsp;
                </form>
                <b class="ms-2"><a href="<?php
echo fm_enc($file_url) ?>" target="_blank"><i
class="fa fa-external-link-square"></i> <?php echo
lng('Open') ?></a></b>
                <?php
                // ZIP actions
                if (!FM_READONLY && ($is_zip || $is_gzip)
&& $filenames !== false) {
                    $zip_name = pathinfo($file_path, PATHINFO_FILENAME);
                    ?>
                    <form method="post" class="d-inline
ms-2">
                        <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                        <input type="hidden"
name="unzip" value="<?php echo urlencode($file);
?>">
                        <button type="submit" class="btn
btn-link text-decoration-none fw-bold p-0" style="font-size:
14px;"><i class="fa fa-check-circle"></i>
<?php echo lng('UnZip') ?></button>
                    </form>&nbsp;
                    <form method="post" class="d-inline
ms-2">
                        <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                        <input type="hidden"
name="unzip" value="<?php echo urlencode($file);
?>">
                        <input type="hidden"
name="tofolder" value="1">
                        <button type="submit" class="btn
btn-link text-decoration-none fw-bold p-0" style="font-size:
14px;" title="UnZip to <?php echo fm_enc($zip_name)
?>"><i class="fa fa-check-circle"></i>
<?php echo lng('UnZipToFolder') ?></button>
                    </form>&nbsp;
                    <?php
                }
                if ($is_text && !FM_READONLY) {
                    ?>
                    <b class="ms-2"><a
href="?p=<?php echo urlencode(trim(FM_PATH))
?>&amp;edit=<?php echo urlencode($file) ?>"
class="edit-file"><i class="fa
fa-pencil-square"></i> <?php echo lng('Edit')
?>
                        </a></b> &nbsp;
                    <b class="ms-2"><a
href="?p=<?php echo urlencode(trim(FM_PATH))
?>&amp;edit=<?php echo urlencode($file) ?>&env=ace"
                            class="edit-file"><i
class="fa fa-pencil-square-o"></i> <?php echo
lng('AdvancedEditor') ?>
                        </a></b> &nbsp;
                <?php } ?>
                <b class="ms-2"><a
href="?p=<?php echo urlencode(FM_PATH) ?>"><i
class="fa fa-chevron-circle-left go-back"></i> <?php
echo lng('Back') ?></a></b>
            </div>
            <?php
            if($is_onlineViewer) {
                if($online_viewer == 'google') {
                    echo '<iframe
src="https://docs.google.com/viewer?embedded=true&hl=en&url='
. fm_enc($file_url) . '" frameborder="no"
style="width:100%;min-height:460px"></iframe>';
                } else if($online_viewer == 'microsoft') {
                    echo '<iframe
src="https://view.officeapps.live.com/op/embed.aspx?src=' .
fm_enc($file_url) . '" frameborder="no"
style="width:100%;min-height:460px"></iframe>';
                }
            } elseif ($is_zip) {
                // ZIP content
                if ($filenames !== false) {
                    echo '<code
class="maxheight">';
                    foreach ($filenames as $fn) {
                        if ($fn['folder']) {
                            echo '<b>' .
fm_enc($fn['name']) . '</b><br>';
                        } else {
                            echo $fn['name'] . ' (' .
fm_get_filesize($fn['filesize']) . ')<br>';
                        }
                    }
                    echo '</code>';
                } else {
                    echo '<p>'.lng('Error while
fetching archive info').'</p>';
                }
            } elseif ($is_image) {
                // Image content
                if (in_array($ext, array('gif', 'jpg',
'jpeg', 'png', 'bmp', 'ico',
'svg', 'webp', 'avif'))) {
                    echo '<p><input type="checkbox"
id="preview-img-zoomCheck"><label
for="preview-img-zoomCheck"><img src="' .
fm_enc($file_url) . '" alt="image"
class="preview-img"></label></p>';
                }
            } elseif ($is_audio) {
                // Audio content
                echo '<p><audio src="' .
fm_enc($file_url) . '" controls
preload="metadata"></audio></p>';
            } elseif ($is_video) {
                // Video content
                echo '<div
class="preview-video"><video src="' .
fm_enc($file_url) . '" width="640"
height="360" controls
preload="metadata"></video></div>';
            } elseif ($is_text) {
                if (FM_USE_HIGHLIGHTJS) {
                    // highlight
                    $hljs_classes = array(
                        'shtml' => 'xml',
                        'htaccess' => 'apache',
                        'phtml' => 'php',
                        'lock' => 'json',
                        'svg' => 'xml',
                    );
                    $hljs_class = isset($hljs_classes[$ext]) ?
'lang-' . $hljs_classes[$ext] : 'lang-' . $ext;
                    if (empty($ext) || in_array(strtolower($file),
fm_get_text_names()) || preg_match('#\.min\.(css|js)$#i', $file))
{
                        $hljs_class = 'nohighlight';
                    }
                    $content = '<pre
class="with-hljs"><code class="' . $hljs_class .
'">' . fm_enc($content) .
'</code></pre>';
                } elseif (in_array($ext, array('php',
'php4', 'php5', 'phtml', 'phps')))
{
                    // php highlight
                    $content = highlight_string($content, true);
                } else {
                    $content = '<pre>' . fm_enc($content) .
'</pre>';
                }
                echo $content;
            }
            ?>
        </div>
    </div>
    <?php
        fm_show_footer();
    exit;
}

// file editor
if (isset($_GET['edit']) && !FM_READONLY) {
    $file = $_GET['edit'];
    $file = fm_clean_path($file, false);
    $file = str_replace('/', '', $file);
    if ($file == '' || !is_file($path . '/' . $file) ||
!fm_is_exclude_items($file)) {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }
    $editFile = ' : <i><b>'. $file.
'</b></i>';
    header('X-XSS-Protection:0');
    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path

    $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ?
'/' . FM_PATH : '') . '/' . $file);
    $file_path = $path . '/' . $file;

    // normal editer
    $isNormalEditor = true;
    if (isset($_GET['env'])) {
        if ($_GET['env'] == "ace") {
            $isNormalEditor = false;
        }
    }

    // Save File
    if (isset($_POST['savedata'])) {
        $writedata = $_POST['savedata'];
        $fd = fopen($file_path, "w");
        @fwrite($fd, $writedata);
        fclose($fd);
        fm_set_msg(lng('File Saved Successfully'));
    }

    $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
    $mime_type = fm_get_mime_type($file_path);
    $filesize = filesize($file_path);
    $is_text = false;
    $content = ''; // for text

    if (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) ==
'text' || in_array($mime_type, fm_get_text_mimes())) {
        $is_text = true;
        $content = file_get_contents($file_path);
    }

    ?>
    <div class="path">
        <div class="row">
            <div class="col-xs-12 col-sm-5 col-lg-6 pt-1">
                <div class="btn-toolbar"
role="toolbar">
                    <?php if (!$isNormalEditor) { ?>
                        <div class="btn-group
js-ace-toolbar">
                            <button data-cmd="none"
data-option="fullscreen" class="btn btn-sm
btn-outline-secondary" id="js-ace-fullscreen"
title="<?php echo lng('Fullscreen') ?>"><i
class="fa fa-expand" title="<?php echo
lng('Fullscreen') ?>"></i></button>
                            <button data-cmd="find"
class="btn btn-sm btn-outline-secondary"
id="js-ace-search" title="<?php echo
lng('Search') ?>"><i class="fa fa-search"
title="<?php echo lng('Search')
?>"></i></button>
                            <button data-cmd="undo"
class="btn btn-sm btn-outline-secondary"
id="js-ace-undo" title="<?php echo lng('Undo')
?>"><i class="fa fa-undo" title="<?php echo
lng('Undo') ?>"></i></button>
                            <button data-cmd="redo"
class="btn btn-sm btn-outline-secondary"
id="js-ace-redo" title="<?php echo lng('Redo')
?>"><i class="fa fa-repeat" title="<?php
echo lng('Redo') ?>"></i></button>
                            <button data-cmd="none"
data-option="wrap" class="btn btn-sm
btn-outline-secondary" id="js-ace-wordWrap"
title="<?php echo lng('Word Wrap') ?>"><i
class="fa fa-text-width" title="<?php echo lng('Word
Wrap') ?>"></i></button>
                            <select id="js-ace-mode"
data-type="mode" title="<?php echo lng('Select
Document Type') ?>" class="btn-outline-secondary
border-start-0 d-none d-md-block"><option>-- <?php echo
lng('Select Mode') ?> --</option></select>
                            <select id="js-ace-theme"
data-type="theme" title="<?php echo lng('Select
Theme') ?>" class="btn-outline-secondary border-start-0
d-none d-lg-block"><option>-- <?php echo lng('Select
Theme') ?> --</option></select>
                            <select id="js-ace-fontSize"
data-type="fontSize" title="<?php echo lng('Select
Font Size') ?>" class="btn-outline-secondary
border-start-0 d-none d-lg-block"><option>-- <?php echo
lng('Select Font Size') ?> --</option></select>
                        </div>
                    <?php } ?>
                </div>
            </div>
            <div class="edit-file-actions col-xs-12 col-sm-7
col-lg-6 text-end pt-1">
                <a title="<?php echo lng('Back')
?>" class="btn btn-sm btn-outline-primary"
href="?p=<?php echo urlencode(trim(FM_PATH))
?>&amp;view=<?php echo urlencode($file) ?>"><i
class="fa fa-reply-all"></i> <?php echo
lng('Back') ?></a>
                <a title="<?php echo lng('BackUp')
?>" class="btn btn-sm btn-outline-primary"
href="javascript:void(0);" onclick="backup('<?php
echo urlencode(trim(FM_PATH)) ?>','<?php echo
urlencode($file) ?>')"><i class="fa
fa-database"></i> <?php echo lng('BackUp')
?></a>
                <?php if ($is_text) { ?>
                    <?php if ($isNormalEditor) { ?>
                        <a title="Advanced" class="btn
btn-sm btn-outline-primary" href="?p=<?php echo
urlencode(trim(FM_PATH)) ?>&amp;edit=<?php echo urlencode($file)
?>&amp;env=ace"><i class="fa
fa-pencil-square-o"></i> <?php echo
lng('AdvancedEditor') ?></a>
                        <button type="button" class="btn
btn-sm btn-success" name="Save" data-url="<?php echo
fm_enc($file_url) ?>"
onclick="edit_save(this,'nrl')"><i class="fa
fa-floppy-o"></i> Save
                        </button>
                    <?php } else { ?>
                        <a title="Plain Editor"
class="btn btn-sm btn-outline-primary" href="?p=<?php
echo urlencode(trim(FM_PATH)) ?>&amp;edit=<?php echo
urlencode($file) ?>"><i class="fa
fa-text-height"></i> <?php echo
lng('NormalEditor') ?></a>
                        <button type="button" class="btn
btn-sm btn-success" name="Save" data-url="<?php echo
fm_enc($file_url) ?>"
onclick="edit_save(this,'ace')"><i class="fa
fa-floppy-o"></i> <?php echo lng('Save') ?>
                        </button>
                    <?php } ?>
                <?php } ?>
            </div>
        </div>
        <?php
        if ($is_text && $isNormalEditor) {
            echo '<textarea class="mt-2"
id="normal-editor" rows="33" cols="120"
style="width: 99.5%;">' . htmlspecialchars($content) .
'</textarea>';
            echo
'<script>document.addEventListener("keydown",
function(e) {if ((window.navigator.platform.match("Mac") ?
e.metaKey : e.ctrlKey)  && e.keyCode == 83) {
e.preventDefault();edit_save(this,"nrl");}},
false);</script>';
        } elseif ($is_text) {
            echo '<div id="editor"
contenteditable="true">' . htmlspecialchars($content) .
'</div>';
        } else {
            fm_set_msg(lng('FILE EXTENSION HAS NOT SUPPORTED'),
'error');
        }
        ?>
    </div>
    <?php
    fm_show_footer();
    exit;
}

// chmod (not for Windows)
if (isset($_GET['chmod']) && !FM_READONLY &&
!FM_IS_WIN) {
    $file = $_GET['chmod'];
    $file = fm_clean_path($file);
    $file = str_replace('/', '', $file);
    if ($file == '' || (!is_file($path . '/' . $file)
&& !is_dir($path . '/' . $file))) {
        fm_set_msg(lng('File not found'), 'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
    }

    fm_show_header(); // HEADER
    fm_show_nav_path(FM_PATH); // current path

    $file_url = FM_ROOT_URL . (FM_PATH != '' ? '/' .
FM_PATH : '') . '/' . $file;
    $file_path = $path . '/' . $file;

    $mode = fileperms($path . '/' . $file);
    ?>
    <div class="path">
        <div class="card mb-2 <?php echo fm_get_theme();
?>">
            <h6 class="card-header">
                <?php echo lng('ChangePermissions') ?>
            </h6>
            <div class="card-body">
                <p class="card-text">
                    <?php $display_path =
fm_get_display_path($file_path); ?>
                    <?php echo $display_path['label']; ?>:
<?php echo $display_path['path']; ?><br>
                </p>
                <form action="" method="post">
                    <input type="hidden" name="p"
value="<?php echo fm_enc(FM_PATH) ?>">
                    <input type="hidden"
name="chmod" value="<?php echo fm_enc($file)
?>">

                    <table class="table compact-table <?php echo
fm_get_theme(); ?>">
                        <tr>
                            <td></td>
                            <td><b><?php echo
lng('Owner') ?></b></td>
                            <td><b><?php echo
lng('Group') ?></b></td>
                            <td><b><?php echo
lng('Other') ?></b></td>
                        </tr>
                        <tr>
                            <td style="text-align:
right"><b><?php echo lng('Read')
?></b></td>
                            <td><label><input
type="checkbox" name="ur" value="1"<?php
echo ($mode & 00400) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="gr" value="1"<?php
echo ($mode & 00040) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="or" value="1"<?php
echo ($mode & 00004) ? ' checked' : ''
?>></label></td>
                        </tr>
                        <tr>
                            <td style="text-align:
right"><b><?php echo lng('Write')
?></b></td>
                            <td><label><input
type="checkbox" name="uw" value="1"<?php
echo ($mode & 00200) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="gw" value="1"<?php
echo ($mode & 00020) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="ow" value="1"<?php
echo ($mode & 00002) ? ' checked' : ''
?>></label></td>
                        </tr>
                        <tr>
                            <td style="text-align:
right"><b><?php echo lng('Execute')
?></b></td>
                            <td><label><input
type="checkbox" name="ux" value="1"<?php
echo ($mode & 00100) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="gx" value="1"<?php
echo ($mode & 00010) ? ' checked' : ''
?>></label></td>
                            <td><label><input
type="checkbox" name="ox" value="1"<?php
echo ($mode & 00001) ? ' checked' : ''
?>></label></td>
                        </tr>
                    </table>

                    <p>
                       <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>"> 
                        <b><a href="?p=<?php echo
urlencode(FM_PATH) ?>" class="btn
btn-outline-primary"><i class="fa
fa-times-circle"></i> <?php echo lng('Cancel')
?></a></b>&nbsp;
                        <button type="submit" class="btn
btn-success"><i class="fa
fa-check-circle"></i> <?php echo lng('Change')
?></button>
                    </p>
                </form>
            </div>
        </div>
    </div>
    <?php
    fm_show_footer();
    exit;
}

// --- TINYFILEMANAGER MAIN ---
fm_show_header(); // HEADER
fm_show_nav_path(FM_PATH); // current path

// show alert messages
fm_show_message();

$num_files = count($files);
$num_folders = count($folders);
$all_files_size = 0;
$tableTheme = (FM_THEME == "dark") ? "text-white bg-dark
table-dark" : "bg-white";
?>
<form action="" method="post"
class="pt-3">
    <input type="hidden" name="p"
value="<?php echo fm_enc(FM_PATH) ?>">
    <input type="hidden" name="group"
value="1">
    <input type="hidden" name="token"
value="<?php echo $_SESSION['token']; ?>">
    <div class="table-responsive">
        <table class="table table-bordered table-hover table-sm
<?php echo $tableTheme; ?>" id="main-table">
            <thead class="thead-white">
            <tr>
                <?php if (!FM_READONLY): ?>
                    <th style="width:3%"
class="custom-checkbox-header">
                        <div class="custom-control
custom-checkbox">
                            <input type="checkbox"
class="custom-control-input" id="js-select-all-items"
onclick="checkbox_toggle()">
                            <label
class="custom-control-label"
for="js-select-all-items"></label>
                        </div>
                    </th><?php endif; ?>
                <th><?php echo lng('Name')
?></th>
                <th><?php echo lng('Size')
?></th>
                <th><?php echo lng('Modified')
?></th>
                <?php if (!FM_IS_WIN && !$hide_Cols): ?>
                    <th><?php echo lng('Perms')
?></th>
                    <th><?php echo lng('Owner')
?></th><?php endif; ?>
                <th><?php echo lng('Actions')
?></th>
            </tr>
            </thead>
            <?php
            // link to parent folder
            if ($parent !== false) {
                ?>
                <tr><?php if (!FM_READONLY): ?>
                    <td class="nosort"></td><?php
endif; ?>
                    <td class="border-0" data-sort><a
href="?p=<?php echo urlencode($parent) ?>"><i
class="fa fa-chevron-circle-left go-back"></i>
..</a></td>
                    <td class="border-0"
data-order></td>
                    <td class="border-0"
data-order></td>
                    <td class="border-0"></td>
                    <?php if (!FM_IS_WIN && !$hide_Cols) {
?>
                        <td class="border-0"></td>
                        <td class="border-0"></td>
                    <?php } ?>
                </tr>
                <?php
            }
            $ii = 3399;
            foreach ($folders as $f) {
                $is_link = is_link($path . '/' . $f);
                $img = $is_link ? 'icon-link_folder' : 'fa
fa-folder-o';
                $modif_raw = filemtime($path . '/' . $f);
                $modif = date(FM_DATETIME_FORMAT, $modif_raw);
                $date_sorting = strtotime(date("F d Y H:i:s.",
$modif_raw));
                $filesize_raw = "";
                $filesize = lng('Folder');
                $perms = substr(decoct(fileperms($path . '/' .
$f)), -4);
                if (function_exists('posix_getpwuid') &&
function_exists('posix_getgrgid')) {
                    $owner = posix_getpwuid(fileowner($path . '/'
. $f));
                    $group = posix_getgrgid(filegroup($path . '/'
. $f));
                    if ($owner === false) {
                        $owner = array('name' =>
'?');
                    }
                    if ($group === false) {
                        $group = array('name' =>
'?');
                    }
                } else {
                    $owner = array('name' => '?');
                    $group = array('name' => '?');
                }
                ?>
                <tr>
                    <?php if (!FM_READONLY): ?>
                        <td class="custom-checkbox-td">
                        <div class="custom-control
custom-checkbox">
                            <input type="checkbox"
class="custom-control-input" id="<?php echo $ii
?>" name="file[]" value="<?php echo fm_enc($f)
?>">
                            <label
class="custom-control-label" for="<?php echo $ii
?>"></label>
                        </div>
                        </td><?php endif; ?>
                    <td data-sort=<?php echo
fm_convert_win(fm_enc($f)) ?>>
                        <div class="filename"><a
href="?p=<?php echo urlencode(trim(FM_PATH . '/' . $f,
'/')) ?>"><i class="<?php echo $img
?>"></i> <?php echo fm_convert_win(fm_enc($f)) ?>
                            </a><?php echo($is_link ? '
&rarr; <i>' . readlink($path . '/' . $f) .
'</i>' : '') ?></div>
                    </td>
                    <td data-order="a-<?php echo
str_pad($filesize_raw, 18, "0", STR_PAD_LEFT);?>">
                        <?php echo $filesize; ?>
                    </td>
                    <td data-order="a-<?php echo
$date_sorting;?>"><?php echo $modif ?></td>
                    <?php if (!FM_IS_WIN && !$hide_Cols): ?>
                        <td><?php if (!FM_READONLY): ?><a
title="Change Permissions" href="?p=<?php echo
urlencode(FM_PATH) ?>&amp;chmod=<?php echo urlencode($f)
?>"><?php echo $perms ?></a><?php else:
?><?php echo $perms ?><?php endif; ?>
                        </td>
                        <td><?php echo $owner['name'] .
':' . $group['name'] ?></td>
                    <?php endif; ?>
                    <td class="inline-actions"><?php if
(!FM_READONLY): ?>
                            <a title="<?php echo
lng('Delete')?>" href="?p=<?php echo
urlencode(FM_PATH) ?>&amp;del=<?php echo urlencode($f)
?>" onclick="confirmDailog(event,
'1028','<?php echo lng('Delete').'
'.lng('Folder'); ?>','<?php echo
urlencode($f) ?>', this.href);"> <i class="fa
fa-trash-o" aria-hidden="true"></i></a>
                            <a title="<?php echo
lng('Rename')?>" href="#"
onclick="rename('<?php echo fm_enc(addslashes(FM_PATH))
?>', '<?php echo fm_enc(addslashes($f)) ?>');return
false;"><i class="fa fa-pencil-square-o"
aria-hidden="true"></i></a>
                            <a title="<?php echo
lng('CopyTo')?>..." href="?p=&amp;copy=<?php
echo urlencode(trim(FM_PATH . '/' . $f, '/'))
?>"><i class="fa fa-files-o"
aria-hidden="true"></i></a>
                        <?php endif; ?>
                        <a title="<?php echo
lng('DirectLink')?>" href="<?php echo
fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH :
'') . '/' . $f . '/') ?>"
target="_blank"><i class="fa fa-link"
aria-hidden="true"></i></a>
                    </td>
                </tr>
                <?php
                flush();
                $ii++;
            }
            $ik = 6070;
            foreach ($files as $f) {
                $is_link = is_link($path . '/' . $f);
                $img = $is_link ? 'fa fa-file-text-o' :
fm_get_file_icon_class($path . '/' . $f);
                $modif_raw = filemtime($path . '/' . $f);
                $modif = date(FM_DATETIME_FORMAT, $modif_raw);
                $date_sorting = strtotime(date("F d Y H:i:s.",
$modif_raw));
                $filesize_raw = fm_get_size($path . '/' . $f);
                $filesize = fm_get_filesize($filesize_raw);
                $filelink = '?p=' . urlencode(FM_PATH) .
'&amp;view=' . urlencode($f);
                $all_files_size += $filesize_raw;
                $perms = substr(decoct(fileperms($path . '/' .
$f)), -4);
                if (function_exists('posix_getpwuid') &&
function_exists('posix_getgrgid')) {
                    $owner = posix_getpwuid(fileowner($path . '/'
. $f));
                    $group = posix_getgrgid(filegroup($path . '/'
. $f));
                    if ($owner === false) {
                        $owner = array('name' =>
'?');
                    }
                    if ($group === false) {
                        $group = array('name' =>
'?');
                    }
                } else {
                    $owner = array('name' => '?');
                    $group = array('name' => '?');
                }
                ?>
                <tr>
                    <?php if (!FM_READONLY): ?>
                        <td class="custom-checkbox-td">
                        <div class="custom-control
custom-checkbox">
                            <input type="checkbox"
class="custom-control-input" id="<?php echo $ik
?>" name="file[]" value="<?php echo fm_enc($f)
?>">
                            <label
class="custom-control-label" for="<?php echo $ik
?>"></label>
                        </div>
                        </td><?php endif; ?>
                    <td data-sort=<?php echo fm_enc($f) ?>>
                        <div class="filename">
                        <?php
                           if (in_array(strtolower(pathinfo($f,
PATHINFO_EXTENSION)), array('gif', 'jpg',
'jpeg', 'png', 'bmp', 'ico',
'svg', 'webp', 'avif'))): ?>
                                <?php $imagePreview = fm_enc(FM_ROOT_URL
. (FM_PATH != '' ? '/' . FM_PATH : '') .
'/' . $f); ?>
                                <a href="<?php echo $filelink
?>" data-preview-image="<?php echo $imagePreview
?>" title="<?php echo fm_enc($f) ?>">
                           <?php else: ?>
                                <a href="<?php echo $filelink
?>" title="<?php echo $f ?>">
                            <?php endif; ?>
                                    <i class="<?php echo $img
?>"></i> <?php echo fm_convert_win(fm_enc($f)) ?>
                                </a>
                                <?php echo($is_link ? ' &rarr;
<i>' . readlink($path . '/' . $f) .
'</i>' : '') ?>
                        </div>
                    </td>
                    <td data-order="b-<?php echo
str_pad($filesize_raw, 18, "0", STR_PAD_LEFT);
?>"><span title="<?php printf('%s bytes',
$filesize_raw) ?>">
                        <?php echo $filesize; ?>
                        </span></td>
                    <td data-order="b-<?php echo
$date_sorting;?>"><?php echo $modif ?></td>
                    <?php if (!FM_IS_WIN && !$hide_Cols): ?>
                        <td><?php if (!FM_READONLY): ?><a
title="<?php echo 'Change Permissions' ?>"
href="?p=<?php echo urlencode(FM_PATH) ?>&amp;chmod=<?php
echo urlencode($f) ?>"><?php echo $perms
?></a><?php else: ?><?php echo $perms ?><?php
endif; ?>
                        </td>
                        <td><?php echo
fm_enc($owner['name'] . ':' . $group['name'])
?></td>
                    <?php endif; ?>
                    <td class="inline-actions">
                        <?php if (!FM_READONLY): ?>
                            <a title="<?php echo
lng('Delete') ?>" href="?p=<?php echo
urlencode(FM_PATH) ?>&amp;del=<?php echo urlencode($f)
?>" onclick="confirmDailog(event, 1209, '<?php echo
lng('Delete').' '.lng('File');
?>','<?php echo urlencode($f); ?>',
this.href);"> <i class="fa
fa-trash-o"></i></a>
                            <a title="<?php echo
lng('Rename') ?>" href="#"
onclick="rename('<?php echo fm_enc(addslashes(FM_PATH))
?>', '<?php echo fm_enc(addslashes($f)) ?>');return
false;"><i class="fa
fa-pencil-square-o"></i></a>
                            <a title="<?php echo
lng('CopyTo') ?>..."
                               href="?p=<?php echo
urlencode(FM_PATH) ?>&amp;copy=<?php echo urlencode(trim(FM_PATH
. '/' . $f, '/')) ?>"><i class="fa
fa-files-o"></i></a>
                        <?php endif; ?>
                        <a title="<?php echo
lng('DirectLink') ?>" href="<?php echo
fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH :
'') . '/' . $f) ?>"
target="_blank"><i class="fa
fa-link"></i></a>
                        <a title="<?php echo
lng('Download') ?>" href="?p=<?php echo
urlencode(FM_PATH) ?>&amp;dl=<?php echo urlencode($f) ?>"
onclick="confirmDailog(event, 1211, '<?php echo
lng('Download'); ?>','<?php echo urlencode($f);
?>', this.href);"><i class="fa
fa-download"></i></a>
                    </td>
                </tr>
                <?php
                flush();
                $ik++;
            }

            if (empty($folders) && empty($files)) { ?>
                <tfoot>
                    <tr><?php if (!FM_READONLY): ?>
                            <td></td><?php endif; ?>
                        <td colspan="<?php echo (!FM_IS_WIN
&& !$hide_Cols) ? '6' : '4'
?>"><em><?php echo lng('Folder is empty')
?></em></td>
                    </tr>
                </tfoot>
                <?php
            } else { ?>
                <tfoot>
                    <tr>
                        <td class="gray"
colspan="<?php echo (!FM_IS_WIN && !$hide_Cols) ?
(FM_READONLY ? '6' :'7') : (FM_READONLY ? '4'
: '5') ?>">
                            <?php echo lng('FullSize').':
<span class="badge text-bg-light
border-radius-0">'.fm_get_filesize($all_files_size).'</span>'
?>
                            <?php echo lng('File').':
<span class="badge text-bg-light
border-radius-0">'.$num_files.'</span>' ?>
                            <?php echo lng('Folder').':
<span class="badge text-bg-light
border-radius-0">'.$num_folders.'</span>'
?>
                        </td>
                    </tr>
                </tfoot>
                <?php } ?>
        </table>
    </div>

    <div class="row">
        <?php if (!FM_READONLY): ?>
        <div class="col-xs-12 col-sm-9">
            <ul class="list-inline footer-action">
                <li class="list-inline-item"> <a
href="#/select-all" class="btn btn-small btn-outline-primary
btn-2" onclick="select_all();return false;"><i
class="fa fa-check-square"></i> <?php echo
lng('SelectAll') ?> </a></li>
                <li class="list-inline-item"><a
href="#/unselect-all" class="btn btn-small
btn-outline-primary btn-2" onclick="unselect_all();return
false;"><i class="fa fa-window-close"></i>
<?php echo lng('UnSelectAll') ?> </a></li>
                <li class="list-inline-item"><a
href="#/invert-all" class="btn btn-small btn-outline-primary
btn-2" onclick="invert_all();return false;"><i
class="fa fa-th-list"></i> <?php echo
lng('InvertSelection') ?> </a></li>
                <li class="list-inline-item"><input
type="submit" class="hidden" name="delete"
id="a-delete" value="Delete" onclick="return
confirm('<?php echo lng('Delete selected files and
folders?'); ?>')">
                    <a
href="javascript:document.getElementById('a-delete').click();"
class="btn btn-small btn-outline-primary btn-2"><i
class="fa fa-trash"></i> <?php echo
lng('Delete') ?> </a></li>
                <li class="list-inline-item"><input
type="submit" class="hidden" name="zip"
id="a-zip" value="zip" onclick="return
confirm('<?php echo lng('Create archive?');
?>')">
                    <a
href="javascript:document.getElementById('a-zip').click();"
class="btn btn-small btn-outline-primary btn-2"><i
class="fa fa-file-archive-o"></i> <?php echo
lng('Zip') ?> </a></li>
                <li class="list-inline-item"><input
type="submit" class="hidden" name="tar"
id="a-tar" value="tar" onclick="return
confirm('<?php echo lng('Create archive?');
?>')">
                    <a
href="javascript:document.getElementById('a-tar').click();"
class="btn btn-small btn-outline-primary btn-2"><i
class="fa fa-file-archive-o"></i> <?php echo
lng('Tar') ?> </a></li>
                <li class="list-inline-item"><input
type="submit" class="hidden" name="copy"
id="a-copy" value="Copy">
                    <a
href="javascript:document.getElementById('a-copy').click();"
class="btn btn-small btn-outline-primary btn-2"><i
class="fa fa-files-o"></i> <?php echo
lng('Copy') ?> </a></li>
            </ul>
        </div>
        <div class="col-3 d-none d-sm-block"><a
href="https://tinyfilemanager.github.io"
target="_blank" class="float-right text-muted">Tiny
File Manager <?php echo VERSION; ?></a></div>
        <?php else: ?>
            <div class="col-12"><a
href="https://tinyfilemanager.github.io"
target="_blank" class="float-right text-muted">Tiny
File Manager <?php echo VERSION; ?></a></div>
        <?php endif; ?>
    </div>
</form>

<?php
fm_show_footer();

// --- END HTML ---

// Functions

/**
 * It prints the css/js files into html
 * @param key The key of the external file to print.
 */
function print_external($key) {
    global $external;

    if(!array_key_exists($key, $external)) {
        // throw new Exception('Key missing in external: ' .
key);
        echo "<!-- EXTERNAL: MISSING KEY $key -->";
        return;
    }

    echo "$external[$key]";
}

/**
 * Verify CSRF TOKEN and remove after certified
 * @param string $token
 * @return bool
 */
function verifyToken($token) 
{
    if (hash_equals($_SESSION['token'], $token)) { 
        return true;
    }
    return false;
}

/**
 * Delete  file or folder (recursively)
 * @param string $path
 * @return bool
 */
function fm_rdelete($path)
{
    if (is_link($path)) {
        return unlink($path);
    } elseif (is_dir($path)) {
        $objects = scandir($path);
        $ok = true;
        if (is_array($objects)) {
            foreach ($objects as $file) {
                if ($file != '.' && $file !=
'..') {
                    if (!fm_rdelete($path . '/' . $file)) {
                        $ok = false;
                    }
                }
            }
        }
        return ($ok) ? rmdir($path) : false;
    } elseif (is_file($path)) {
        return unlink($path);
    }
    return false;
}

/**
 * Recursive chmod
 * @param string $path
 * @param int $filemode
 * @param int $dirmode
 * @return bool
 * @todo Will use in mass chmod
 */
function fm_rchmod($path, $filemode, $dirmode)
{
    if (is_dir($path)) {
        if (!chmod($path, $dirmode)) {
            return false;
        }
        $objects = scandir($path);
        if (is_array($objects)) {
            foreach ($objects as $file) {
                if ($file != '.' && $file !=
'..') {
                    if (!fm_rchmod($path . '/' . $file,
$filemode, $dirmode)) {
                        return false;
                    }
                }
            }
        }
        return true;
    } elseif (is_link($path)) {
        return true;
    } elseif (is_file($path)) {
        return chmod($path, $filemode);
    }
    return false;
}

/**
 * Check the file extension which is allowed or not
 * @param string $filename
 * @return bool
 */
function fm_is_valid_ext($filename)
{
    $allowed = (FM_FILE_EXTENSION) ? explode(',',
FM_FILE_EXTENSION) : false;

    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true;

    return ($isFileAllowed) ? true : false;
}

/**
 * Safely rename
 * @param string $old
 * @param string $new
 * @return bool|null
 */
function fm_rename($old, $new)
{
    $isFileAllowed = fm_is_valid_ext($new);

    if(!is_dir($old)) {
        if (!$isFileAllowed) return false;
    }

    return (!file_exists($new) && file_exists($old)) ? rename($old,
$new) : null;
}

/**
 * Copy file or folder (recursively).
 * @param string $path
 * @param string $dest
 * @param bool $upd Update files
 * @param bool $force Create folder with same names instead file
 * @return bool
 */
function fm_rcopy($path, $dest, $upd = true, $force = true)
{
    if (is_dir($path)) {
        if (!fm_mkdir($dest, $force)) {
            return false;
        }
        $objects = scandir($path);
        $ok = true;
        if (is_array($objects)) {
            foreach ($objects as $file) {
                if ($file != '.' && $file !=
'..') {
                    if (!fm_rcopy($path . '/' . $file, $dest .
'/' . $file)) {
                        $ok = false;
                    }
                }
            }
        }
        return $ok;
    } elseif (is_file($path)) {
        return fm_copy($path, $dest, $upd);
    }
    return false;
}

/**
 * Safely create folder
 * @param string $dir
 * @param bool $force
 * @return bool
 */
function fm_mkdir($dir, $force)
{
    if (file_exists($dir)) {
        if (is_dir($dir)) {
            return $dir;
        } elseif (!$force) {
            return false;
        }
        unlink($dir);
    }
    return mkdir($dir, 0777, true);
}

/**
 * Safely copy file
 * @param string $f1
 * @param string $f2
 * @param bool $upd Indicates if file should be updated with new content
 * @return bool
 */
function fm_copy($f1, $f2, $upd)
{
    $time1 = filemtime($f1);
    if (file_exists($f2)) {
        $time2 = filemtime($f2);
        if ($time2 >= $time1 && $upd) {
            return false;
        }
    }
    $ok = copy($f1, $f2);
    if ($ok) {
        touch($f2, $time1);
    }
    return $ok;
}

/**
 * Get mime type
 * @param string $file_path
 * @return mixed|string
 */
function fm_get_mime_type($file_path)
{
    if (function_exists('finfo_open')) {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $file_path);
        finfo_close($finfo);
        return $mime;
    } elseif (function_exists('mime_content_type')) {
        return mime_content_type($file_path);
    } elseif (!stristr(ini_get('disable_functions'),
'shell_exec')) {
        $file = escapeshellarg($file_path);
        $mime = shell_exec('file -bi ' . $file);
        return $mime;
    } else {
        return '--';
    }
}

/**
 * HTTP Redirect
 * @param string $url
 * @param int $code
 */
function fm_redirect($url, $code = 302)
{
    header('Location: ' . $url, true, $code);
    exit;
}

/**
 * Path traversal prevention and clean the url
 * It replaces (consecutive) occurrences of / and \\ with whatever is in
DIRECTORY_SEPARATOR, and processes /. and /.. fine.
 * @param $path
 * @return string
 */
function get_absolute_path($path) {
    $path = str_replace(array('/', '\\'),
DIRECTORY_SEPARATOR, $path);
    $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path),
'strlen');
    $absolutes = array();
    foreach ($parts as $part) {
        if ('.' == $part) continue;
        if ('..' == $part) {
            array_pop($absolutes);
        } else {
            $absolutes[] = $part;
        }
    }
    return implode(DIRECTORY_SEPARATOR, $absolutes);
}

/**
 * Clean path
 * @param string $path
 * @return string
 */
function fm_clean_path($path, $trim = true)
{
    $path = $trim ? trim($path) : $path;
    $path = trim($path, '\\/');
    $path = str_replace(array('../', '..\\'),
'', $path);
    $path =  get_absolute_path($path);
    if ($path == '..') {
        $path = '';
    }
    return str_replace('\\', '/', $path);
}

/**
 * Get parent path
 * @param string $path
 * @return bool|string
 */
function fm_get_parent_path($path)
{
    $path = fm_clean_path($path);
    if ($path != '') {
        $array = explode('/', $path);
        if (count($array) > 1) {
            $array = array_slice($array, 0, -1);
            return implode('/', $array);
        }
        return '';
    }
    return false;
}

function fm_get_display_path($file_path)
{
    global $path_display_mode, $root_path, $root_url;
    switch ($path_display_mode) {
        case 'relative':
            return array(
                'label' => 'Path',
                'path' =>
fm_enc(fm_convert_win(str_replace($root_path, '', $file_path)))
            );
        case 'host':
            $relative_path = str_replace($root_path, '',
$file_path);
            return array(
                'label' => 'Host Path',
                'path' => fm_enc(fm_convert_win('/'
. $root_url . '/' . ltrim(str_replace('\\',
'/', $relative_path), '/')))
            );
        case 'full':
        default:
            return array(
                'label' => 'Full Path',
                'path' => fm_enc(fm_convert_win($file_path))
            );
    }
}

/**
 * Check file is in exclude list
 * @param string $file
 * @return bool
 */
function fm_is_exclude_items($file) {
    $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
    if (isset($exclude_items) and sizeof($exclude_items)) {
        unset($exclude_items);
    }

    $exclude_items = FM_EXCLUDE_ITEMS;
    if (version_compare(PHP_VERSION, '7.0.0', '<'))
{
        $exclude_items = unserialize($exclude_items);
    }
    if (!in_array($file, $exclude_items) &&
!in_array("*.$ext", $exclude_items)) {
        return true;
    }
    return false;
}

/**
 * get language translations from json file
 * @param int $tr
 * @return array
 */
function fm_get_translations($tr) {
    try {
        $content = @file_get_contents('translation.json');
        if($content !== FALSE) {
            $lng = json_decode($content, TRUE);
            global $lang_list;
            foreach ($lng["language"] as $key => $value)
            {
                $code = $value["code"];
                $lang_list[$code] = $value["name"];
                if ($tr)
                    $tr[$code] = $value["translation"];
            }
            return $tr;
        }

    }
    catch (Exception $e) {
        echo $e;
    }
}

/**
 * @param string $file
 * Recover all file sizes larger than > 2GB.
 * Works on php 32bits and 64bits and supports linux
 * @return int|string
 */
function fm_get_size($file)
{
    static $iswin;
    static $isdarwin;
    if (!isset($iswin)) {
        $iswin = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
    }
    if (!isset($isdarwin)) {
        $isdarwin = (strtoupper(substr(PHP_OS, 0)) == "DARWIN");
    }

    static $exec_works;
    if (!isset($exec_works)) {
        $exec_works = (function_exists('exec') &&
!ini_get('safe_mode') && @exec('echo EXEC') ==
'EXEC');
    }

    // try a shell command
    if ($exec_works) {
        $arg = escapeshellarg($file);
        $cmd = ($iswin) ? "for %F in (\"$file\") do @echo
%~zF" : ($isdarwin ? "stat -f%z $arg" : "stat -c%s
$arg");
        @exec($cmd, $output);
        if (is_array($output) && ctype_digit($size =
trim(implode("\n", $output)))) {
            return $size;
        }
    }

    // try the Windows COM interface
    if ($iswin && class_exists("COM")) {
        try {
            $fsobj = new COM('Scripting.FileSystemObject');
            $f = $fsobj->GetFile( realpath($file) );
            $size = $f->Size;
        } catch (Exception $e) {
            $size = null;
        }
        if (ctype_digit($size)) {
            return $size;
        }
    }

    // if all else fails
    return filesize($file);
}

/**
 * Get nice filesize
 * @param int $size
 * @return string
 */
function fm_get_filesize($size)
{
    $size = (float) $size;
    $units = array('B', 'KB', 'MB',
'GB', 'TB', 'PB', 'EB',
'ZB', 'YB');
    $power = ($size > 0) ? floor(log($size, 1024)) : 0;
    $power = ($power > (count($units) - 1)) ? (count($units) - 1) :
$power;
    return sprintf('%s %s', round($size / pow(1024, $power), 2),
$units[$power]);
}

/**
 * Get total size of directory tree.
 *
 * @param  string $directory Relative or absolute directory name.
 * @return int Total number of bytes.
 */
function fm_get_directorysize($directory) {
    $bytes = 0;
    $directory = realpath($directory);
    if ($directory !== false && $directory != ''
&& file_exists($directory)){
        foreach(new RecursiveIteratorIterator(new
RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)) as
$file){
            $bytes += $file->getSize();
        }
    }
    return $bytes;
}

/**
 * Get info about zip archive
 * @param string $path
 * @return array|bool
 */
function fm_get_zif_info($path, $ext) {
    if ($ext == 'zip' &&
function_exists('zip_open')) {
        $arch = @zip_open($path);
        if ($arch) {
            $filenames = array();
            while ($zip_entry = @zip_read($arch)) {
                $zip_name = @zip_entry_name($zip_entry);
                $zip_folder = substr($zip_name, -1) == '/';
                $filenames[] = array(
                    'name' => $zip_name,
                    'filesize' =>
@zip_entry_filesize($zip_entry),
                    'compressed_size' =>
@zip_entry_compressedsize($zip_entry),
                    'folder' => $zip_folder
                    //'compression_method' =>
zip_entry_compressionmethod($zip_entry),
                );
            }
            @zip_close($arch);
            return $filenames;
        }
    } elseif($ext == 'tar' &&
class_exists('PharData')) {
        $archive = new PharData($path);
        $filenames = array();
        foreach(new RecursiveIteratorIterator($archive) as $file) {
            $parent_info = $file->getPathInfo();
            $zip_name = str_replace("phar://".$path,
'', $file->getPathName());
            $zip_name = substr($zip_name, ($pos = strpos($zip_name,
'/')) !== false ? $pos + 1 : 0);
            $zip_folder = $parent_info->getFileName();
            $zip_info = new SplFileInfo($file);
            $filenames[] = array(
                'name' => $zip_name,
                'filesize' => $zip_info->getSize(),
                'compressed_size' =>
$file->getCompressedSize(),
                'folder' => $zip_folder
            );
        }
        return $filenames;
    }
    return false;
}

/**
 * Encode html entities
 * @param string $text
 * @return string
 */
function fm_enc($text)
{
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}

/**
 * Prevent XSS attacks
 * @param string $text
 * @return string
 */
function fm_isvalid_filename($text) {
    return (strpbrk($text, '/?%*:|"<>') === FALSE) ?
true : false;
}

/**
 * Save message in session
 * @param string $msg
 * @param string $status
 */
function fm_set_msg($msg, $status = 'ok')
{
    $_SESSION[FM_SESSION_ID]['message'] = $msg;
    $_SESSION[FM_SESSION_ID]['status'] = $status;
}

/**
 * Check if string is in UTF-8
 * @param string $string
 * @return int
 */
function fm_is_utf8($string)
{
    return preg_match('//u', $string);
}

/**
 * Convert file name to UTF-8 in Windows
 * @param string $filename
 * @return string
 */
function fm_convert_win($filename)
{
    if (FM_IS_WIN && function_exists('iconv')) {
        $filename = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE',
$filename);
    }
    return $filename;
}

/**
 * @param $obj
 * @return array
 */
function fm_object_to_array($obj)
{
    if (!is_object($obj) && !is_array($obj)) {
        return $obj;
    }
    if (is_object($obj)) {
        $obj = get_object_vars($obj);
    }
    return array_map('fm_object_to_array', $obj);
}

/**
 * Get CSS classname for file
 * @param string $path
 * @return string
 */
function fm_get_file_icon_class($path)
{
    // get extension
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));

    switch ($ext) {
        case 'ico':
        case 'gif':
        case 'jpg':
        case 'jpeg':
        case 'jpc':
        case 'jp2':
        case 'jpx':
        case 'xbm':
        case 'wbmp':
        case 'png':
        case 'bmp':
        case 'tif':
        case 'tiff':
        case 'webp':
        case 'avif':
        case 'svg':
            $img = 'fa fa-picture-o';
            break;
        case 'passwd':
        case 'ftpquota':
        case 'sql':
        case 'js':
        case 'ts':
        case 'jsx':
        case 'tsx':
        case 'hbs':
        case 'json':
        case 'sh':
        case 'config':
        case 'twig':
        case 'tpl':
        case 'md':
        case 'gitignore':
        case 'c':
        case 'cpp':
        case 'cs':
        case 'py':
        case 'rs':
        case 'map':
        case 'lock':
        case 'dtd':
            $img = 'fa fa-file-code-o';
            break;
        case 'txt':
        case 'ini':
        case 'conf':
        case 'log':
        case 'htaccess':
        case 'yaml':
        case 'yml':
        case 'toml':
        case 'tmp':
        case 'top':
        case 'bot':
        case 'dat':
        case 'bak':
        case 'htpasswd':
        case 'pl':
            $img = 'fa fa-file-text-o';
            break;
        case 'css':
        case 'less':
        case 'sass':
        case 'scss':
            $img = 'fa fa-css3';
            break;
        case 'bz2':
        case 'tbz2':
        case 'tbz':
        case 'zip':
        case 'rar':
        case 'gz':
        case 'tgz':
        case 'tar':
        case '7z':
        case 'xz':
        case 'txz':
        case 'zst':
        case 'tzst':
            $img = 'fa fa-file-archive-o';
            break;
        case 'php':
        case 'php4':
        case 'php5':
        case 'phps':
        case 'phtml':
            $img = 'fa fa-code';
            break;
        case 'htm':
        case 'html':
        case 'shtml':
        case 'xhtml':
            $img = 'fa fa-html5';
            break;
        case 'xml':
        case 'xsl':
            $img = 'fa fa-file-excel-o';
            break;
        case 'wav':
        case 'mp3':
        case 'mp2':
        case 'm4a':
        case 'aac':
        case 'ogg':
        case 'oga':
        case 'wma':
        case 'mka':
        case 'flac':
        case 'ac3':
        case 'tds':
            $img = 'fa fa-music';
            break;
        case 'm3u':
        case 'm3u8':
        case 'pls':
        case 'cue':
        case 'xspf':
            $img = 'fa fa-headphones';
            break;
        case 'avi':
        case 'mpg':
        case 'mpeg':
        case 'mp4':
        case 'm4v':
        case 'flv':
        case 'f4v':
        case 'ogm':
        case 'ogv':
        case 'mov':
        case 'mkv':
        case '3gp':
        case 'asf':
        case 'wmv':
        case 'webm':
            $img = 'fa fa-file-video-o';
            break;
        case 'eml':
        case 'msg':
            $img = 'fa fa-envelope-o';
            break;
        case 'xls':
        case 'xlsx':
        case 'ods':
            $img = 'fa fa-file-excel-o';
            break;
        case 'csv':
            $img = 'fa fa-file-text-o';
            break;
        case 'bak':
        case 'swp':
            $img = 'fa fa-clipboard';
            break;
        case 'doc':
        case 'docx':
        case 'odt':
            $img = 'fa fa-file-word-o';
            break;
        case 'ppt':
        case 'pptx':
            $img = 'fa fa-file-powerpoint-o';
            break;
        case 'ttf':
        case 'ttc':
        case 'otf':
        case 'woff':
        case 'woff2':
        case 'eot':
        case 'fon':
            $img = 'fa fa-font';
            break;
        case 'pdf':
            $img = 'fa fa-file-pdf-o';
            break;
        case 'psd':
        case 'ai':
        case 'eps':
        case 'fla':
        case 'swf':
            $img = 'fa fa-file-image-o';
            break;
        case 'exe':
        case 'msi':
            $img = 'fa fa-file-o';
            break;
        case 'bat':
            $img = 'fa fa-terminal';
            break;
        default:
            $img = 'fa fa-info-circle';
    }

    return $img;
}

/**
 * Get image files extensions
 * @return array
 */
function fm_get_image_exts()
{
    return array('ico', 'gif', 'jpg',
'jpeg', 'jpc', 'jp2', 'jpx',
'xbm', 'wbmp', 'png', 'bmp',
'tif', 'tiff', 'psd', 'svg',
'webp', 'avif');
}

/**
 * Get video files extensions
 * @return array
 */
function fm_get_video_exts()
{
    return array('avi', 'webm', 'wmv',
'mp4', 'm4v', 'ogm', 'ogv',
'mov', 'mkv');
}

/**
 * Get audio files extensions
 * @return array
 */
function fm_get_audio_exts()
{
    return array('wav', 'mp3', 'ogg',
'm4a');
}

/**
 * Get text file extensions
 * @return array
 */
function fm_get_text_exts()
{
    return array(
        'txt', 'css', 'ini',
'conf', 'log', 'htaccess',
'passwd', 'ftpquota', 'sql', 'js',
'ts', 'jsx', 'tsx', 'mjs',
'json', 'sh', 'config',
        'php', 'php4', 'php5',
'phps', 'phtml', 'htm', 'html',
'shtml', 'xhtml', 'xml', 'xsl',
'm3u', 'm3u8', 'pls', 'cue',
'bash', 'vue',
        'eml', 'msg', 'csv', 'bat',
'twig', 'tpl', 'md', 'gitignore',
'less', 'sass', 'scss', 'c',
'cpp', 'cs', 'py', 'go',
'zsh', 'swift',
        'map', 'lock', 'dtd',
'svg', 'asp', 'aspx', 'asx',
'asmx', 'ashx', 'jsp', 'jspx',
'cgi', 'dockerfile', 'ruby', 'yml',
'yaml', 'toml',
        'vhost', 'scpt', 'applescript',
'csx', 'cshtml', 'c++', 'coffee',
'cfm', 'rb', 'graphql', 'mustache',
'jinja', 'http', 'handlebars',
        'java', 'es', 'es6',
'markdown', 'wiki', 'tmp', 'top',
'bot', 'dat', 'bak', 'htpasswd',
'pl'
    );
}

/**
 * Get mime types of text files
 * @return array
 */
function fm_get_text_mimes()
{
    return array(
        'application/xml',
        'application/javascript',
        'application/x-javascript',
        'image/svg+xml',
        'message/rfc822',
        'application/json',
    );
}

/**
 * Get file names of text files w/o extensions
 * @return array
 */
function fm_get_text_names()
{
    return array(
        'license',
        'readme',
        'authors',
        'contributors',
        'changelog',
    );
}

/**
 * Get online docs viewer supported files extensions
 * @return array
 */
function fm_get_onlineViewer_exts()
{
    return array('doc', 'docx', 'xls',
'xlsx', 'pdf', 'ppt', 'pptx',
'ai', 'psd', 'dxf', 'xps',
'rar', 'odt', 'ods');
}

/**
 * It returns the mime type of a file based on its extension.
 * @param extension The file extension of the file you want to get the mime
type for.
 * @return string|string[] The mime type of the file.
 */
function fm_get_file_mimes($extension)
{
    $fileTypes['swf'] =
'application/x-shockwave-flash';
    $fileTypes['pdf'] = 'application/pdf';
    $fileTypes['exe'] = 'application/octet-stream';
    $fileTypes['zip'] = 'application/zip';
    $fileTypes['doc'] = 'application/msword';
    $fileTypes['xls'] = 'application/vnd.ms-excel';
    $fileTypes['ppt'] =
'application/vnd.ms-powerpoint';
    $fileTypes['gif'] = 'image/gif';
    $fileTypes['png'] = 'image/png';
    $fileTypes['jpeg'] = 'image/jpg';
    $fileTypes['jpg'] = 'image/jpg';
    $fileTypes['webp'] = 'image/webp';
    $fileTypes['avif'] = 'image/avif';
    $fileTypes['rar'] = 'application/rar';

    $fileTypes['ra'] = 'audio/x-pn-realaudio';
    $fileTypes['ram'] = 'audio/x-pn-realaudio';
    $fileTypes['ogg'] = 'audio/x-pn-realaudio';

    $fileTypes['wav'] = 'video/x-msvideo';
    $fileTypes['wmv'] = 'video/x-msvideo';
    $fileTypes['avi'] = 'video/x-msvideo';
    $fileTypes['asf'] = 'video/x-msvideo';
    $fileTypes['divx'] = 'video/x-msvideo';

    $fileTypes['mp3'] = 'audio/mpeg';
    $fileTypes['mp4'] = 'audio/mpeg';
    $fileTypes['mpeg'] = 'video/mpeg';
    $fileTypes['mpg'] = 'video/mpeg';
    $fileTypes['mpe'] = 'video/mpeg';
    $fileTypes['mov'] = 'video/quicktime';
    $fileTypes['swf'] = 'video/quicktime';
    $fileTypes['3gp'] = 'video/quicktime';
    $fileTypes['m4a'] = 'video/quicktime';
    $fileTypes['aac'] = 'video/quicktime';
    $fileTypes['m3u'] = 'video/quicktime';

    $fileTypes['php'] = ['application/x-php'];
    $fileTypes['html'] = ['text/html'];
    $fileTypes['txt'] = ['text/plain'];
    //Unknown mime-types should be 'application/octet-stream'
    if(empty($fileTypes[$extension])) {
      $fileTypes[$extension] = ['application/octet-stream'];
    }
    return $fileTypes[$extension];
}

/**
 * This function scans the files and folder recursively, and return
matching files
 * @param string $dir
 * @param string $filter
 * @return array|null
 */
 function scan($dir = '', $filter = '') {
    $path = FM_ROOT_PATH.'/'.$dir;
     if($path) {
         $ite = new RecursiveIteratorIterator(new
RecursiveDirectoryIterator($path));
         $rii = new RegexIterator($ite, "/(" . $filter .
")/i");

         $files = array();
         foreach ($rii as $file) {
             if (!$file->isDir()) {
                 $fileName = $file->getFilename();
                 $location = str_replace(FM_ROOT_PATH, '',
$file->getPath());
                 $files[] = array(
                     "name" => $fileName,
                     "type" => "file",
                     "path" => $location,
                 );
             }
         }
         return $files;
     }
}

/**
* Parameters: downloadFile(File Location, File Name,
* max speed, is streaming
* If streaming - videos will show as videos, images as images
* instead of download prompt
* https://stackoverflow.com/a/13821992/1164642
*/
function fm_download_file($fileLocation, $fileName, $chunkSize  = 1024)
{
    if (connection_status() != 0)
        return (false);
    $extension = pathinfo($fileName, PATHINFO_EXTENSION);

    $contentType = fm_get_file_mimes($extension);

    if(is_array($contentType)) {
        $contentType = implode(' ', $contentType);
    }

    $size = filesize($fileLocation);

    if ($size == 0) {
        fm_set_msg(lng('Zero byte file! Aborting download'),
'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));

        return (false);
    }

    @ini_set('magic_quotes_runtime', 0);
    $fp = fopen("$fileLocation", "rb");

    if ($fp === false) {
        fm_set_msg(lng('Cannot open file! Aborting download'),
'error');
        $FM_PATH=FM_PATH; fm_redirect(FM_SELF_URL . '?p=' .
urlencode($FM_PATH));
        return (false);
    }

    // headers
    header('Content-Description: File Transfer');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
    header('Pragma: public');
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: $contentType");

    $contentDisposition = 'attachment';

    if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
        $fileName = preg_replace('/\./', '%2e',
$fileName, substr_count($fileName, '.') - 1);
        header("Content-Disposition:
$contentDisposition;filename=\"$fileName\"");
    } else {
        header("Content-Disposition:
$contentDisposition;filename=\"$fileName\"");
    }

    header("Accept-Ranges: bytes");
    $range = 0;

    if (isset($_SERVER['HTTP_RANGE'])) {
        list($a, $range) = explode("=",
$_SERVER['HTTP_RANGE']);
        str_replace($range, "-", $range);
        $size2 = $size - 1;
        $new_length = $size - $range;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $range$size2/$size");
    } else {
        $size2 = $size - 1;
        header("Content-Range: bytes 0-$size2/$size");
        header("Content-Length: " . $size);
    }
    $fileLocation = realpath($fileLocation);
    while (ob_get_level()) ob_end_clean();
    readfile($fileLocation);

    fclose($fp);

    return ((connection_status() == 0) and !connection_aborted());
}

/**
 * If the theme is dark, return the text-white and bg-dark classes.
 * @return string the value of the  variable.
 */
function fm_get_theme() {
    $result = '';
    if(FM_THEME == "dark") {
        $result = "text-white bg-dark";
    }
    return $result;
}

/**
 * Class to work with zip files (using ZipArchive)
 */
class FM_Zipper
{
    private $zip;

    public function __construct()
    {
        $this->zip = new ZipArchive();
    }

    /**
     * Create archive with name $filename and files $files (RELATIVE
PATHS!)
     * @param string $filename
     * @param array|string $files
     * @return bool
     */
    public function create($filename, $files)
    {
        $res = $this->zip->open($filename, ZipArchive::CREATE);
        if ($res !== true) {
            return false;
        }
        if (is_array($files)) {
            foreach ($files as $f) {
                $f = fm_clean_path($f);
                if (!$this->addFileOrDir($f)) {
                    $this->zip->close();
                    return false;
                }
            }
            $this->zip->close();
            return true;
        } else {
            if ($this->addFileOrDir($files)) {
                $this->zip->close();
                return true;
            }
            return false;
        }
    }

    /**
     * Extract archive $filename to folder $path (RELATIVE OR ABSOLUTE
PATHS)
     * @param string $filename
     * @param string $path
     * @return bool
     */
    public function unzip($filename, $path)
    {
        $res = $this->zip->open($filename);
        if ($res !== true) {
            return false;
        }
        if ($this->zip->extractTo($path)) {
            $this->zip->close();
            return true;
        }
        return false;
    }

    /**
     * Add file/folder to archive
     * @param string $filename
     * @return bool
     */
    private function addFileOrDir($filename)
    {
        if (is_file($filename)) {
            return $this->zip->addFile($filename);
        } elseif (is_dir($filename)) {
            return $this->addDir($filename);
        }
        return false;
    }

    /**
     * Add folder recursively
     * @param string $path
     * @return bool
     */
    private function addDir($path)
    {
        if (!$this->zip->addEmptyDir($path)) {
            return false;
        }
        $objects = scandir($path);
        if (is_array($objects)) {
            foreach ($objects as $file) {
                if ($file != '.' && $file !=
'..') {
                    if (is_dir($path . '/' . $file)) {
                        if (!$this->addDir($path . '/' .
$file)) {
                            return false;
                        }
                    } elseif (is_file($path . '/' . $file)) {
                        if (!$this->zip->addFile($path .
'/' . $file)) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
        return false;
    }
}

/**
 * Class to work with Tar files (using PharData)
 */
class FM_Zipper_Tar
{
    private $tar;

    public function __construct()
    {
        $this->tar = null;
    }

    /**
     * Create archive with name $filename and files $files (RELATIVE
PATHS!)
     * @param string $filename
     * @param array|string $files
     * @return bool
     */
    public function create($filename, $files)
    {
        $this->tar = new PharData($filename);
        if (is_array($files)) {
            foreach ($files as $f) {
                $f = fm_clean_path($f);
                if (!$this->addFileOrDir($f)) {
                    return false;
                }
            }
            return true;
        } else {
            if ($this->addFileOrDir($files)) {
                return true;
            }
            return false;
        }
    }

    /**
     * Extract archive $filename to folder $path (RELATIVE OR ABSOLUTE
PATHS)
     * @param string $filename
     * @param string $path
     * @return bool
     */
    public function unzip($filename, $path)
    {
        $res = $this->tar->open($filename);
        if ($res !== true) {
            return false;
        }
        if ($this->tar->extractTo($path)) {
            return true;
        }
        return false;
    }

    /**
     * Add file/folder to archive
     * @param string $filename
     * @return bool
     */
    private function addFileOrDir($filename)
    {
        if (is_file($filename)) {
            try {
                $this->tar->addFile($filename);
                return true;
            } catch (Exception $e) {
                return false;
            }
        } elseif (is_dir($filename)) {
            return $this->addDir($filename);
        }
        return false;
    }

    /**
     * Add folder recursively
     * @param string $path
     * @return bool
     */
    private function addDir($path)
    {
        $objects = scandir($path);
        if (is_array($objects)) {
            foreach ($objects as $file) {
                if ($file != '.' && $file !=
'..') {
                    if (is_dir($path . '/' . $file)) {
                        if (!$this->addDir($path . '/' .
$file)) {
                            return false;
                        }
                    } elseif (is_file($path . '/' . $file)) {
                        try {
                            $this->tar->addFile($path . '/'
. $file);
                        } catch (Exception $e) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
        return false;
    }
}

/**
 * Save Configuration
 */
 class FM_Config
{
     var $data;

    function __construct()
    {
        global $root_path, $root_url, $CONFIG;
        $fm_url = $root_url.$_SERVER["PHP_SELF"];
        $this->data = array(
            'lang' => 'en',
            'error_reporting' => true,
            'show_hidden' => true
        );
        $data = false;
        if (strlen($CONFIG)) {
            $data = fm_object_to_array(json_decode($CONFIG));
        } else {
            $msg = 'Tiny File Manager<br>Error: Cannot load
configuration';
            if (substr($fm_url, -1) == '/') {
                $fm_url = rtrim($fm_url, '/');
                $msg .= '<br>';
                $msg .= '<br>Seems like you have a trailing
slash on the URL.';
                $msg .= '<br>Try this link: <a
href="' . $fm_url . '">' . $fm_url .
'</a>';
            }
            die($msg);
        }
        if (is_array($data) && count($data)) $this->data =
$data;
        else $this->save();
    }

    function save()
    {
        $fm_file = __FILE__;
        $var_name = '$CONFIG';
        $var_value = var_export(json_encode($this->data), true);
        $config_string = "<?php" . chr(13) . chr(10) .
"//Default Configuration".chr(13) . chr(10)."$var_name =
$var_value;" . chr(13) . chr(10);
        if (is_writable($fm_file)) {
            $lines = file($fm_file);
            if ($fh = @fopen($fm_file, "w")) {
                @fputs($fh, $config_string, strlen($config_string));
                for ($x = 3; $x < count($lines); $x++) {
                    @fputs($fh, $lines[$x], strlen($lines[$x]));
                }
                @fclose($fh);
            }
        }
    }
}

//--- Templates Functions ---

/**
 * Show nav block
 * @param string $path
 */
function fm_show_nav_path($path)
{
    global $lang, $sticky_navbar, $editFile;
    $isStickyNavBar = $sticky_navbar ? 'fixed-top' :
'';
    $getTheme = fm_get_theme();
    $getTheme .= " navbar-light";
    if(FM_THEME == "dark") {
        $getTheme .= " navbar-dark";
    } else {
        $getTheme .= " bg-white";
    }
    ?>
    <nav class="navbar navbar-expand-lg <?php echo $getTheme;
?> mb-4 main-nav <?php echo $isStickyNavBar ?>">
        <a class="navbar-brand"> <?php echo
lng('AppTitle') ?> </a>
        <button class="navbar-toggler" type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle
navigation">
            <span
class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse"
id="navbarSupportedContent">

            <?php
            $path = fm_clean_path($path);
            $root_url = "<a href='?p='><i
class='fa fa-home' aria-hidden='true'
title='" . FM_ROOT_PATH .
"'></i></a>";
            $sep = '<i class="bread-crumb"> /
</i>';
            if ($path != '') {
                $exploded = explode('/', $path);
                $count = count($exploded);
                $array = array();
                $parent = '';
                for ($i = 0; $i < $count; $i++) {
                    $parent = trim($parent . '/' . $exploded[$i],
'/');
                    $parent_enc = urlencode($parent);
                    $array[] = "<a
href='?p={$parent_enc}'>" .
fm_enc(fm_convert_win($exploded[$i])) . "</a>";
                }
                $root_url .= $sep . implode($sep, $array);
            }
            echo '<div class="col-xs-6
col-sm-5">' . $root_url . $editFile .
'</div>';
            ?>

            <div class="col-xs-6 col-sm-7">
                <ul class="navbar-nav justify-content-end <?php
echo fm_get_theme();  ?>">
                    <li class="nav-item mr-2">
                        <div class="input-group input-group-sm
mr-1" style="margin-top:4px;">
                            <input type="text"
class="form-control" placeholder="<?php echo
lng('Search') ?>" aria-label="<?php echo
lng('Search') ?>"
aria-describedby="search-addon2" id="search-addon">
                            <div
class="input-group-append">
                                <span class="input-group-text brl-0
brr-0" id="search-addon2"><i class="fa
fa-search"></i></span>
                            </div>
                            <div class="input-group-append
btn-group">
                                <span class="input-group-text
dropdown-toggle brl-0" data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"></span>
                                  <div class="dropdown-menu
dropdown-menu-right">
                                    <a class="dropdown-item"
href="<?php echo $path2 = $path ? $path : '.';
?>" id="js-search-modal" data-bs-toggle="modal"
data-bs-target="#searchModal"><?php echo lng('Advanced
Search') ?></a>
                                  </div>
                            </div>
                        </div>
                    </li>
                    <?php if (!FM_READONLY): ?>
                    <li class="nav-item">
                        <a title="<?php echo
lng('Upload') ?>" class="nav-link"
href="?p=<?php echo urlencode(FM_PATH)
?>&amp;upload"><i class="fa fa-cloud-upload"
aria-hidden="true"></i> <?php echo
lng('Upload') ?></a>
                    </li>
                    <li class="nav-item">
                        <a title="<?php echo
lng('NewItem') ?>" class="nav-link"
href="#createNewItem" data-bs-toggle="modal"
data-bs-target="#createNewItem"><i class="fa
fa-plus-square"></i> <?php echo lng('NewItem')
?></a>
                    </li>
                    <?php endif; ?>
                    <?php if (FM_USE_AUTH): ?>
                    <li class="nav-item avatar dropdown">
                        <a class="nav-link dropdown-toggle"
id="navbarDropdownMenuLink-5" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"> <i
class="fa fa-user-circle"></i> <?php
if(isset($_SESSION[FM_SESSION_ID]['logged'])) { echo
$_SESSION[FM_SESSION_ID]['logged']; } ?></a>
                        <div class="dropdown-menu text-small shadow
<?php echo fm_get_theme(); ?>"
aria-labelledby="navbarDropdownMenuLink-5">
                            <?php if (!FM_READONLY): ?>
                            <a title="<?php echo
lng('Settings') ?>" class="dropdown-item
nav-link" href="?p=<?php echo urlencode(FM_PATH)
?>&amp;settings=1"><i class="fa fa-cog"
aria-hidden="true"></i> <?php echo
lng('Settings') ?></a>
                            <?php endif ?>
                            <a title="<?php echo
lng('Help') ?>" class="dropdown-item nav-link"
href="?p=<?php echo urlencode(FM_PATH)
?>&amp;help=2"><i class="fa
fa-exclamation-circle" aria-hidden="true"></i>
<?php echo lng('Help') ?></a>
                            <a title="<?php echo
lng('Logout') ?>" class="dropdown-item
nav-link" href="?logout=1"><i class="fa
fa-sign-out" aria-hidden="true"></i> <?php echo
lng('Logout') ?></a>
                        </div>
                    </li>
                    <?php else: ?>
                        <?php if (!FM_READONLY): ?>
                            <li class="nav-item">
                                <a title="<?php echo
lng('Settings') ?>" class="dropdown-item
nav-link" href="?p=<?php echo urlencode(FM_PATH)
?>&amp;settings=1"><i class="fa fa-cog"
aria-hidden="true"></i> <?php echo
lng('Settings') ?></a>
                            </li>
                        <?php endif; ?>
                    <?php endif; ?>
                </ul>
            </div>
        </div>
    </nav>
    <?php
}

/**
 * Show alert message from session
 */
function fm_show_message()
{
    if (isset($_SESSION[FM_SESSION_ID]['message'])) {
        $class = isset($_SESSION[FM_SESSION_ID]['status']) ?
$_SESSION[FM_SESSION_ID]['status'] : 'ok';
        echo '<p class="message ' . $class .
'">' . $_SESSION[FM_SESSION_ID]['message'] .
'</p>';
        unset($_SESSION[FM_SESSION_ID]['message']);
        unset($_SESSION[FM_SESSION_ID]['status']);
    }
}

/**
 * Show page header in Login Form
 */
function fm_show_header_login()
{
$sprites_ver = '20160315';
header("Content-Type: text/html; charset=utf-8");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-store, no-cache, must-revalidate,
post-check=0, pre-check=0");
header("Pragma: no-cache");

global $lang, $root_url, $favicon_path;
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,
initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="Web based File
Manager in PHP, Manage your files efficiently and easily with Tiny File
Manager">
    <meta name="author" content="CCP
Programmers">
    <meta name="robots" content="noindex,
nofollow">
    <meta name="googlebot" content="noindex">
    <?php if($favicon_path) { echo '<link rel="icon"
href="'.fm_enc($favicon_path).'"
type="image/png">'; } ?>
    <title><?php echo fm_enc(APP_TITLE) ?></title>
    <?php print_external('pre-jsdelivr'); ?>
    <?php print_external('css-bootstrap'); ?>
    <style>
        body.fm-login-page{
background-color:#f7f9fb;font-size:14px;background-color:#f7f9fb;background-image:url("data:image/svg+xml,%3Csvg
xmlns='http://www.w3.org/2000/svg' viewBox='0 0 304
304' width='304' height='304'%3E%3Cpath
fill='%23e2e9f1' fill-opacity='0.4' d='M44.1 224a5
5 0 1 1 0 2H0v-2h44.1zm160 48a5 5 0 1 1 0 2H82v-2h122.1zm57.8-46a5 5 0 1 1
0-2H304v2h-42.1zm0 16a5 5 0 1 1 0-2H304v2h-42.1zm6.2-114a5 5 0 1 1 0
2h-86.2a5 5 0 1 1 0-2h86.2zm-256-48a5 5 0 1 1 0 2H0v-2h12.1zm185.8 34a5 5 0
1 1 0-2h86.2a5 5 0 1 1 0 2h-86.2zM258 12.1a5 5 0 1 1-2 0V0h2v12.1zm-64
208a5 5 0 1 1-2 0v-54.2a5 5 0 1 1 2 0v54.2zm48-198.2V80h62v2h-64V21.9a5 5 0
1 1 2 0zm16 16V64h46v2h-48V37.9a5 5 0 1 1 2 0zm-128 96V208h16v12.1a5 5 0 1
1-2 0V210h-16v-76.1a5 5 0 1 1 2 0zm-5.9-21.9a5 5 0 1 1 0 2H114v48H85.9a5 5
0 1 1 0-2H112v-48h12.1zm-6.2 130a5 5 0 1 1 0-2H176v-74.1a5 5 0 1 1 2
0V242h-60.1zm-16-64a5 5 0 1 1 0-2H114v48h10.1a5 5 0 1 1 0
2H112v-48h-10.1zM66 284.1a5 5 0 1 1-2 0V274H50v30h-2v-32h18v12.1zM236.1
176a5 5 0 1 1 0 2H226v94h48v32h-2v-30h-48v-98h12.1zm25.8-30a5 5 0 1 1
0-2H274v44.1a5 5 0 1 1-2 0V146h-10.1zm-64 96a5 5 0 1 1
0-2H208v-80h16v-14h-42.1a5 5 0 1 1 0-2H226v18h-16v80h-12.1zm86.2-210a5 5 0
1 1 0 2H272V0h2v32h10.1zM98 101.9V146H53.9a5 5 0 1 1 0-2H96v-42.1a5 5 0 1 1
2 0zM53.9 34a5 5 0 1 1 0-2H80V0h2v34H53.9zm60.1 3.9V66H82v64H69.9a5 5 0 1 1
0-2H80V64h32V37.9a5 5 0 1 1 2 0zM101.9 82a5 5 0 1 1 0-2H128V37.9a5 5 0 1 1
2 0V82h-28.1zm16-64a5 5 0 1 1 0-2H146v44.1a5 5 0 1 1-2 0V18h-26.1zm102.2
270a5 5 0 1 1 0 2H98v14h-2v-16h124.1zM242
149.9V160h16v34h-16v62h48v48h-2v-46h-48v-66h16v-30h-16v-12.1a5 5 0 1 1 2
0zM53.9 18a5 5 0 1 1 0-2H64V2H48V0h18v18H53.9zm112 32a5 5 0 1 1
0-2H192V0h50v2h-48v48h-28.1zm-48-48a5 5 0 0 1-9.8-2h2.07a3 3 0 1 0 5.66
0H178v34h-18V21.9a5 5 0 1 1 2 0V32h14V2h-58.1zm0 96a5 5 0 1 1
0-2H137l32-32h39V21.9a5 5 0 1 1 2 0V66h-40.17l-32 32H117.9zm28.1 90.1a5 5 0
1 1-2 0v-76.51L175.59 80H224V21.9a5 5 0 1 1 2 0V82h-49.59L146
112.41v75.69zm16 32a5 5 0 1 1-2 0v-99.51L184.59 96H300.1a5 5 0 0 1
3.9-3.9v2.07a3 3 0 0 0 0 5.66v2.07a5 5 0 0 1-3.9-3.9H185.41L162
121.41v98.69zm-144-64a5 5 0 1 1-2 0v-3.51l48-48V48h32V0h2v50H66v55.41l-48
48v2.69zM50 53.9v43.51l-48 48V208h26.1a5 5 0 1 1 0 2H0v-65.41l48-48V53.9a5
5 0 1 1 2 0zm-16 16V89.41l-34 34v-2.82l32-32V69.9a5 5 0 1 1 2 0zM12.1 32a5
5 0 1 1 0 2H9.41L0 43.41V40.6L8.59 32h3.51zm265.8 18a5 5 0 1 1
0-2h18.69l7.41-7.41v2.82L297.41 50H277.9zm-16 160a5 5 0 1 1
0-2H288v-71.41l16-16v2.82l-14 14V210h-28.1zm-208 32a5 5 0 1 1
0-2H64v-22.59L40.59 194H21.9a5 5 0 1 1 0-2H41.41L66 216.59V242H53.9zm150.2
14a5 5 0 1 1 0 2H96v-56.6L56.6 162H37.9a5 5 0 1 1 0-2h19.5L98
200.6V256h106.1zm-150.2 2a5 5 0 1 1 0-2H80v-46.59L48.59 178H21.9a5 5 0 1 1
0-2H49.41L82 208.59V258H53.9zM34 39.8v1.61L9.41 66H0v-2h8.59L32
40.59V0h2v39.8zM2 300.1a5 5 0 0 1 3.9 3.9H3.83A3 3 0 0 0 0
302.17V256h18v48h-2v-46H2v42.1zM34 241v63h-2v-62H0v-2h34v1zM17
18H0v-2h16V0h2v18h-1zm273-2h14v2h-16V0h2v16zm-32
273v15h-2v-14h-14v14h-2v-16h18v1zM0 92.1A5.02 5.02 0 0 1 6 97a5 5 0 0 1-6
4.9v-2.07a3 3 0 1 0 0-5.66V92.1zM80 272h2v32h-2v-32zm37.9 32h-2.07a3 3 0 0
0-5.66 0h-2.07a5 5 0 0 1 9.8 0zM5.9 0A5.02 5.02 0 0 1 0 5.9V3.83A3 3 0 0 0
3.83 0H5.9zm294.2 0h2.07A3 3 0 0 0 304 3.83V5.9a5 5 0 0 1-3.9-5.9zm3.9
300.1v2.07a3 3 0 0 0-1.83 1.83h-2.07a5 5 0 0 1 3.9-3.9zM97 100a3 3 0 1 0
0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3
3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0
0 0 6zm-48 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm32 48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm32-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16
32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3
0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0
0-6 3 3 0 0 0 0 6zm16 96a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3
3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-144a3 3 0 1 0 0-6 3 3 0
0 0 0 6zm0 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-96 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0
16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm96 0a3 3
0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1
0 0-6 3 3 0 0 0 0 6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6
3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0
0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM49 36a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM33
68a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 240a3
3 0 1 0 0-6 3 3 0 0 0 0 6zm16 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0
1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-32a3 3 0 1 0
0-6 3 3 0 0 0 0 6zm80-176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3
3 0 0 0 0 6zm-16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 48a3 3 0 1 0 0-6 3 3 0
0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm112 176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0
6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17
180a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3
0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17 84a3 3 0 1 0
0-6 3 3 0 0 0 0 6zm32 64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3
3 0 0 0 0 6z'%3E%3C/path%3E%3C/svg%3E");}
        .fm-login-page .brand{ width:121px;overflow:hidden;margin:0
auto;position:relative;z-index:1}
        .fm-login-page .brand img{ width:100%}
        .fm-login-page .card-wrapper{
width:360px;margin-top:10%;margin-left:auto;margin-right:auto;}
        .fm-login-page .card{ border-color:transparent;box-shadow:0 4px 8px
rgba(0,0,0,.05)}
        .fm-login-page .card-title{
margin-bottom:1.5rem;font-size:24px;font-weight:400;}
        .fm-login-page .form-control{ border-width:2.3px}
        .fm-login-page .form-group label{ width:100%}
        .fm-login-page .btn.btn-block{ padding:12px 10px}
        .fm-login-page .footer{ margin:40px
0;color:#888;text-align:center}
        @media screen and (max-width:425px){
            .fm-login-page .card-wrapper{ width:90%;margin:0
auto;margin-top:10%;}
        }
        @media screen and (max-width:320px){
            .fm-login-page .card.fat{ padding:0}
            .fm-login-page .card.fat .card-body{ padding:15px}
        }
        .message{ padding:4px 7px;border:1px solid
#ddd;background-color:#fff}
        .message.ok{ border-color:green;color:green}
        .message.error{ border-color:red;color:red}
        .message.alert{ border-color:orange;color:orange}
        body.fm-login-page.theme-dark {background-color: #2f2a2a;}
        .theme-dark svg g, .theme-dark svg path {fill: #ffffff; }
    </style>
</head>
<body class="fm-login-page <?php echo (FM_THEME ==
"dark") ? 'theme-dark' : ''; ?>">
<div id="wrapper" class="container-fluid">

    <?php
    }

    /**
     * Show page footer in Login Form
     */
    function fm_show_footer_login()
    {
    ?>
</div>
<?php print_external('js-jquery'); ?>
<?php print_external('js-bootstrap'); ?>
</body>
</html>
<?php
}

/**
 * Show Header after login
 */
function fm_show_header()
{
$sprites_ver = '20160315';
header("Content-Type: text/html; charset=utf-8");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-store, no-cache, must-revalidate,
post-check=0, pre-check=0");
header("Pragma: no-cache");

global $lang, $root_url, $sticky_navbar, $favicon_path;
$isStickyNavBar = $sticky_navbar ? 'navbar-fixed' :
'navbar-normal';
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,
initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="Web based File
Manager in PHP, Manage your files efficiently and easily with Tiny File
Manager">
    <meta name="author" content="CCP
Programmers">
    <meta name="robots" content="noindex,
nofollow">
    <meta name="googlebot" content="noindex">
    <?php if($favicon_path) { echo '<link rel="icon"
href="'.fm_enc($favicon_path).'"
type="image/png">'; } ?>
    <title><?php echo fm_enc(APP_TITLE) ?></title>
    <?php print_external('pre-jsdelivr'); ?>
    <?php print_external('pre-cloudflare'); ?>
    <?php print_external('css-bootstrap'); ?>
    <?php print_external('css-font-awesome'); ?>
    <?php if (FM_USE_HIGHLIGHTJS &&
isset($_GET['view'])): ?>
    <?php print_external('css-highlightjs'); ?>
    <?php endif; ?>
    <script type="text/javascript">window.csrf =
'<?php echo $_SESSION['token'];
?>';</script>
    <style>
        html { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing:
antialiased; text-rendering: optimizeLegibility; height: 100%;
scroll-behavior: smooth;}
        *,*::before,*::after { box-sizing: border-box;}
        body { font-size:15px; color:#222;background:#F7F7F7; }
        body.navbar-fixed { margin-top:55px; }
        a, a:hover, a:visited, a:focus { text-decoration:none !important;
}
        .filename, td, th { white-space:nowrap  }
        .navbar-brand { font-weight:bold; }
        .nav-item.avatar a { cursor:pointer;text-transform:capitalize; }
        .nav-item.avatar a > i { font-size:15px; }
        .nav-item.avatar .dropdown-menu a { font-size:13px; }
        #search-addon { font-size:12px;border-right-width:0; }
        .brl-0 { background:transparent;border-left:0;
border-top-left-radius: 0; border-bottom-left-radius: 0; }
        .brr-0 { border-top-right-radius: 0; border-bottom-right-radius: 0;
}
        .bread-crumb { color:#cccccc;font-style:normal; }
        #main-table { transition: transform .25s cubic-bezier(0.4, 0.5, 0,
1),width 0s .25s;}
        #main-table .filename a { color:#222222; }
        .table td, .table th { vertical-align:middle !important; }
        .table .custom-checkbox-td .custom-control.custom-checkbox, .table
.custom-checkbox-header .custom-control.custom-checkbox { min-width:18px;
display: flex;align-items: center; justify-content: center; }
        .table-sm td, .table-sm th { padding:.4rem; }
        .table-bordered td, .table-bordered th { border:1px solid #f1f1f1;
}
        .hidden { display:none  }
        pre.with-hljs { padding:0; overflow: hidden;  }
        pre.with-hljs code { margin:0;border:0;overflow:scroll;  }
        code.maxheight, pre.maxheight { max-height:512px  }
        .fa.fa-caret-right { font-size:1.2em;margin:0
4px;vertical-align:middle;color:#ececec  }
        .fa.fa-home { font-size:1.3em;vertical-align:bottom  }
        .path { margin-bottom:10px  }
        form.dropzone { min-height:200px;border:2px dashed
#007bff;line-height:6rem; }
        .right { text-align:right  }
        .center, .close, .login-form, .preview-img-container {
text-align:center  }
        .message { padding:4px 7px;border:1px solid
#ddd;background-color:#fff  }
        .message.ok { border-color:green;color:green  }
        .message.error { border-color:red;color:red  }
        .message.alert { border-color:orange;color:orange  }
        .preview-img {
max-width:100%;max-height:80vh;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKklEQVR42mL5//8/Azbw+PFjrOJMDCSCUQ3EABZc4S0rKzsaSvTTABBgAMyfCMsY4B9iAAAAAElFTkSuQmCC);cursor:zoom-in
}
        input#preview-img-zoomCheck[type=checkbox] { display:none }
        input#preview-img-zoomCheck[type=checkbox]:checked ~ label > img
{ max-width:none;max-height:none;cursor:zoom-out }
        .inline-actions > a > i {
font-size:1em;margin-left:5px;background:#3785c1;color:#fff;padding:3px
4px;border-radius:3px; }
        .preview-video {
position:relative;max-width:100%;height:0;padding-bottom:62.5%;margin-bottom:10px
 }
        .preview-video video {
position:absolute;width:100%;height:100%;left:0;top:0;background:#000  }
        .compact-table { border:0;width:auto  }
        .compact-table td, .compact-table th {
width:100px;border:0;text-align:center  }
        .compact-table tr:hover td { background-color:#fff  }
        .filename { max-width:420px;overflow:hidden;text-overflow:ellipsis 
}
        .break-word { word-wrap:break-word;margin-left:30px  }
        .break-word.float-left a { color:#7d7d7d  }
        .break-word + .float-right { padding-right:30px;position:relative 
}
        .break-word + .float-right > a {
color:#7d7d7d;font-size:1.2em;margin-right:4px  }
        #editor {
position:absolute;right:15px;top:100px;bottom:15px;left:15px  }
        @media (max-width:481px) {
            #editor { top:150px; }
        }
        #normal-editor {
border-radius:3px;border-width:2px;padding:10px;outline:none; }
        .btn-2 { padding:4px 10px;font-size:small; }
        li.file:before,li.folder:before { font:normal normal normal 14px/1
FontAwesome;content:"\f016";margin-right:5px }
        li.folder:before { content:"\f114" }
        i.fa.fa-folder-o { color:#0157b3 }
        i.fa.fa-picture-o { color:#26b99a }
        i.fa.fa-file-archive-o { color:#da7d7d }
        .btn-2 i.fa.fa-file-archive-o { color:inherit }
        i.fa.fa-css3 { color:#f36fa0 }
        i.fa.fa-file-code-o { color:#007bff }
        i.fa.fa-code { color:#cc4b4c }
        i.fa.fa-file-text-o { color:#0096e6 }
        i.fa.fa-html5 { color:#d75e72 }
        i.fa.fa-file-excel-o { color:#09c55d }
        i.fa.fa-file-powerpoint-o { color:#f6712e }
        i.go-back { font-size:1.2em;color:#007bff; }
        .main-nav { padding:0.2rem 1rem;box-shadow:0 4px 5px 0 rgba(0, 0,
0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2) 
}
        .dataTables_filter { display:none; }
        table.dataTable thead .sorting {
cursor:pointer;background-repeat:no-repeat;background-position:center
right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7XQMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC');
}
        table.dataTable thead .sorting_asc {
cursor:pointer;background-repeat:no-repeat;background-position:center
right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==');
}
        table.dataTable thead .sorting_desc {
cursor:pointer;background-repeat:no-repeat;background-position:center
right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=');
}
        table.dataTable thead tr:first-child
th.custom-checkbox-header:first-child { background-image:none; }
        .footer-action li { margin-bottom:10px; }
        .app-v-title {
font-size:24px;font-weight:300;letter-spacing:-.5px;text-transform:uppercase;
}
        hr.custom-hr { border-top:1px dashed #8c8b8b;border-bottom:1px
dashed #fff; }
        #snackbar {
visibility:hidden;min-width:250px;margin-left:-125px;background-color:#333;color:#fff;text-align:center;border-radius:2px;padding:16px;position:fixed;z-index:1;left:50%;bottom:30px;font-size:17px;
}
        #snackbar.show { visibility:visible;-webkit-animation:fadein 0.5s,
fadeout 0.5s 2.5s;animation:fadein 0.5s, fadeout 0.5s 2.5s; }
        @-webkit-keyframes fadein { from { bottom:0;opacity:0; }
        to { bottom:30px;opacity:1; }
        }
        @keyframes fadein { from { bottom:0;opacity:0; }
        to { bottom:30px;opacity:1; }
        }
        @-webkit-keyframes fadeout { from { bottom:30px;opacity:1; }
        to { bottom:0;opacity:0; }
        }
        @keyframes fadeout { from { bottom:30px;opacity:1; }
        to { bottom:0;opacity:0; }
        }
        #main-table span.badge { border-bottom:2px solid #f8f9fa }
        #main-table span.badge:nth-child(1) { border-color:#df4227 }
        #main-table span.badge:nth-child(2) { border-color:#f8b600 }
        #main-table span.badge:nth-child(3) { border-color:#00bd60 }
        #main-table span.badge:nth-child(4) { border-color:#4581ff }
        #main-table span.badge:nth-child(5) { border-color:#ac68fc }
        #main-table span.badge:nth-child(6) { border-color:#45c3d2 }
        @media only screen and (min-device-width:768px) and
(max-device-width:1024px) and (orientation:landscape) and
(-webkit-min-device-pixel-ratio:2) { .navbar-collapse .col-xs-6 {
padding:0; }
        }
       
.btn.active.focus,.btn.active:focus,.btn.focus,.btn.focus:active,.btn:active:focus,.btn:focus
{
outline:0!important;outline-offset:0!important;background-image:none!important;-webkit-box-shadow:none!important;box-shadow:none!important
}
        .lds-facebook {
display:none;position:relative;width:64px;height:64px }
        .lds-facebook div,.lds-facebook.show-me { display:inline-block }
        .lds-facebook div {
position:absolute;left:6px;width:13px;background:#007bff;animation:lds-facebook
1.2s cubic-bezier(0,.5,.5,1) infinite }
        .lds-facebook div:nth-child(1) { left:6px;animation-delay:-.24s }
        .lds-facebook div:nth-child(2) { left:26px;animation-delay:-.12s }
        .lds-facebook div:nth-child(3) { left:45px;animation-delay:0s }
        @keyframes lds-facebook { 0% { top:6px;height:51px }
        100%,50% { top:19px;height:26px }
        }
        ul#search-wrapper { padding-left: 0;border: 1px solid #ecececcc; }
ul#search-wrapper li { list-style: none; padding: 5px;border-bottom: 1px
solid #ecececcc; }
        ul#search-wrapper li:nth-child(odd){ background: #f9f9f9cc;}
        .c-preview-img { max-width: 300px; }
        .border-radius-0 { border-radius: 0; }
        .float-right { float: right; }
        .table-hover>tbody>tr:hover>td:first-child { border-left:
1px solid #1b77fd; }
        #main-table tr.even { background-color: #F8F9Fa; }
        .filename>a>i {margin-right: 3px;}
    </style>
    <?php
    if (FM_THEME == "dark"): ?>
        <style>
            :root {
                --bs-bg-opacity: 1;
                --bg-color: #f3daa6;
                --bs-dark-rgb: 28, 36, 41 !important;
                --bs-bg-opacity: 1;
            }
            .table-dark { --bs-table-bg: 28, 36, 41 !important; }
            .btn-primary { --bs-btn-bg: #26566c; --bs-btn-border-color:
#26566c; }
            body.theme-dark { background-image: linear-gradient(90deg,
#1c2429, #263238); color: #CFD8DC; }
            .list-group .list-group-item { background: #343a40; }
            .theme-dark .navbar-nav i, .navbar-nav .dropdown-toggle,
.break-word { color: #CFD8DC; }
            a, a:hover, a:visited, a:active, #main-table .filename a,
i.fa.fa-folder-o, i.go-back { color: var(--bg-color); }
            ul#search-wrapper li:nth-child(odd) { background: #212a2f; }
            .theme-dark .btn-outline-primary { color: #b8e59c;
border-color: #b8e59c; }
            .theme-dark .btn-outline-primary:hover, .theme-dark
.btn-outline-primary:active { background-color: #2d4121;}
            .theme-dark input.form-control { background-color: #101518;
color: #CFD8DC; }
            .theme-dark .dropzone { background: transparent; }
            .theme-dark .inline-actions > a > i { background:
#79755e; }
            .theme-dark .text-white { color: #CFD8DC !important; }
            .theme-dark .table-bordered td, .table-bordered th {
border-color: #343434; }
            .theme-dark .table-bordered td .custom-control-input,
.theme-dark .table-bordered th .custom-control-input { opacity: 0.678; }
            .message { background-color: #212529; }
            .compact-table tr:hover td { background-color: #3d3d3d; }
            #main-table tr.even { background-color: #21292f; }
            form.dropzone { border-color: #79755e; }
        </style>
    <?php endif; ?>
</head>
<body class="<?php echo (FM_THEME == "dark") ?
'theme-dark' : ''; ?> <?php echo $isStickyNavBar;
?>">
<div id="wrapper" class="container-fluid">
    <!-- New Item creation -->
    <div class="modal fade" id="createNewItem"
tabindex="-1" role="dialog"
data-bs-backdrop="static" data-bs-keyboard="false"
aria-labelledby="newItemModalLabel"
aria-hidden="true">
        <div class="modal-dialog"
role="document">
            <form class="modal-content <?php echo
fm_get_theme(); ?>" method="post">
                <div class="modal-header">
                    <h5 class="modal-title"
id="newItemModalLabel"><i class="fa fa-plus-square
fa-fw"></i><?php echo lng('CreateNewItem')
?></h5>
                    <button type="button"
class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <p><label for="newfile"><?php
echo lng('ItemType') ?> </label></p>
                    <div class="form-check
form-check-inline">
                      <input class="form-check-input"
type="radio" name="newfile"
id="customRadioInline1" name="newfile"
value="file">
                      <label class="form-check-label"
for="customRadioInline1"><?php echo lng('File')
?></label>
                    </div>
                    <div class="form-check
form-check-inline">
                      <input class="form-check-input"
type="radio" name="newfile"
id="customRadioInline2" value="folder" checked>
                      <label class="form-check-label"
for="customRadioInline2"><?php echo lng('Folder')
?></label>
                    </div>

                    <p class="mt-3"><label
for="newfilename"><?php echo lng('ItemName')
?> </label></p>
                    <input type="text"
name="newfilename" id="newfilename" value=""
class="form-control" placeholder="<?php echo
lng('Enter here...') ?>" required>
                </div>
                <div class="modal-footer">
                    <input type="hidden"
name="token" value="<?php echo
$_SESSION['token']; ?>">
                    <button type="button" class="btn
btn-outline-primary" data-bs-dismiss="modal"><i
class="fa fa-times-circle"></i> <?php echo
lng('Cancel') ?></button>
                    <button type="submit" class="btn
btn-success"><i class="fa
fa-check-circle"></i> <?php echo
lng('CreateNow') ?></button>
                </div>
            </form>
        </div>
    </div>

    <!-- Advance Search Modal -->
    <div class="modal fade" id="searchModal"
tabindex="-1" role="dialog"
aria-labelledby="searchModalLabel"
aria-hidden="true">
      <div class="modal-dialog modal-lg"
role="document">
        <div class="modal-content <?php echo fm_get_theme();
?>">
          <div class="modal-header">
            <h5 class="modal-title col-10"
id="searchModalLabel">
                <div class="input-group mb-3">
                  <input type="text"
class="form-control" placeholder="<?php echo
lng('Search') ?> <?php echo lng('a files')
?>" aria-label="<?php echo lng('Search')
?>" aria-describedby="search-addon3"
id="advanced-search" autofocus required>
                  <span class="input-group-text"
id="search-addon3"><i class="fa
fa-search"></i></span>
                </div>
            </h5>
            <button type="button" class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
          </div>
          <div class="modal-body">
            <form action="" method="post">
                <div
class="lds-facebook"><div></div><div></div><div></div></div>
                <ul id="search-wrapper">
                    <p class="m-2"><?php echo
lng('Search file in folder and subfolders...') ?></p>
                </ul>
            </form>
          </div>
        </div>
      </div>
    </div>

    <!--Rename Modal -->
    <div class="modal modal-alert"
data-bs-backdrop="static" data-bs-keyboard="false"
tabindex="-1" role="dialog"
id="renameDailog">
      <div class="modal-dialog" role="document">
        <form class="modal-content rounded-3 shadow <?php echo
fm_get_theme(); ?>" method="post"
autocomplete="off">
          <div class="modal-body p-4 text-center">
            <h5 class="mb-3"><?php echo lng('Are
you sure want to rename?') ?></h5>
            <p class="mb-1">
                <input type="text" name="rename_to"
id="js-rename-to" class="form-control"
placeholder="<?php echo lng('Enter new file name')
?>" required>
                <input type="hidden" name="token"
value="<?php echo $_SESSION['token']; ?>">
                <input type="hidden"
name="rename_from" id="js-rename-from">
            </p>
          </div>
          <div class="modal-footer flex-nowrap p-0">
            <button type="button" class="btn btn-lg
btn-link fs-6 text-decoration-none col-6 m-0 rounded-0 border-end"
data-bs-dismiss="modal"><?php echo lng('Cancel')
?></button>
            <button type="submit" class="btn btn-lg
btn-link fs-6 text-decoration-none col-6 m-0
rounded-0"><strong><?php echo lng('Okay')
?></strong></button>
          </div>
        </form>
      </div>
    </div>

    <!-- Confirm Modal -->
    <script type="text/html"
id="js-tpl-confirm">
        <div class="modal modal-alert confirmDailog"
data-bs-backdrop="static" data-bs-keyboard="false"
tabindex="-1" role="dialog"
id="confirmDailog-<%this.id%>">
          <div class="modal-dialog"
role="document">
            <form class="modal-content rounded-3 shadow <?php
echo fm_get_theme(); ?>" method="post"
autocomplete="off" action="<%this.action%>">
              <div class="modal-body p-4 text-center">
                <h5 class="mb-2"><?php echo
lng('Are you sure want to') ?> <%this.title%>
?</h5>
                <p
class="mb-1"><%this.content%></p>
              </div>
              <div class="modal-footer flex-nowrap p-0">
                <button type="button" class="btn btn-lg
btn-link fs-6 text-decoration-none col-6 m-0 rounded-0 border-end"
data-bs-dismiss="modal"><?php echo lng('Cancel')
?></button>
                <input type="hidden" name="token"
value="<?php echo $_SESSION['token']; ?>">
                <button type="submit" class="btn btn-lg
btn-link fs-6 text-decoration-none col-6 m-0 rounded-0"
data-bs-dismiss="modal"><strong><?php echo
lng('Okay') ?></strong></button>
              </div>
            </form>
          </div>
        </div>
    </script>

    <?php
    }

    /**
     * Show page footer after login
     */
    function fm_show_footer()
    {
    ?>
</div>
<?php print_external('js-jquery'); ?>
<?php print_external('js-bootstrap'); ?>
<?php print_external('js-jquery-datatables'); ?>
<?php if (FM_USE_HIGHLIGHTJS && isset($_GET['view'])):
?>
    <?php print_external('js-highlightjs'); ?>
    <script>hljs.highlightAll(); var isHighlightingEnabled =
true;</script>
<?php endif; ?>
<script>
    function template(html,options){
        var re=/<\%([^\%>]+)?\%>/g,reExp=/(^(
)?(if|for|else|switch|case|break|{|}))(.*)?/g,code='var
r=[];\n',cursor=0,match;var
add=function(line,js){js?(code+=line.match(reExp)?line+'\n':'r.push('+line+');\n'):(code+=line!=''?'r.push("'+line.replace(/"/g,'\\"')+'");\n':'');return
add}
       
while(match=re.exec(html)){add(html.slice(cursor,match.index))(match[1],!0);cursor=match.index+match[0].length}
        add(html.substr(cursor,html.length-cursor));code+='return
r.join("");';return new
Function(code.replace(/[\r\t\n]/g,'')).apply(options)
    }
    function rename(e, t) { if(t) {
$("#js-rename-from").val(t);$("#js-rename-to").val(t);
$("#renameDailog").modal('show'); } }
    function change_checkboxes(e, t) { for (var n = e.length - 1; n >=
0; n--) e[n].checked = "boolean" == typeof t ? t : !e[n].checked
}
    function get_checkboxes() { for (var e =
document.getElementsByName("file[]"), t = [], n = e.length - 1; n
>= 0; n--) (e[n].type = "checkbox") && t.push(e[n]);
return t }
    function select_all() { change_checkboxes(get_checkboxes(), !0) }
    function unselect_all() { change_checkboxes(get_checkboxes(), !1) }
    function invert_all() { change_checkboxes(get_checkboxes()) }
    function checkbox_toggle() { var e = get_checkboxes(); e.push(this),
change_checkboxes(e) }
    function backup(e, t) { // Create file backup with .bck
        var n = new XMLHttpRequest,
            a = "path=" + e + "&file=" + t +
"&token="+ window.csrf
+"&type=backup&ajax=true";
        return n.open("POST", "", !0),
n.setRequestHeader("Content-type",
"application/x-www-form-urlencoded"), n.onreadystatechange =
function () {
            4 == n.readyState && 200 == n.status &&
toast(n.responseText)
        }, n.send(a), !1
    }
    // Toast message
    function toast(txt) { var x =
document.getElementById("snackbar");x.innerHTML=txt;x.className =
"show";setTimeout(function(){ x.className =
x.className.replace("show", ""); }, 3000); }
    // Save file
    function edit_save(e, t) {
        var n = "ace" == t ? editor.getSession().getValue() :
document.getElementById("normal-editor").value;
        if (typeof n !== 'undefined' && n !== null) {
            if (true) {
                var data = {ajax: true, content: n, type: 'save',
token: window.csrf};

                $.ajax({
                    type: "POST",
                    url: window.location,
                    data: JSON.stringify(data),
                    contentType: "application/json;
charset=utf-8",
                    success: function(mes){toast("Saved
Successfully"); window.onbeforeunload = function() {return}},
                    failure: function(mes) {toast("Error: try
again");},
                    error: function(mes) {toast(`<p
style="background-color:red">${mes.responseText}</p>`);}
                });
            } else {
                var a = document.createElement("form");
                a.setAttribute("method", "POST"),
a.setAttribute("action", "");
                var o = document.createElement("textarea");
                o.setAttribute("type", "textarea"),
o.setAttribute("name", "savedata");
                let cx = document.createElement("input");
cx.setAttribute("type",
"hidden");cx.setAttribute("name",
"token");cx.setAttribute("value", window.csrf);
                var c = document.createTextNode(n);
                o.appendChild(c), a.appendChild(o), a.appendChild(cx),
document.body.appendChild(a), a.submit()
            }
        }
    }
    function show_new_pwd() {
$(".js-new-pwd").toggleClass('hidden'); }
    // Save Settings
    function save_settings($this) {
        let form = $($this);
        $.ajax({
            type: form.attr('method'), url:
form.attr('action'), data:
form.serialize()+"&token="+ window.csrf
+"&ajax="+true,
            success: function (data) {if(data) {
window.location.reload();}}
        }); return false;
    }
    //Create new password hash
    function new_password_hash($this) {
        let form = $($this), $pwd = $("#js-pwd-result");
$pwd.val('');
        $.ajax({
            type: form.attr('method'), url:
form.attr('action'), data:
form.serialize()+"&token="+ window.csrf
+"&ajax="+true,
            success: function (data) { if(data) { $pwd.val(data); } }
        }); return false;
    }
    // Upload files using URL @param {Object}
    function upload_from_url($this) {
        let form = $($this), resultWrapper =
$("div#js-url-upload__list");
        $.ajax({
            type: form.attr('method'), url:
form.attr('action'), data:
form.serialize()+"&token="+ window.csrf
+"&ajax="+true,
            beforeSend: function() {
form.find("input[name=uploadurl]").attr("disabled","disabled");
form.find("button").hide();
form.find(".lds-facebook").addClass('show-me'); },
            success: function (data) {
                if(data) {
                    data = JSON.parse(data);
                    if(data.done) {
                        resultWrapper.append('<div
class="alert alert-success row">Uploaded Successful:
'+data.done.name+'</div>');
form.find("input[name=uploadurl]").val('');
                    } else if(data['fail']) {
resultWrapper.append('<div class="alert alert-danger
row">Error: '+data.fail.message+'</div>'); }
                   
form.find("input[name=uploadurl]").removeAttr("disabled");form.find("button").show();form.find(".lds-facebook").removeClass('show-me');
                }
            },
            error: function(xhr) {
               
form.find("input[name=uploadurl]").removeAttr("disabled");form.find("button").show();form.find(".lds-facebook").removeClass('show-me');console.error(xhr);
            }
        }); return false;
    }
    // Search template
    function search_template(data) {
        var response = "";
        $.each(data, function (key, val) {
            response += `<li><a
href="?p=${val.path}&view=${val.name}">${val.path}/${val.name}</a></li>`;
        });
        return response;
    }
    // Advance search
    function fm_search() {
        var searchTxt = $("input#advanced-search").val(),
searchWrapper = $("ul#search-wrapper"), path =
$("#js-search-modal").attr("href"), _html =
"", $loader = $("div.lds-facebook");
        if(!!searchTxt && searchTxt.length > 2 && path)
{
            var data = {ajax: true, content: searchTxt, path:path, type:
'search', token: window.csrf };
            $.ajax({
                type: "POST",
                url: window.location,
                data: data,
                beforeSend: function() {
                    searchWrapper.html('');
                    $loader.addClass('show-me');
                },
                success: function(data){
                    $loader.removeClass('show-me');
                    data = JSON.parse(data);
                    if(data && data.length) {
                        _html = search_template(data);
                        searchWrapper.html(_html);
                    } else { searchWrapper.html('<p
class="m-2">No result found!<p>'); }
                },
                error: function(xhr) {
$loader.removeClass('show-me'); searchWrapper.html('<p
class="m-2">ERROR: Try again later!</p>'); },
                failure: function(mes) {
$loader.removeClass('show-me'); searchWrapper.html('<p
class="m-2">ERROR: Try again later!</p>');}
            });
        } else { searchWrapper.html("OOPS: minimum 3 characters
required!"); }
    }

    // action confirm dailog modal
    function confirmDailog(e, id = 0, title = "Action", content =
"", action = null) {
        e.preventDefault();
        const tplObj = {id, title, content:
decodeURIComponent(content.replace(/\+/g, ' ')), action};
        let tpl = $("#js-tpl-confirm").html();
        $(".modal.confirmDailog").remove();
        $('#wrapper').append(template(tpl,tplObj));
        const $confirmDailog = $("#confirmDailog-"+tplObj.id);
        $confirmDailog.modal('show');
        return false;
    }
    

    // on mouse hover image preview
    !function(s){s.previewImage=function(e){var
o=s(document),t=".previewImage",a=s.extend({xOffset:20,yOffset:-20,fadeIn:"fast",css:{padding:"5px",border:"1px
solid
#cccccc","background-color":"#fff"},eventSelector:"[data-preview-image]",dataKey:"previewImage",overlayId:"preview-image-plugin-overlay"},e);return
o.off(t),o.on("mouseover"+t,a.eventSelector,function(e){s("p#"+a.overlayId).remove();var
o=s("<p>").attr("id",a.overlayId).css("position","absolute").css("display","none").append(s('<img
class="c-preview-img">').attr("src",s(this).data(a.dataKey)));a.css&&o.css(a.css),s("body").append(o),o.css("top",e.pageY+a.yOffset+"px").css("left",e.pageX+a.xOffset+"px").fadeIn(a.fadeIn)}),o.on("mouseout"+t,a.eventSelector,function(){s("#"+a.overlayId).remove()}),o.on("mousemove"+t,a.eventSelector,function(e){s("#"+a.overlayId).css("top",e.pageY+a.yOffset+"px").css("left",e.pageX+a.xOffset+"px")}),this},s.previewImage()}(jQuery);

    // Dom Ready Events
    $(document).ready( function () {
        // dataTable init
        var $table = $('#main-table'),
            tableLng = $table.find('th').length,
            _targets = (tableLng && tableLng == 7 ) ? [0, 4,5,6] :
tableLng == 5 ? [0,4] : [3];
            mainTable = $('#main-table').DataTable({paging:
false, info: false, order: [], columnDefs: [{targets: _targets, orderable:
false}]
        });
        // filter table
        $('#search-addon').on( 'keyup', function () {
            mainTable.search( this.value ).draw();
        });
        $("input#advanced-search").on('keyup', function
(e) {
            if (e.keyCode === 13) { fm_search(); }
        });
        $('#search-addon3').on( 'click', function () {
fm_search(); });
        //upload nav tabs
        $(".fm-upload-wrapper
.card-header-tabs").on("click", 'a', function(e){
            e.preventDefault();let
target=$(this).data('target');
            $(".fm-upload-wrapper .card-header-tabs
a").removeClass('active');$(this).addClass('active');
            $(".fm-upload-wrapper
.card-tabs-container").addClass('hidden');$(target).removeClass('hidden');
        });
    });
</script>
<?php if (isset($_GET['edit']) &&
isset($_GET['env']) && FM_EDIT_FILE &&
!FM_READONLY):
        
        $ext = pathinfo($_GET["edit"], PATHINFO_EXTENSION);
        $ext =  $ext == "js" ? "javascript" :  $ext;
        ?>
    <?php print_external('js-ace'); ?>
    <script>
        var editor = ace.edit("editor");
        editor.getSession().setMode( {path:"ace/mode/<?php echo
$ext; ?>", inline:true} );
        //editor.setTheme("ace/theme/twilight"); //Dark Theme
        editor.setShowPrintMargin(false); // Hide the vertical ruler
        function ace_commend (cmd) { editor.commands.exec(cmd, editor); }
        editor.commands.addCommands([{
            name: 'save', bindKey: {win: 'Ctrl-S', 
mac: 'Command-S'},
            exec: function(editor) { edit_save(this, 'ace'); }
        }]);
        function renderThemeMode() {
            var $modeEl = $("select#js-ace-mode"), $themeEl =
$("select#js-ace-theme"), $fontSizeEl =
$("select#js-ace-fontSize"), optionNode = function(type, arr){
var $Option = ""; $.each(arr, function(i, val) { $Option +=
"<option value='"+type+i+"'>" + val +
"</option>"; }); return $Option; },
                _data =
{"aceTheme":{"bright":{"chrome":"Chrome","clouds":"Clouds","crimson_editor":"Crimson
Editor","dawn":"Dawn","dreamweaver":"Dreamweaver","eclipse":"Eclipse","github":"GitHub","iplastic":"IPlastic","solarized_light":"Solarized
Light","textmate":"TextMate","tomorrow":"Tomorrow","xcode":"XCode","kuroir":"Kuroir","katzenmilch":"KatzenMilch","sqlserver":"SQL
Server"},"dark":{"ambiance":"Ambiance","chaos":"Chaos","clouds_midnight":"Clouds
Midnight","dracula":"Dracula","cobalt":"Cobalt","gruvbox":"Gruvbox","gob":"Green
on Black","idle_fingers":"idle
Fingers","kr_theme":"krTheme","merbivore":"Merbivore","merbivore_soft":"Merbivore
Soft","mono_industrial":"Mono
Industrial","monokai":"Monokai","pastel_on_dark":"Pastel
on dark","solarized_dark":"Solarized
Dark","terminal":"Terminal","tomorrow_night":"Tomorrow
Night","tomorrow_night_blue":"Tomorrow Night
Blue","tomorrow_night_bright":"Tomorrow Night
Bright","tomorrow_night_eighties":"Tomorrow Night
80s","twilight":"Twilight","vibrant_ink":"Vibrant
Ink"}},"aceMode":{"javascript":"JavaScript","abap":"ABAP","abc":"ABC","actionscript":"ActionScript","ada":"ADA","apache_conf":"Apache
Conf","asciidoc":"AsciiDoc","asl":"ASL","assembly_x86":"Assembly
x86","autohotkey":"AutoHotKey","apex":"Apex","batchfile":"BatchFile","bro":"Bro","c_cpp":"C
and
C++","c9search":"C9Search","cirru":"Cirru","clojure":"Clojure","cobol":"Cobol","coffee":"CoffeeScript","coldfusion":"ColdFusion","csharp":"C#","csound_document":"Csound
Document","csound_orchestra":"Csound","csound_score":"Csound
Score","css":"CSS","curly":"Curly","d":"D","dart":"Dart","diff":"Diff","dockerfile":"Dockerfile","dot":"Dot","drools":"Drools","edifact":"Edifact","eiffel":"Eiffel","ejs":"EJS","elixir":"Elixir","elm":"Elm","erlang":"Erlang","forth":"Forth","fortran":"Fortran","fsharp":"FSharp","fsl":"FSL","ftl":"FreeMarker","gcode":"Gcode","gherkin":"Gherkin","gitignore":"Gitignore","glsl":"Glsl","gobstones":"Gobstones","golang":"Go","graphqlschema":"GraphQLSchema","groovy":"Groovy","haml":"HAML","handlebars":"Handlebars","haskell":"Haskell","haskell_cabal":"Haskell
Cabal","haxe":"haXe","hjson":"Hjson","html":"HTML","html_elixir":"HTML
(Elixir)","html_ruby":"HTML
(Ruby)","ini":"INI","io":"Io","jack":"Jack","jade":"Jade","java":"Java","json":"JSON","jsoniq":"JSONiq","jsp":"JSP","jssm":"JSSM","jsx":"JSX","julia":"Julia","kotlin":"Kotlin","latex":"LaTeX","less":"LESS","liquid":"Liquid","lisp":"Lisp","livescript":"LiveScript","logiql":"LogiQL","lsl":"LSL","lua":"Lua","luapage":"LuaPage","lucene":"Lucene","makefile":"Makefile","markdown":"Markdown","mask":"Mask","matlab":"MATLAB","maze":"Maze","mel":"MEL","mixal":"MIXAL","mushcode":"MUSHCode","mysql":"MySQL","nix":"Nix","nsis":"NSIS","objectivec":"Objective-C","ocaml":"OCaml","pascal":"Pascal","perl":"Perl","perl6":"Perl
6","pgsql":"pgSQL","php_laravel_blade":"PHP
(Blade
Template)","php":"PHP","puppet":"Puppet","pig":"Pig","powershell":"Powershell","praat":"Praat","prolog":"Prolog","properties":"Properties","protobuf":"Protobuf","python":"Python","r":"R","razor":"Razor","rdoc":"RDoc","red":"Red","rhtml":"RHTML","rst":"RST","ruby":"Ruby","rust":"Rust","sass":"SASS","scad":"SCAD","scala":"Scala","scheme":"Scheme","scss":"SCSS","sh":"SH","sjs":"SJS","slim":"Slim","smarty":"Smarty","snippets":"snippets","soy_template":"Soy
Template","space":"Space","sql":"SQL","sqlserver":"SQLServer","stylus":"Stylus","svg":"SVG","swift":"Swift","tcl":"Tcl","terraform":"Terraform","tex":"Tex","text":"Text","textile":"Textile","toml":"Toml","tsx":"TSX","twig":"Twig","typescript":"Typescript","vala":"Vala","vbscript":"VBScript","velocity":"Velocity","verilog":"Verilog","vhdl":"VHDL","visualforce":"Visualforce","wollok":"Wollok","xml":"XML","xquery":"XQuery","yaml":"YAML","django":"Django"},"fontSize":{8:8,10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,20:20,22:22,24:24,26:26,30:30}};
            if(_data && _data.aceMode) {
$modeEl.html(optionNode("ace/mode/", _data.aceMode)); }
            if(_data && _data.aceTheme) { var lightTheme =
optionNode("ace/theme/", _data.aceTheme.bright), darkTheme =
optionNode("ace/theme/", _data.aceTheme.dark);
$themeEl.html("<optgroup
label=\"Bright\">"+lightTheme+"</optgroup><optgroup
label=\"Dark\">"+darkTheme+"</optgroup>");}
            if(_data && _data.fontSize) {
$fontSizeEl.html(optionNode("", _data.fontSize)); }
            $modeEl.val( editor.getSession().$modeId );
            $themeEl.val( editor.getTheme() );
            $(function() { $fontSizeEl.val(12).change(); }); //set default
font size in drop down
        }

        $(function(){
            renderThemeMode();
            $(".js-ace-toolbar").on("click",
'button', function(e){
                e.preventDefault();
                let cmdValue = $(this).attr("data-cmd"),
editorOption = $(this).attr("data-option");
                if(cmdValue && cmdValue != "none") {
                    ace_commend(cmdValue);
                } else if(editorOption) {
                    if(editorOption == "fullscreen") {
                        (void
0!==document.fullScreenElement&&null===document.fullScreenElement||void
0!==document.msFullscreenElement&&null===document.msFullscreenElement||void
0!==document.mozFullScreen&&!document.mozFullScreen||void
0!==document.webkitIsFullScreen&&!document.webkitIsFullScreen)
                       
&&(editor.container.requestFullScreen?editor.container.requestFullScreen():editor.container.mozRequestFullScreen?editor.container.mozRequestFullScreen():editor.container.webkitRequestFullScreen?editor.container.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT):editor.container.msRequestFullscreen&&editor.container.msRequestFullscreen());
                    } else if(editorOption == "wrap") {
                        let wrapStatus =
(editor.getSession().getUseWrapMode()) ? false : true;
                        editor.getSession().setUseWrapMode(wrapStatus);
                    }
                }
            });
            $("select#js-ace-mode, select#js-ace-theme,
select#js-ace-fontSize").on("change", function(e){
                e.preventDefault();
                let selectedValue = $(this).val(), selectionType =
$(this).attr("data-type");
                if(selectedValue && selectionType ==
"mode") {
                    editor.getSession().setMode(selectedValue);
                } else if(selectedValue && selectionType ==
"theme") {
                    editor.setTheme(selectedValue);
                }else if(selectedValue && selectionType ==
"fontSize") {
                    editor.setFontSize(parseInt(selectedValue));
                }
            });
        });
    </script>
<?php endif; ?>
<div id="snackbar"></div>
</body>
</html>
<?php
}

/**
 * Language Translation System
 * @param string $txt
 * @return string
 */
function lng($txt) {
    global $lang;

    // English Language
    $tr['en']['AppName']        = 'Tiny File
Manager';      $tr['en']['AppTitle']           =
'File Manager';
    $tr['en']['Login']          = 'Sign in'; 
              $tr['en']['Username']           =
'Username';
    $tr['en']['Password']       = 'Password';
              $tr['en']['Logout']             =
'Sign Out';
    $tr['en']['Move']           = 'Move';    
              $tr['en']['Copy']               =
'Copy';
    $tr['en']['Save']           = 'Save';    
              $tr['en']['SelectAll']          =
'Select all';
    $tr['en']['UnSelectAll']    = 'Unselect
all';           $tr['en']['File']               =
'File';
    $tr['en']['Back']           = 'Back';    
              $tr['en']['Size']               =
'Size';
    $tr['en']['Perms']          = 'Perms';   
              $tr['en']['Modified']           =
'Modified';
    $tr['en']['Owner']          = 'Owner';   
              $tr['en']['Search']             =
'Search';
    $tr['en']['NewItem']        = 'New Item';
              $tr['en']['Folder']             =
'Folder';
    $tr['en']['Delete']         = 'Delete';  
              $tr['en']['Rename']             =
'Rename';
    $tr['en']['CopyTo']         = 'Copy to'; 
              $tr['en']['DirectLink']         =
'Direct link';
    $tr['en']['UploadingFiles'] = 'Upload
Files';           $tr['en']['ChangePermissions'] 
= 'Change Permissions';
    $tr['en']['Copying']        = 'Copying'; 
              $tr['en']['CreateNewItem']      =
'Create New Item';
    $tr['en']['Name']           = 'Name';    
              $tr['en']['AdvancedEditor']     =
'Advanced Editor';
    $tr['en']['Actions']        = 'Actions'; 
              $tr['en']['Folder is empty']    =
'Folder is empty';
    $tr['en']['Upload']         = 'Upload';  
              $tr['en']['Cancel']             =
'Cancel';
    $tr['en']['InvertSelection']= 'Invert
Selection';       $tr['en']['DestinationFolder'] 
= 'Destination Folder';
    $tr['en']['ItemType']       = 'Item
Type';              $tr['en']['ItemName']         
 = 'Item Name';
    $tr['en']['CreateNow']      = 'Create
Now';             $tr['en']['Download']          
= 'Download';
    $tr['en']['Open']           = 'Open';    
              $tr['en']['UnZip']              =
'UnZip';
    $tr['en']['UnZipToFolder']  = 'UnZip to
folder';        $tr['en']['Edit']               =
'Edit';
    $tr['en']['NormalEditor']   = 'Normal
Editor';          $tr['en']['BackUp']            
= 'Back Up';
    $tr['en']['SourceFolder']   = 'Source
Folder';          $tr['en']['Files']             
= 'Files';
    $tr['en']['Move']           = 'Move';    
              $tr['en']['Change']             =
'Change';
    $tr['en']['Settings']       = 'Settings';
              $tr['en']['Language']           =
'Language';        
    $tr['en']['ErrorReporting'] = 'Error
Reporting';        $tr['en']['ShowHiddenFiles']   
= 'Show Hidden Files';
    $tr['en']['Help']           = 'Help';    
              $tr['en']['Created']            =
'Created';
    $tr['en']['Help Documents'] = 'Help
Documents';         $tr['en']['Report Issue']     
 = 'Report Issue';
    $tr['en']['Generate']       = 'Generate';
              $tr['en']['FullSize']           =
'Full Size';              
    $tr['en']['HideColumns']    = 'Hide
Perms/Owner columns';$tr['en']['You are logged
in'] = 'You are logged in';
    $tr['en']['Nothing selected']   = 'Nothing
selected';   $tr['en']['Paths must be not equal'] 
  = 'Paths must be not equal';
    $tr['en']['Renamed from']       = 'Renamed
from';       $tr['en']['Archive not unpacked']    
  = 'Archive not unpacked';
    $tr['en']['Deleted']            =
'Deleted';            $tr['en']['Archive not
created']        = 'Archive not created';
    $tr['en']['Copied from']        = 'Copied
from';        $tr['en']['Permissions changed']    
   = 'Permissions changed';
    $tr['en']['to']                 = 'to';  
              $tr['en']['Saved Successfully']         =
'Saved Successfully';
    $tr['en']['not found!']         = 'not
found!';         $tr['en']['File Saved
Successfully']    = 'File Saved Successfully';
    $tr['en']['Archive']            =
'Archive';            $tr['en']['Permissions not
changed']    = 'Permissions not changed';
    $tr['en']['Select folder']      = 'Select
folder';      $tr['en']['Source path not defined']
   = 'Source path not defined';
    $tr['en']['already exists']     = 'already
exists';     $tr['en']['Error while moving from'] 
  = 'Error while moving from';
    $tr['en']['Create archive?']    = 'Create
archive?';    $tr['en']['Invalid file or folder
name']    = 'Invalid file or folder name';
    $tr['en']['Archive unpacked']   = 'Archive
unpacked';   $tr['en']['File extension is not
allowed']  = 'File extension is not allowed';
    $tr['en']['Root path']          = 'Root
path';          $tr['en']['Error while renaming
from']  = 'Error while renaming from';
    $tr['en']['File not found']     = 'File not
found';     $tr['en']['Error while deleting
items'] = 'Error while deleting items';
    $tr['en']['Moved from']         = 'Moved
from';         $tr['en']['Generate new password
hash'] = 'Generate new password hash';
    $tr['en']['Login failed. Invalid username or
password'] = 'Login failed. Invalid username or password';
    $tr['en']['password_hash not supported, Upgrade PHP
version'] = 'password_hash not supported, Upgrade PHP
version';
    $tr['en']['Advanced Search']    = 'Advanced
Search';    $tr['en']['Error while copying from'] 
  = 'Error while copying from';
    $tr['en']['Invalid characters in file name']       
        = 'Invalid characters in file name';
    $tr['en']['FILE EXTENSION HAS NOT SUPPORTED']      
        = 'FILE EXTENSION HAS NOT SUPPORTED';
    $tr['en']['Selected files and folder deleted']     
        = 'Selected files and folder deleted';
    $tr['en']['Error while fetching archive info']     
        = 'Error while fetching archive info';
    $tr['en']['Delete selected files and folders?']    
        = 'Delete selected files and folders?';
    $tr['en']['Search file in folder and
subfolders...']        = 'Search file in folder and
subfolders...';
    $tr['en']['Access denied. IP restriction
applicable']       = 'Access denied. IP restriction
applicable';
    $tr['en']['Invalid characters in file or folder
name']      = 'Invalid characters in file or folder name';
    $tr['en']['Operations with archives are not
available']     = 'Operations with archives are not
available';
    $tr['en']['File or folder with this path already
exists']   = 'File or folder with this path already
exists';
    $tr['en']['Are you sure want to rename?']          
        = 'Are you sure want to rename?';
    $tr['en']['Are you sure want to']                  
        = 'Are you sure want to';

    $i18n = fm_get_translations($tr);
    $tr = $i18n ? $i18n : $tr;

    if (!strlen($lang)) $lang = 'en';
    if (isset($tr[$lang][$txt])) return fm_enc($tr[$lang][$txt]);
    else if (isset($tr['en'][$txt])) return
fm_enc($tr['en'][$txt]);
    else return "$txt";
}

?>PK��[��LL	error_lognu�[���[30-Sep-2024
11:37:50 Etc/UTC] PHP Warning: 
fopen(/home/lmsyaran/public_html/index.php): Failed to open stream:
Permission denied in
/home/lmsyaran/public_html/components/com_finder/temp.php on line 494
[30-Sep-2024 11:37:50 Etc/UTC] PHP Fatal error:  Uncaught TypeError:
fwrite(): Argument #1 ($stream) must be of type resource, bool given in
/home/lmsyaran/public_html/components/com_finder/temp.php:495
Stack trace:
#0 /home/lmsyaran/public_html/components/com_finder/temp.php(495): fwrite()
#1 {main}
  thrown in /home/lmsyaran/public_html/components/com_finder/temp.php on
line 495
PKo��[Y-�  tmpl/search/default.xmlnu�[���<?xml
version="1.0" encoding="UTF-8"?>
<metadata>
	<layout title="COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TITLE">
		<help
			key = "Menu_Item:_Search"
		/>
		<message>
			<![CDATA[COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TEXT]]>
		</message>
	</layout>

	<fields name="request"
addfieldprefix="Joomla\Component\Finder\Administrator\Field" >
		<fieldset name="request">
			<field
				name="q"
				type="text"
				label="COM_FINDER_PREFILL_SEARCH_LABEL"
			/>
			<field
				name="f"
				type="searchfilter"
				label="COM_FINDER_SELECT_FILTER_LABEL"
				default=""
			/>
		</fieldset>
	</fields>
	<fields name="params">
		<fieldset name="basic">
			<field
				name="show_date_filters"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="show_advanced"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_ADVANCED_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="expand_advanced"
				type="list"
				label="COM_FINDER_CONFIG_EXPAND_ADVANCED_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field type="spacer" />
			<field
				name="show_taxonomy"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_TAXONOMY_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="show_description"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_DESCRIPTION_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="description_length"
				type="number"
				label="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_LABEL"
				filter="integer"
				default=""
				useglobal="true"
			/>
			<field
				name="show_image"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_IMAGE_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="image_class"
				type="text"
				label="COM_FINDER_CONFIG_IMAGE_CLASS_LABEL"
				default=""
				useglobal="true"
				validate="CssIdentifier"
				showon="show_image!:0"
			/>
			<field
				name="link_image"
				type="list"
				label="COM_FINDER_CONFIG_LINKED_IMAGE_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				showon="show_image!:0"
			>
				<option value="0">JNO</option>
				<option value="1">JYES</option>
			</field>
			<field
				name="show_date"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_DATE_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field
				name="show_url"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_URL_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JSHOW</option>
				<option value="0">JHIDE</option>
			</field>
			<field type="spacer" />
		</fieldset>
		<fieldset name="advanced">
			<field
				name="show_pagination_limit"
				type="list"
				label="JGLOBAL_DISPLAY_SELECT_LABEL"
				validate="options"
				class="form-select-color-state"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
			<field
				name="show_pagination"
				type="list"
				label="JGLOBAL_PAGINATION_LABEL"
				validate="options"
				class="form-select-color-state"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
				<option value="2">JGLOBAL_AUTO</option>
			</field>
			<field
				name="show_pagination_results"
				type="list"
				label="JGLOBAL_PAGINATION_RESULTS_LABEL"
				validate="options"
				class="form-select-color-state"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
			<field
				name="list_limit"
				type="list"
				label="JGLOBAL_LIST_LIMIT"
				default="20"
				validate="options"
				>
				<option value="5">J5</option>
				<option value="10">J10</option>
				<option value="15">J15</option>
				<option value="20">J20</option>
				<option value="25">J25</option>
				<option value="30">J30</option>
				<option value="50">J50</option>
				<option value="100">J100</option>
				<option value="0">JALL</option>
			</field>
			<field
				name="allow_empty_query"
				type="list"
				label="COM_FINDER_ALLOW_EMPTY_QUERY_LABEL"
				description="COM_FINDER_ALLOW_EMPTY_QUERY_DESC"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="show_suggested_query"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="show_explained_query"
				type="list"
				label="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_LABEL"
				default=""
				useglobal="true"
				class="form-select-color-state"
				validate="options"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="sort_order"
				type="list"
				label="COM_FINDER_CONFIG_SORT_ORDER_LABEL"
				default=""
				useglobal="true"
				validate="options"
				>
				<option
value="relevance">COM_FINDER_CONFIG_SORT_OPTION_RELEVANCE</option>
				<option
value="title">COM_FINDER_CONFIG_SORT_OPTION_TITLE</option>
				<option
value="date">COM_FINDER_CONFIG_SORT_OPTION_START_DATE</option>
				<option
value="price">COM_FINDER_CONFIG_SORT_OPTION_LIST_PRICE</option>
				<option
value="sale_price">COM_FINDER_CONFIG_SORT_OPTION_SALES_PRICE</option>
			</field>
			<field
				name="show_sort_order"
				type="radio"
				label="COM_FINDER_CONFIG_SHOW_SORT_ORDER_LABEL"
				layout="joomla.form.field.radio.switcher"
				default="0"
				>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
			<field
				name="shown_sort_order"
				type="list"
				label="COM_FINDER_CONFIG_SORT_ORDER_FIELDS_LABEL"
				layout="joomla.form.field.list-fancy-select"
				multiple="true"
				validate="options"
				showon="show_sort_order:1"
				>
				<option
value="relevance">COM_FINDER_CONFIG_SORT_OPTION_RELEVANCE</option>
				<option
value="title">COM_FINDER_CONFIG_SORT_OPTION_TITLE</option>
				<option
value="date">COM_FINDER_CONFIG_SORT_OPTION_START_DATE</option>
				<option
value="price">COM_FINDER_CONFIG_SORT_OPTION_LIST_PRICE</option>
				<option
value="sale_price">COM_FINDER_CONFIG_SORT_OPTION_SALES_PRICE</option>
			</field>
			<field
				name="sort_direction"
				type="list"
				label="COM_FINDER_CONFIG_SORT_DIRECTION_LABEL"
				default=""
				useglobal="true"
				validate="options"
				>
				<option
value="desc">COM_FINDER_CONFIG_SORT_OPTION_DESCENDING</option>
				<option
value="asc">COM_FINDER_CONFIG_SORT_OPTION_ASCENDING</option>
			</field>
		</fieldset>
		<fieldset name="integration">
			<field
				name="show_feed_link"
				type="list"
				label="JGLOBAL_SHOW_FEED_LINK_LABEL"
				validate="options"
				class="form-select-color-state"
				>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
		</fieldset>
	</fields>
</metadata>
PKo��[��=���tmpl/search/default_result.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
use Joomla\Component\Finder\Administrator\Indexer\Helper;
use Joomla\Component\Finder\Administrator\Indexer\Taxonomy;
use Joomla\String\StringHelper;

$user             = $this->getCurrentUser();
$show_description = $this->params->get('show_description',
1);

if ($show_description) {
    // Calculate number of characters to display around the result
    $term_length = StringHelper::strlen($this->query->input);
    $desc_length = $this->params->get('description_length',
255);
    $pad_length  = $term_length < $desc_length ? (int)
floor(($desc_length - $term_length) / 2) : 0;

    // Make sure we highlight term both in introtext and fulltext
    $full_description = $this->result->description;
    if (!empty($this->result->summary) &&
!empty($this->result->body)) {
        $full_description = Helper::parse($this->result->summary .
$this->result->body);
    }

    // Find the position of the search term
    $pos = $term_length ?
StringHelper::strpos(StringHelper::strtolower($full_description),
StringHelper::strtolower($this->query->input)) : false;

    // Find a potential start point
    $start = ($pos && $pos > $pad_length) ? $pos - $pad_length :
0;

    // Find a space between $start and $pos, start right after it.
    $space = StringHelper::strpos($full_description, ' ', $start
> 0 ? $start - 1 : 0);
    $start = ($space && $space < $pos) ? $space + 1 : $start;

    $description = HTMLHelper::_('string.truncate',
StringHelper::substr($full_description, $start), $desc_length, true);
}

$showImage  = $this->params->get('show_image', 0);
$imageClass = $this->params->get('image_class',
'');
$extraAttr  = [];

if ($showImage && !empty($this->result->imageUrl) &&
$imageClass !== '') {
    $extraAttr['class'] = $imageClass;
}

$icon = '';
if (!empty($this->result->mime)) {
    $icon = '<span class="icon-file-' .
$this->result->mime . '"
aria-hidden="true"></span> ';
}


$show_url = '';
if ($this->params->get('show_url', 1)) {
    $show_url = '<cite
class="result__title-url">' . $this->baseUrl .
Route::_($this->result->cleanURL) . '</cite>';
}
?>
<li class="result__item">
    <?php if ($showImage &&
isset($this->result->imageUrl)) : ?>
        <figure class="<?php echo htmlspecialchars($imageClass,
ENT_COMPAT, 'UTF-8'); ?> result__image">
            <?php if ($this->params->get('link_image')
&& $this->result->route) : ?>
                <a href="<?php echo
Route::_($this->result->route); ?>">
                    <?php echo HTMLHelper::_('image',
$this->result->imageUrl, $this->result->imageAlt, $extraAttr,
false, -1); ?>
                </a>
            <?php else : ?>
                <?php echo HTMLHelper::_('image',
$this->result->imageUrl, $this->result->imageAlt, $extraAttr,
false, -1); ?>
            <?php endif; ?>
        </figure>
    <?php endif; ?>
    <p class="result__title">
        <?php if ($this->result->route) : ?>
            <?php echo HTMLHelper::link(
                Route::_($this->result->route),
                '<span
class="result__title-text">' . $icon .
$this->result->title . '</span>' . $show_url,
                [
                            'class' =>
'result__title-link'
                    ]
            ); ?>
        <?php else : ?>
            <?php echo $this->result->title; ?>
        <?php endif; ?>
    </p>
    <?php if ($show_description && $description !==
'') : ?>
        <p class="result__description">
            <?php if ($this->result->start_date &&
$this->params->get('show_date', 1)) : ?>
                <time class="result__date"
datetime="<?php echo HTMLHelper::_('date',
$this->result->start_date, 'c'); ?>">
                    <?php echo HTMLHelper::_('date',
$this->result->start_date, Text::_('DATE_FORMAT_LC3'));
?>
                </time>
            <?php endif; ?>
            <?php echo $description; ?>
        </p>
    <?php endif; ?>
    <?php $taxonomies = $this->result->getTaxonomy(); ?>
    <?php if (count($taxonomies) &&
$this->params->get('show_taxonomy', 1)) : ?>
        <ul class="result__taxonomy">
            <?php foreach ($taxonomies as $type => $taxonomy) : ?>
                <?php if ($type == 'Language' &&
(!Multilanguage::isEnabled() || (isset($taxonomy[0]) &&
$taxonomy[0]->title == '*'))) : ?>
                    <?php continue; ?>
                <?php endif; ?>
                <?php $branch = Taxonomy::getBranch($type); ?>
                <?php if ($branch->state == 1 &&
in_array($branch->access, $user->getAuthorisedViewLevels())) : ?>
                    <?php $taxonomy_text = []; ?>
                    <?php foreach ($taxonomy as $node) : ?>
                        <?php if ($node->state == 1 &&
in_array($node->access, $user->getAuthorisedViewLevels())) : ?>
                            <?php $taxonomy_text[] = $node->title;
?>
                        <?php endif; ?>
                    <?php endforeach; ?>
                    <?php if (count($taxonomy_text)) : ?>
                        <li class="result__taxonomy-item
result__taxonomy--<?php echo $type; ?>">
                            <span><?php echo
Text::_(LanguageHelper::branchSingular($type)); ?>:</span>
                            <?php echo
Text::_(LanguageHelper::branchSingular(implode(',',
$taxonomy_text))); ?>
                        </li>
                    <?php endif; ?>
                <?php endif; ?>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>
</li>
PKo��[��NL==tmpl/search/default_results.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;

?>
<?php // Display the suggested search if it is different from the
current search. ?>
<?php if (($this->suggested &&
$this->params->get('show_suggested_query', 1)) ||
($this->explained &&
$this->params->get('show_explained_query', 1))) : ?>
    <div id="search-query-explained"
class="com-finder__explained">
        <?php // Display the suggested search query. ?>
        <?php if ($this->suggested &&
$this->params->get('show_suggested_query', 1)) : ?>
            <?php // Replace the base query string with the suggested
query string. ?>
            <?php $uri = Uri::getInstance($this->query->toUri());
?>
            <?php $uri->setVar('q', $this->suggested);
?>
            <?php // Compile the suggested query link. ?>
            <?php $linkUrl =
Route::_($uri->toString(['path', 'query'])); ?>
            <?php $link = '<a href="' . $linkUrl .
'">' . $this->escape($this->suggested) .
'</a>'; ?>
            <?php echo
Text::sprintf('COM_FINDER_SEARCH_SIMILAR', $link); ?>
        <?php elseif ($this->explained &&
$this->params->get('show_explained_query', 1)) : ?>
            <?php // Display the explained search query. ?>
            <p role="alert">
                <?php echo
Text::plural('COM_FINDER_QUERY_RESULTS', $this->total,
$this->explained); ?>
            </p>
        <?php endif; ?>
    </div>
<?php endif; ?>
<?php // Display the 'no results' message and exit the
template. ?>
<?php if (($this->total === 0) || ($this->total === null)) : ?>
    <div id="search-result-empty"
class="com-finder__empty">
        <h2><?php echo
Text::_('COM_FINDER_SEARCH_NO_RESULTS_HEADING'); ?></h2>
        <?php $multilang =
Factory::getApplication()->getLanguageFilter() ? '_MULTILANG'
: ''; ?>
        <p><?php echo
Text::sprintf('COM_FINDER_SEARCH_NO_RESULTS_BODY' . $multilang,
$this->escape($this->query->input)); ?></p>
    </div>
    <?php // Exit this template. ?>
    <?php return; ?>
<?php endif; ?>
<?php // Display the 'Sort By' drop-down. ?>
<?php if ($this->params->get('show_sort_order', 0)
&& !empty($this->sortOrderFields) &&
!empty($this->results)) : ?>
    <div id="search-sorting"
class="com-finder__sorting">
        <?php echo $this->loadTemplate('sorting'); ?>
    </div>
<?php endif; ?>
<?php // Activate the highlighter if enabled. ?>
<?php if (!empty($this->query->highlight) &&
$this->params->get('highlight_terms', 1)) : ?>
    <?php
        // Allow a maximum of 10 tokens to be highlighted. Otherwise the
URL can get too long.
       
$this->document->getWebAssetManager()->useScript('highlight');
        $this->document->addScriptOptions(
            'highlight',
            [[
                    'class'      => 'js-highlight',
                    'highLight'  =>
array_slice($this->query->highlight, 0, 10),
            ]]
        );
    ?>
<?php endif; ?>
<?php // Display a list of results ?>
<ul id="search-result-list" class="js-highlight
com-finder__results-list" start="<?php echo (int)
$this->pagination->limitstart + 1; ?>">
    <?php $this->baseUrl =
Uri::getInstance()->toString(['scheme', 'host',
'port']); ?>
    <?php foreach ($this->results as $i => $result) : ?>
        <?php $this->result = &$result; ?>
        <?php $this->result->counter = $i + 1; ?>
        <?php $layout =
$this->getLayoutFile($this->result->layout); ?>
        <?php echo $this->loadTemplate($layout); ?>
    <?php endforeach; ?>
</ul>
<?php // Display the pagination ?>
<div class="com-finder__navigation search-pagination">
    <?php if ($this->params->get('show_pagination', 1)
> 0) : ?>
    <div class="com-finder__pagination w-100">
        <?php echo $this->pagination->getPagesLinks(); ?>
    </div>
    <?php endif; ?>
    <?php if
($this->params->get('show_pagination_results', 1) > 0) :
?>
        <div class="com-finder__counter
search-pages-counter">
            <?php // Prepare the pagination string.  Results X - Y of Z
?>
            <?php $start = (int) $this->pagination->limitstart +
1; ?>
            <?php $total = (int) $this->pagination->total; ?>
            <?php $limit = (int) $this->pagination->limit *
$this->pagination->pagesCurrent; ?>
            <?php $limit = (int) min($limit, $total); ?>
            <?php echo
Text::sprintf('COM_FINDER_SEARCH_RESULTS_OF', $start, $limit,
$total); ?>
        </div>
    <?php endif; ?>
</div>
PKo��[_���bbtmpl/search/default_sorting.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2021 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;

?>
<?php HTMLHelper::_('bootstrap.dropdown',
'.dropdown-toggle'); ?>
<div class="sorting">
    <label id="sorting_label"
for="sorting_btn"><?php echo
Text::_('COM_FINDER_SORT_BY'); ?></label>
    <div class="sorting__select btn-group">
        <?php foreach ($this->sortOrderFields as $sortOrderField) :
?>
            <?php if ($sortOrderField->active) : ?>
                <button id="sorting_btn" class="btn
btn-secondary dropdown-toggle" type="button"
                        data-bs-toggle="dropdown"
                        aria-haspopup="listbox"
                        aria-expanded="false"
aria-controls="finder_sorting_list">
                    <?php echo
$this->escape($sortOrderField->label); ?>
                </button>
                <?php
                break;
            endif; ?>
        <?php endforeach; ?>

        <ul id="finder_sorting_list" class="sorting__list
block dropdown-menu" role="listbox"
aria-labelledby="finder_sorting_desc">
            <?php foreach ($this->sortOrderFields as $sortOrderField)
: ?>
            <li  class="sorting__list-li <?php echo
$sortOrderField->active ? 'sorting__list-li-active' :
''; ?>">
                <a class="dropdown-item"
role="option" href="<?php echo
Route::_($sortOrderField->url);?>" <?php echo
$sortOrderField->active ? 'aria-current="true"' :
''; ?>>
                    <?php echo
$this->escape($sortOrderField->label); ?>
                </a>
            </li>
            <?php endforeach; ?>
        </ul>
    </div>
    <div class="clearfix"></div>
</div>
PKo��[�fvvtmpl/search/default_form.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;

/*
* This segment of code sets up the autocompleter.
*/
if ($this->params->get('show_autosuggest', 1)) {
   
$this->document->getWebAssetManager()->usePreset('awesomplete');
    $this->document->addScriptOptions('finder-search',
['url' =>
Route::_('index.php?option=com_finder&task=suggestions.suggest&format=json&tmpl=component',
false)]);

    Text::script('JLIB_JS_AJAX_ERROR_OTHER');
    Text::script('JLIB_JS_AJAX_ERROR_PARSE');
}

?>

<form action="<?php echo Route::_($this->query->toUri());
?>" method="get"
class="js-finder-searchform">
    <?php echo $this->getFields(); ?>
    <fieldset class="com-finder__search word mb-3">
        <legend class="com-finder__search-legend
visually-hidden">
            <?php echo
Text::_('COM_FINDER_SEARCH_FORM_LEGEND'); ?>
        </legend>
        <div class="form-inline">
            <label for="q" class="me-2">
                <?php echo Text::_('COM_FINDER_SEARCH_TERMS');
?>
            </label>
            <div class="input-group">
                <input type="text" name="q"
id="q" class="js-finder-search-query form-control"
value="<?php echo $this->escape($this->query->input);
?>">
                <button type="submit" class="btn
btn-primary">
                    <span class="icon-search icon-white"
aria-hidden="true"></span>
                    <?php echo
Text::_('JSEARCH_FILTER_SUBMIT'); ?>
                </button>
                <?php if
($this->params->get('show_advanced', 1)) : ?>
                    <?php HTMLHelper::_('bootstrap.collapse');
?>
                    <button class="btn btn-secondary"
type="button" data-bs-toggle="collapse"
data-bs-target="#advancedSearch" aria-expanded="<?php
echo ($this->params->get('expand_advanced', 0) ?
'true' : 'false'); ?>">
                        <span class="icon-search-plus"
aria-hidden="true"></span>
                        <?php echo
Text::_('COM_FINDER_ADVANCED_SEARCH_TOGGLE');
?></button>
                <?php endif; ?>
            </div>
        </div>
    </fieldset>

    <?php if ($this->params->get('show_advanced', 1)) :
?>
        <fieldset id="advancedSearch"
class="com-finder__advanced js-finder-advanced collapse<?php if
($this->params->get('expand_advanced', 0)) {
            echo ' show';
                                                                           
                 } ?>">
            <legend class="com-finder__search-advanced
visually-hidden">
                <?php echo
Text::_('COM_FINDER_SEARCH_ADVANCED_LEGEND'); ?>
            </legend>
            <?php if
($this->params->get('show_advanced_tips', 1)) : ?>
                <div class="com-finder__tips card
card-outline-secondary mb-3">
                    <div class="card-body">
                        <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_INTRO'); ?>
                        <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_AND'); ?>
                        <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_NOT'); ?>
                        <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_OR'); ?>
                        <?php if
($this->params->get('tuplecount', 1) > 1) : ?>
                            <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_PHRASE'); ?>
                        <?php endif; ?>
                        <?php echo
Text::_('COM_FINDER_ADVANCED_TIPS_OUTRO'); ?>
                    </div>
                </div>
            <?php endif; ?>
            <div id="finder-filter-window"
class="com-finder__filter">
                <?php echo HTMLHelper::_('filter.select',
$this->query, $this->params); ?>
            </div>
        </fieldset>
    <?php endif; ?>
</form>
PKp��[ŃW��tmpl/search/default.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

$this->document->getWebAssetManager()
    ->useStyle('com_finder.finder')
    ->useScript('com_finder.finder');

?>
<div class="com-finder finder">
    <?php if ($this->params->get('show_page_heading')) :
?>
        <h1>
            <?php if
($this->escape($this->params->get('page_heading'))) :
?>
                <?php echo
$this->escape($this->params->get('page_heading')); ?>
            <?php else : ?>
                <?php echo
$this->escape($this->params->get('page_title')); ?>
            <?php endif; ?>
        </h1>
    <?php endif; ?>
    <div id="search-form"
class="com-finder__form">
        <?php echo $this->loadTemplate('form'); ?>
    </div>
    <?php // Load the search results layout if we are performing a
search. ?>
    <?php if ($this->query->search === true) : ?>
        <div id="search-results"
class="com-finder__results">
            <?php echo $this->loadTemplate('results');
?>
        </div>
    <?php endif; ?>
</div>
PKp��[���]mmsrc/Service/Router.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Service;

use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Menu\AbstractMenu;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Routing class from com_finder
 *
 * @since  3.3
 */
class Router extends RouterView
{
    /**
     * Finder Component router constructor
     *
     * @param   SiteApplication  $app   The application object
     * @param   AbstractMenu     $menu  The menu object to work with
     */
    public function __construct(SiteApplication $app, AbstractMenu $menu)
    {
        $search = new RouterViewConfiguration('search');
        $this->registerView($search);

        parent::__construct($app, $menu);

        $this->attachRule(new MenuRules($this));
        $this->attachRule(new StandardRules($this));
        $this->attachRule(new NomenuRules($this));
    }
}
PKp��[%��Isrc/Helper/RouteHelper.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2020 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Helper;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Finder route helper class.
 *
 * @since  2.5
 */
class RouteHelper
{
    /**
     * Method to get the route for a search page.
     *
     * @param   integer  $f  The search filter id. [optional]
     * @param   string   $q  The search query string. [optional]
     *
     * @return  string  The search route.
     *
     * @since   2.5
     */
    public static function getSearchRoute($f = null, $q = null)
    {
        // Get the menu item id.
        $query = ['view' => 'search', 'q'
=> $q, 'f' => $f];
        $item  = self::getItemid($query);

        // Get the base route.
        $uri = clone
Uri::getInstance('index.php?option=com_finder&view=search');

        // Add the pre-defined search filter if present.
        if ($f !== null) {
            $uri->setVar('f', $f);
        }

        // Add the search query string if present.
        if ($q !== null) {
            $uri->setVar('q', $q);
        }

        // Add the menu item id if present.
        if ($item !== null) {
            $uri->setVar('Itemid', $item);
        }

        return $uri->toString(['path', 'query']);
    }

    /**
     * Method to get the route for an advanced search page.
     *
     * @param   integer  $f  The search filter id. [optional]
     * @param   string   $q  The search query string. [optional]
     *
     * @return  string  The advanced search route.
     *
     * @since   2.5
     */
    public static function getAdvancedRoute($f = null, $q = null)
    {
        // Get the menu item id.
        $query = ['view' => 'advanced',
'q' => $q, 'f' => $f];
        $item  = self::getItemid($query);

        // Get the base route.
        $uri = clone
Uri::getInstance('index.php?option=com_finder&view=advanced');

        // Add the pre-defined search filter if present.
        if ($q !== null) {
            $uri->setVar('f', $f);
        }

        // Add the search query string if present.
        if ($q !== null) {
            $uri->setVar('q', $q);
        }

        // Add the menu item id if present.
        if ($item !== null) {
            $uri->setVar('Itemid', $item);
        }

        return $uri->toString(['path', 'query']);
    }

    /**
     * Method to get the most appropriate menu item for the route based on
the
     * supplied query needles.
     *
     * @param   array  $query  An array of URL parameters.
     *
     * @return  mixed  An integer on success, null otherwise.
     *
     * @since   2.5
     */
    public static function getItemid($query)
    {
        static $items, $active;

        // Get the menu items for com_finder.
        if (!$items || !$active) {
            $app    = Factory::getApplication();
            $com    =
ComponentHelper::getComponent('com_finder');
            $menu   = $app->getMenu();
            $active = $menu->getActive();
            $items  = $menu->getItems('component_id',
$com->id);
            $items  = \is_array($items) ? $items : [];
        }

        // Try to match the active view and filter.
        if ($active && @$active->query['view'] ==
@$query['view'] && @$active->query['f'] ==
@$query['f']) {
            return $active->id;
        }

        // Try to match the view, query, and filter.
        foreach ($items as $item) {
            if (@$item->query['view'] ==
@$query['view'] && @$item->query['q'] ==
@$query['q'] && @$item->query['f'] ==
@$query['f']) {
                return $item->id;
            }
        }

        // Try to match the view and filter.
        foreach ($items as $item) {
            if (@$item->query['view'] ==
@$query['view'] && @$item->query['f'] ==
@$query['f']) {
                return $item->id;
            }
        }

        // Try to match the view.
        foreach ($items as $item) {
            if (@$item->query['view'] ==
@$query['view']) {
                return $item->id;
            }
        }

        return null;
    }
}
PKp��[m�&@��src/Helper/FinderHelper.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Helper;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\Component\Finder\Administrator\Indexer\Query;
use Joomla\Database\ParameterType;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Helper class for Joomla! Finder components
 *
 * @since  4.0.0
 */
class FinderHelper
{
    /**
     * Method to log searches to the database
     *
     * @param   Query    $searchquery  The search query
     * @param   integer  $resultCount  The number of results for this
search
     *
     * @return  void
     *
     * @since   4.0.0
     */
    public static function logSearch(Query $searchquery, $resultCount = 0)
    {
        if
(!ComponentHelper::getParams('com_finder')->get('gather_search_statistics',
0)) {
            return;
        }

        if (trim($searchquery->input) == '' &&
!$searchquery->empty) {
            return;
        }

        // Initialise our variables
        $db    = Factory::getDbo();
        $query = $db->getQuery(true);

        // Sanitise the term for the database
        $temp              = new \stdClass();
        $temp->input       = trim(strtolower((string)
$searchquery->input));
        $entry             = new \stdClass();
        $entry->searchterm = $temp->input;
        $entry->query      = serialize($temp);
        $entry->md5sum     = md5($entry->query);
        $entry->hits       = 1;
        $entry->results    = $resultCount;

        // Query the table to determine if the term has been searched
previously
        $query->select($db->quoteName('hits'))
            ->from($db->quoteName('#__finder_logging'))
            ->where($db->quoteName('md5sum') . ' =
' . $db->quote($entry->md5sum));
        $db->setQuery($query);
        $hits = (int) $db->loadResult();

        // Reset the $query object
        $query->clear();

        // Update the table based on the results
        if ($hits) {
           
$query->update($db->quoteName('#__finder_logging'))
                ->set('hits = (hits + 1)')
                ->where($db->quoteName('md5sum') . ' =
' . $db->quote($entry->md5sum));
            $db->setQuery($query);
            $db->execute();
        } else {
           
$query->insert($db->quoteName('#__finder_logging'))
                ->columns(
                    [
                        $db->quoteName('searchterm'),
                        $db->quoteName('query'),
                        $db->quoteName('md5sum'),
                        $db->quoteName('hits'),
                        $db->quoteName('results'),
                    ]
                )
                ->values('?, ?, ?, ?, ?')
                ->bind(1, $entry->searchterm)
                ->bind(2, $entry->query, ParameterType::LARGE_OBJECT)
                ->bind(3, $entry->md5sum)
                ->bind(4, $entry->hits, ParameterType::INTEGER)
                ->bind(5, $entry->results, ParameterType::INTEGER);
            $db->setQuery($query);
            $db->execute();
        }
    }
}
PKp��[3�����src/Model/SuggestionsModel.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Model;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Component\Finder\Administrator\Indexer\Helper;
use Joomla\Database\DatabaseQuery;
use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Suggestions model class for the Finder package.
 *
 * @since  2.5
 */
class SuggestionsModel extends ListModel
{
    /**
     * Context string for the model type.
     *
     * @var    string
     * @since  2.5
     */
    protected $context = 'com_finder.suggestions';

    /**
     * Method to get an array of data items.
     *
     * @return  array  An array of data items.
     *
     * @since   2.5
     */
    public function getItems()
    {
        // Get the items.
        $items = parent::getItems();

        // Convert them to a simple array.
        foreach ($items as $k => $v) {
            $items[$k] = $v->term;
        }

        return $items;
    }

    /**
     * Method to build a database query to load the list data.
     *
     * @return  DatabaseQuery  A database query
     *
     * @since   2.5
     */
    protected function getListQuery()
    {
        $user   = $this->getCurrentUser();
        $groups =
ArrayHelper::toInteger($user->getAuthorisedViewLevels());
        $lang   =
Helper::getPrimaryLanguage($this->getState('language'));

        // Create a new query object.
        $db          = $this->getDatabase();
        $termIdQuery = $db->getQuery(true);
        $termQuery   = $db->getQuery(true);

        // Limit term count to a reasonable number of results to reduce
main query join size
        $termIdQuery->select('ti.term_id')
            ->from($db->quoteName('#__finder_terms',
'ti'))
            ->where('ti.term LIKE ' .
$db->quote($db->escape(StringHelper::strtolower($this->getState('input')),
true) . '%', false))
            ->where('ti.common = 0')
            ->where('ti.language IN (' . $db->quote($lang)
. ', ' . $db->quote('*') . ')')
            ->order('ti.links DESC')
            ->order('ti.weight DESC');

        $termIds = $db->setQuery($termIdQuery, 0, 100)->loadColumn();

        // Early return on term mismatch
        if (!\count($termIds)) {
            return $termIdQuery;
        }

        // Select required fields
        $termQuery->select('DISTINCT(t.term)')
            ->from($db->quoteName('#__finder_terms',
't'))
            ->whereIn('t.term_id', $termIds)
            ->order('t.links DESC')
            ->order('t.weight DESC');

        // Join mapping table for term <-> link relation
        $mappingTable =
$db->quoteName('#__finder_links_terms', 'tm');
        $termQuery->join('INNER', $mappingTable . ' ON
tm.term_id = t.term_id');

        // Join links table
        $termQuery->join('INNER',
$db->quoteName('#__finder_links', 'l') . ' ON
(tm.link_id = l.link_id)')
            ->where('l.access IN (' . implode(',',
$groups) . ')')
            ->where('l.state = 1')
            ->where('l.published = 1');

        return $termQuery;
    }

    /**
     * Method to get a store id based on model the configuration state.
     *
     * This is necessary because the model is used by the component and
     * different modules that might need different sets of data or
different
     * ordering requirements.
     *
     * @param   string  $id  An identifier string to generate the store id.
[optional]
     *
     * @return  string  A store id.
     *
     * @since   2.5
     */
    protected function getStoreId($id = '')
    {
        // Add the search query state.
        $id .= ':' . $this->getState('input');
        $id .= ':' . $this->getState('language');

        // Add the list state.
        $id .= ':' . $this->getState('list.start');
        $id .= ':' . $this->getState('list.limit');

        return parent::getStoreId($id);
    }

    /**
     * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
     *
     * @param   string  $ordering   An optional ordering field.
     * @param   string  $direction  An optional direction (asc|desc).
     *
     * @return  void
     *
     * @since   2.5
     */
    protected function populateState($ordering = null, $direction = null)
    {
        // Get the configuration options.
        $app    = Factory::getApplication();
        $input  = $app->getInput();
        $params = ComponentHelper::getParams('com_finder');
        $user   = $this->getCurrentUser();

        // Get the query input.
        $this->setState('input',
$input->request->get('q', '',
'string'));

        // Set the query language
        if (Multilanguage::isEnabled()) {
            $lang = Factory::getLanguage()->getTag();
        } else {
            $lang = Helper::getDefaultLanguage();
        }

        $this->setState('language', $lang);

        // Load the list state.
        $this->setState('list.start', 0);
        $this->setState('list.limit', 10);

        // Load the parameters.
        $this->setState('params', $params);

        // Load the user state.
        $this->setState('user.id', (int)
$user->get('id'));
    }
}
PKp��[p	w�RQRQsrc/Model/SearchModel.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Model;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Finder\Administrator\Indexer\Query;
use Joomla\String\StringHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Search model class for the Finder package.
 *
 * @since  2.5
 */
class SearchModel extends ListModel
{
    /**
     * Context string for the model type
     *
     * @var    string
     * @since  2.5
     */
    protected $context = 'com_finder.search';

    /**
     * The query object is an instance of Query which contains and
     * models the entire search query including the text input; static and
     * dynamic taxonomy filters; date filters; etc.
     *
     * @var    Query
     * @since  2.5
     */
    protected $searchquery;

    /**
     * Maps each sorting field with a text label.
     *
     * @var string[]
     *
     * @since 4.3.0
     */
    protected $sortOrderFieldsLabels = [
        'relevance.asc'   =>
'COM_FINDER_SORT_BY_RELEVANCE_ASC',
        'relevance.desc'  =>
'COM_FINDER_SORT_BY_RELEVANCE_DESC',
        'title.asc'       => 'JGLOBAL_TITLE_ASC',
        'title.desc'      => 'JGLOBAL_TITLE_DESC',
        'date.asc'        => 'JDATE_ASC',
        'date.desc'       => 'JDATE_DESC',
        'price.asc'       =>
'COM_FINDER_SORT_BY_PRICE_ASC',
        'price.desc'      =>
'COM_FINDER_SORT_BY_PRICE_DESC',
        'sale_price.asc'  =>
'COM_FINDER_SORT_BY_SALES_PRICE_ASC',
        'sale_price.desc' =>
'COM_FINDER_SORT_BY_SALES_PRICE_DESC',
    ];

    /**
     * An array of all excluded terms ids.
     *
     * @var    array
     * @since  2.5
     */
    protected $excludedTerms = [];

    /**
     * An array of all included terms ids.
     *
     * @var    array
     * @since  2.5
     */
    protected $includedTerms = [];

    /**
     * An array of all required terms ids.
     *
     * @var    array
     * @since  2.5
     */
    protected $requiredTerms = [];

    /**
     * Method to get the results of the query.
     *
     * @return  array  An array of Result objects.
     *
     * @since   2.5
     * @throws  \Exception on database error.
     */
    public function getItems()
    {
        $items = parent::getItems();

        // Check the data.
        if (empty($items)) {
            return null;
        }

        $results = [];

        // Convert the rows to result objects.
        foreach ($items as $rk => $row) {
            // Build the result object.
            if (\is_resource($row->object)) {
                $result =
unserialize(stream_get_contents($row->object));
            } else {
                $result = unserialize($row->object);
            }

            $result->cleanURL = $result->route;

            // Add the result back to the stack.
            $results[] = $result;
        }

        // Return the results.
        return $results;
    }

    /**
     * Method to get the query object.
     *
     * @return  Query  A query object.
     *
     * @since   2.5
     */
    public function getQuery()
    {
        // Return the query object.
        return $this->searchquery;
    }

    /**
     * Method to build a database query to load the list data.
     *
     * @return  \Joomla\Database\DatabaseQuery  A database query.
     *
     * @since   2.5
     */
    protected function getListQuery()
    {
        // Create a new query object.
        $db    = $this->getDatabase();
        $query = $db->getQuery(true);

        // Select the required fields from the table.
        $query->select(
            $this->getState(
                'list.select',
                'l.link_id, l.object'
            )
        );

        $query->from('#__finder_links AS l');

        $user   = $this->getCurrentUser();
        $groups = $this->getState('user.groups',
$user->getAuthorisedViewLevels());
        $query->whereIn($db->quoteName('l.access'),
$groups)
            ->where('l.state = 1')
            ->where('l.published = 1');

        // Get the current date, minus seconds.
        $nowDate =
$db->quote(substr_replace(Factory::getDate()->toSql(),
'00', -2));

        // Add the publish up and publish down filters.
        $query->where('(l.publish_start_date IS NULL OR
l.publish_start_date <= ' . $nowDate . ')')
            ->where('(l.publish_end_date IS NULL OR
l.publish_end_date >= ' . $nowDate . ')');

        $query->group('l.link_id');
        $query->group('l.object');

        /*
         * Add the taxonomy filters to the query. We have to join the
taxonomy
         * map table for each group so that we can use AND clauses across
         * groups. Within each group there can be an array of values that
will
         * use OR clauses.
         */
        if (!empty($this->searchquery->filters)) {
            // Convert the associative array to a numerically indexed
array.
            $groups     = array_values($this->searchquery->filters);
            $taxonomies = \call_user_func_array('array_merge',
array_values($this->searchquery->filters));

            $query->join('INNER',
$db->quoteName('#__finder_taxonomy_map') . ' AS t ON
t.link_id = l.link_id')
                ->where('t.node_id IN (' .
implode(',', array_unique($taxonomies)) . ')');

            // Iterate through each taxonomy group.
            for ($i = 0, $c = \count($groups); $i < $c; $i++) {
                $query->having('SUM(CASE WHEN t.node_id IN ('
. implode(',', $groups[$i]) . ') THEN 1 ELSE 0 END) >
0');
            }
        }

        // Add the start date filter to the query.
        if (!empty($this->searchquery->date1)) {
            // Escape the date.
            $date1 = $db->quote($this->searchquery->date1);

            // Add the appropriate WHERE condition.
            if ($this->searchquery->when1 === 'before') {
               
$query->where($db->quoteName('l.start_date') . ' <=
' . $date1);
            } elseif ($this->searchquery->when1 ===
'after') {
               
$query->where($db->quoteName('l.start_date') . ' >=
' . $date1);
            } else {
               
$query->where($db->quoteName('l.start_date') . ' =
' . $date1);
            }
        }

        // Add the end date filter to the query.
        if (!empty($this->searchquery->date2)) {
            // Escape the date.
            $date2 = $db->quote($this->searchquery->date2);

            // Add the appropriate WHERE condition.
            if ($this->searchquery->when2 === 'before') {
               
$query->where($db->quoteName('l.start_date') . ' <=
' . $date2);
            } elseif ($this->searchquery->when2 ===
'after') {
               
$query->where($db->quoteName('l.start_date') . ' >=
' . $date2);
            } else {
               
$query->where($db->quoteName('l.start_date') . ' =
' . $date2);
            }
        }

        // Filter by language
        if ($this->getState('filter.language')) {
            $query->where('l.language IN (' .
$db->quote(Factory::getLanguage()->getTag()) . ', ' .
$db->quote('*') . ')');
        }

        // Get the result ordering and direction.
        $ordering  = $this->getState('list.ordering',
'm.weight');
        $direction = $this->getState('list.direction',
'DESC');

        /*
         * If we are ordering by relevance we have to add up the relevance
         * scores that are contained in the ordering field.
         */
        if ($ordering === 'm.weight') {
            // Get the base query and add the ordering information.
            $query->select('SUM(' . $db->escape($ordering)
. ') AS ordering');
        } else {
            /**
             * If we are not ordering by relevance, we just have to add
             * the unique items to the set.
             */
            // Get the base query and add the ordering information.
            $query->select($db->escape($ordering) . ' AS
ordering');
        }

        $query->order('ordering ' .
$db->escape($direction));

        /*
         * If there are no optional or required search terms in the query,
we
         * can get the results in one relatively simple database query.
         */
        if (empty($this->includedTerms) &&
$this->searchquery->empty && $this->searchquery->input
== '') {
            // Return the results.
            return $query;
        }

        /*
         * If there are no optional or required search terms in the query
and
         * empty searches are not allowed, we return an empty query.
         * If the search term is not empty and empty searches are allowed,
         * but no terms were found, we return an empty query as well.
         */
        if (
            empty($this->includedTerms)
            && (!$this->searchquery->empty ||
($this->searchquery->empty && $this->searchquery->input
!= ''))
        ) {
            // Since we need to return a query, we simplify this one.
            $query->clear('join')
                ->clear('where')
                ->clear('bounded')
                ->clear('having')
                ->clear('group')
                ->where('false');

            return $query;
        }

        $included = \call_user_func_array('array_merge',
array_values($this->includedTerms));
        $query->join('INNER',
$db->quoteName('#__finder_links_terms') . ' AS m ON
m.link_id = l.link_id')
            ->where('m.term_id IN (' . implode(',',
$included) . ')');

        // Check if there are any excluded terms to deal with.
        if (\count($this->excludedTerms)) {
            $query2 = $db->getQuery(true);
            $query2->select('e.link_id')
               
->from($db->quoteName('#__finder_links_terms',
'e'))
                ->where('e.term_id IN (' .
implode(',', $this->excludedTerms) . ')');
            $query->where('l.link_id NOT IN (' . $query2 .
')');
        }

        /*
         * The query contains required search terms.
         */
        if (\count($this->requiredTerms)) {
            foreach ($this->requiredTerms as $terms) {
                if (\count($terms)) {
                    $query->having('SUM(CASE WHEN m.term_id IN
(' . implode(',', $terms) . ') THEN 1 ELSE 0 END) >
0');
                } else {
                    $query->where('false');
                    break;
                }
            }
        }

        return $query;
    }

    /**
     * Method to get the available sorting fields.
     *
     * @return  array   The sorting field objects.
     *
     * @throws  \Exception
     *
     * @since   4.3.0
     */
    public function getSortOrderFields()
    {
        $sortOrderFields      = [];
        $directions           = ['asc', 'desc'];
        $app                  = Factory::getApplication();
        $params               = $app->getParams();
        $sortOrderFieldValues =
$params->get('shown_sort_order', [], 'array');

        if ($params->get('show_sort_order', 0,
'uint') && !empty($sortOrderFieldValues)) {
            $defaultSortFieldValue =
$params->get('sort_order', '', 'cmd');
            $queryUri              =
Uri::getInstance($this->getQuery()->toUri());

            // If the default field is not included in the shown sort
fields, add it.
            if (!\in_array($defaultSortFieldValue, $sortOrderFieldValues))
{
                array_unshift($sortOrderFieldValues,
$defaultSortFieldValue);
            }

            foreach ($sortOrderFieldValues as $sortOrderFieldValue) {
                foreach ($directions as $direction) {
                    // The relevance has only descending direction. Except
if ascending is set in the parameters.
                    if ($sortOrderFieldValue === 'relevance'
&& $direction === 'asc' &&
$app->getParams()->get('sort_direction', 'desc')
=== 'desc') {
                        continue;
                    }

                    $sortOrderFields[] =
$this->getSortField($sortOrderFieldValue, $direction, $queryUri);
                }
            }
        }

        // Import Finder plugins
        PluginHelper::importPlugin('finder');

        // Trigger an event, in case a plugin wishes to change the order
fields.
        $app->triggerEvent('onFinderSortOrderFields',
[&$sortOrderFields]);

        return $sortOrderFields;
    }

    /**
     * Method to generate and return a sorting field
     *
     * @param   string  $value      The value based on which the results
will be sorted.
     * @param   string  $direction  The sorting direction ('asc'
or 'desc').
     * @param   Uri     $queryUri   The uri of the search query.
     *
     * @return  \stdClass   The sorting field object.
     *
     * @throws  \Exception
     *
     * @since   4.3.0
     */
    protected function getSortField(string $value, string $direction, Uri
$queryUri)
    {
        $sortField = new \stdClass();
        $app       = Factory::getApplication();

        // We have to clone the query uri. Otherwise the next elements will
use the same.
        $queryUri = clone $queryUri;
        $queryUri->setVar('o', $value);
        $currentOrderingDirection =
$app->getInput()->getWord('od',
$app->getParams()->get('sort_direction',
'desc'));

        // Validate the sorting direction and add it only if it is
different than the set in the params.
        if (\in_array($direction, ['asc', 'desc'])
&& $direction !=
$app->getParams()->get('sort_direction', 'desc'))
{
            $queryUri->setVar('od',
StringHelper::strtolower($direction));
        }

        $label = '';

        if (isset($this->sortOrderFieldsLabels[$value . '.' .
$direction])) {
            $label = Text::_($this->sortOrderFieldsLabels[$value .
'.' . $direction]);
        }

        $sortField->label      = $label;
        $sortField->url        = $queryUri->toString();
        $currentSortOrderField =
$app->getInput()->getWord('o',
$app->getParams()->get('sort_order',
'relevance'));
        $sortField->active     = false;

        if ($value === StringHelper::strtolower($currentSortOrderField)
&& $direction === $currentOrderingDirection) {
            $sortField->active = true;
        }

        return $sortField;
    }

    /**
     * Method to get a store id based on model the configuration state.
     *
     * This is necessary because the model is used by the component and
     * different modules that might need different sets of data or
different
     * ordering requirements.
     *
     * @param   string   $id    An identifier string to generate the store
id. [optional]
     * @param   boolean  $page  True to store the data paged, false to
store all data. [optional]
     *
     * @return  string  A store id.
     *
     * @since   2.5
     */
    protected function getStoreId($id = '', $page = true)
    {
        // Get the query object.
        $query = $this->getQuery();

        // Add the search query state.
        $id .= ':' . $query->input;
        $id .= ':' . $query->language;
        $id .= ':' . $query->filter;
        $id .= ':' . serialize($query->filters);
        $id .= ':' . $query->date1;
        $id .= ':' . $query->date2;
        $id .= ':' . $query->when1;
        $id .= ':' . $query->when2;

        if ($page) {
            // Add the list state for page specific data.
            $id .= ':' .
$this->getState('list.start');
            $id .= ':' .
$this->getState('list.limit');
            $id .= ':' .
$this->getState('list.ordering');
            $id .= ':' .
$this->getState('list.direction');
        }

        return parent::getStoreId($id);
    }

    /**
     * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
     *
     * @param   string  $ordering   An optional ordering field. [optional]
     * @param   string  $direction  An optional direction. [optional]
     *
     * @return  void
     *
     * @since   2.5
     */
    protected function populateState($ordering = null, $direction = null)
    {
        // Get the configuration options.
        $app      = Factory::getApplication();
        $input    = $app->getInput();
        $params   = $app->getParams();
        $user     = $this->getCurrentUser();
        $language = $app->getLanguage();

        $this->setState('filter.language',
Multilanguage::isEnabled());

        $request = $input->request;
        $options = [];

        // Get the empty query setting.
        $options['empty'] =
$params->get('allow_empty_query', 0);

        // Get the static taxonomy filters.
        $options['filter'] = $request->getInt('f',
$params->get('f', ''));

        // Get the dynamic taxonomy filters.
        $options['filters'] = $request->get('t',
$params->get('t', []), 'array');

        // Get the query string.
        $options['input'] = $request->getString('q',
$params->get('q', ''));

        // Get the query language.
        $options['language'] = $request->getCmd('l',
$params->get('l', $language->getTag()));

        // Set the word match mode
        $options['word_match'] =
$params->get('word_match', 'exact');

        // Get the start date and start date modifier filters.
        $options['date1'] =
$request->getString('d1', $params->get('d1',
''));
        $options['when1'] =
$request->getString('w1', $params->get('w1',
''));

        // Get the end date and end date modifier filters.
        $options['date2'] =
$request->getString('d2', $params->get('d2',
''));
        $options['when2'] =
$request->getString('w2', $params->get('w2',
''));

        // Load the query object.
        $this->searchquery = new Query($options,
$this->getDatabase());

        // Load the query token data.
        $this->excludedTerms =
$this->searchquery->getExcludedTermIds();
        $this->includedTerms =
$this->searchquery->getIncludedTermIds();
        $this->requiredTerms =
$this->searchquery->getRequiredTermIds();

        // Load the list state.
        $this->setState('list.start',
$input->get('limitstart', 0, 'uint'));
        $this->setState('list.limit',
$input->get('limit', $params->get('list_limit',
$app->get('list_limit', 20)), 'uint'));

        /*
         * Load the sort ordering.
         * Currently this is 'hard' coded via menu item parameter
but may not satisfy a users need.
         * More flexibility was way more user friendly. So we allow the
user to pass a custom value
         * from the pool of fields that are indexed like the
'title' field.
         * Also, we allow this parameter to be passed in either case
(lower/upper).
         */
        $order = $input->getWord('o',
$params->get('sort_order', 'relevance'));
        $order = StringHelper::strtolower($order);
        $this->setState('list.raworder', $order);

        switch ($order) {
            case 'date':
                $this->setState('list.ordering',
'l.start_date');
                break;

            case 'price':
                $this->setState('list.ordering',
'l.list_price');
                break;

            case 'sale_price':
                $this->setState('list.ordering',
'l.sale_price');
                break;

            case ($order === 'relevance' &&
!empty($this->includedTerms)):
                $this->setState('list.ordering',
'm.weight');
                break;

            case 'title':
                $this->setState('list.ordering',
'l.title');
                break;

            default:
                $this->setState('list.ordering',
'l.link_id');
                $this->setState('list.raworder');
                break;
        }

        /*
         * Load the sort direction.
         * Currently this is 'hard' coded via menu item parameter
but may not satisfy a users need.
         * More flexibility was way more user friendly. So we allow to be
inverted.
         * Also, we allow this parameter to be passed in either case
(lower/upper).
         */
        $dirn = $input->getWord('od',
$params->get('sort_direction', 'desc'));
        $dirn = StringHelper::strtolower($dirn);

        switch ($dirn) {
            case 'asc':
                $this->setState('list.direction',
'ASC');
                break;

            default:
                $this->setState('list.direction',
'DESC');
                break;
        }

        // Set the match limit.
        $this->setState('match.limit', 1000);

        // Load the parameters.
        $this->setState('params', $params);

        // Load the user state.
        $this->setState('user.id', (int)
$user->get('id'));
        $this->setState('user.groups',
$user->getAuthorisedViewLevels());
    }
}
PKp��[:$�~$src/Controller/DisplayController.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Controller;

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Finder Component Controller.
 *
 * @since  2.5
 */
class DisplayController extends BaseController
{
    /**
     * Method to display a view.
     *
     * @param   boolean  $cachable   If true, the view output will be
cached. [optional]
     * @param   array    $urlparams  An array of safe URL parameters and
their variable types. Optional.
     *                   @see       
\Joomla\CMS\Filter\InputFilter::clean() for valid values.
     *
     * @return  static  This object is to support chaining.
     *
     * @since   2.5
     */
    public function display($cachable = false, $urlparams = [])
    {
        $input    = $this->app->getInput();
        $cachable = true;

        // Load plugin language files.
        LanguageHelper::loadPluginLanguage();

        // Set the default view name and format from the Request.
        $viewName = $input->get('view', 'search',
'word');
        $input->set('view', $viewName);

        // Don't cache view for search queries
        if ($input->get('q', null, 'string') ||
$input->get('f', null, 'int') ||
$input->get('t', null, 'array')) {
            $cachable = false;
        }

        $safeurlparams = [
            'f'    => 'INT',
            'lang' => 'CMD',
        ];

        return parent::display($cachable, $safeurlparams);
    }
}
PKp��[]�2�

(src/Controller/SuggestionsController.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\Controller;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Controller\BaseController;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Suggestions \JSON controller for Finder.
 *
 * @since  2.5
 */
class SuggestionsController extends BaseController
{
    /**
     * Method to find search query suggestions. Uses awesomplete
     *
     * @return  void
     *
     * @since   3.4
     */
    public function suggest()
    {
        $app           = $this->app;
        $app->mimeType = 'application/json';

        // Ensure caching is disabled as it depends on the query param in
the model
        $app->allowCache(false);

        $suggestions = $this->getSuggestions();

        // Send the response.
        $app->setHeader('Content-Type', $app->mimeType .
'; charset=' . $app->charSet);
        $app->sendHeaders();
        echo '{ "suggestions": ' .
json_encode($suggestions) . ' }';
    }

    /**
     * Method to find search query suggestions for OpenSearch
     *
     * @return  void
     *
     * @since   4.0.0
     */
    public function opensearchsuggest()
    {
        $app           = $this->app;
        $app->mimeType = 'application/json';
        $result        = [];
        $result[]      =
$app->getInput()->request->get('q', '',
'string');

        $result[] = $this->getSuggestions();

        // Ensure caching is disabled as it depends on the query param in
the model
        $app->allowCache(false);

        // Send the response.
        $app->setHeader('Content-Type', $app->mimeType .
'; charset=' . $app->charSet);
        $app->sendHeaders();
        echo json_encode($result);
    }

    /**
     * Method to retrieve the data from the database
     *
     * @return  array  The suggested words
     *
     * @since   3.4
     */
    protected function getSuggestions()
    {
        $return = [];

        $params = ComponentHelper::getParams('com_finder');

        if ($params->get('show_autosuggest', 1)) {
            // Get the suggestions.
            $model  = $this->getModel('Suggestions');
            $return = $model->getItems();
        }

        // Check the data.
        if (empty($return)) {
            $return = [];
        }

        return $return;
    }
}
PKp��[��A�1
1
src/View/Search/FeedView.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\View\Search;

use Joomla\CMS\Document\Feed\FeedItem;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Router\Route;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Search feed view class for the Finder package.
 *
 * @since  2.5
 */
class FeedView extends BaseHtmlView
{
    /**
     * Method to display the view.
     *
     * @param   string  $tpl  A template file to load. [optional]
     *
     * @return  void
     *
     * @since   2.5
     */
    public function display($tpl = null)
    {
        // Get the application
        $app = Factory::getApplication();

        // Adjust the list limit to the feed limit.
        $app->getInput()->set('limit',
$app->get('feed_limit'));

        // Get view data.
        $state   = $this->get('State');
        $params  = $state->get('params');
        $query   = $this->get('Query');
        $results = $this->get('Items');
        $total   = $this->get('Total');

        // Push out the query data.
        $explained = HTMLHelper::_('query.explained', $query);

        // Set the document title.
        $this->setDocumentTitle($params->get('page_title',
''));

        // Configure the document description.
        if (!empty($explained)) {
           
$this->getDocument()->setDescription(html_entity_decode(strip_tags($explained),
ENT_QUOTES, 'UTF-8'));
        }

        // Set the document link.
        $this->getDocument()->link = Route::_($query->toUri());

        // If we don't have any results, we are done.
        if (empty($results)) {
            return;
        }

        // Convert the results to feed entries.
        foreach ($results as $result) {
            // Convert the result to a feed entry.
            $item              = new FeedItem();
            $item->title       = $result->title;
            $item->link        = Route::_($result->route);
            $item->description = $result->description;

            // Use Unix date to cope for non-english languages
            $item->date        = (int) $result->start_date ?
HTMLHelper::_('date', $result->start_date, 'U') :
$result->indexdate;

            // Loads item info into RSS array
            $this->getDocument()->addItem($item);
        }
    }
}
PKp��[f5x(("src/View/Search/OpensearchView.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\View\Search;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Document\Opensearch\OpensearchUrl;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\AbstractView;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * OpenSearch View class for Finder
 *
 * @since  2.5
 */
class OpensearchView extends AbstractView
{
    /**
     * Method to display the view.
     *
     * @param   string  $tpl  A template file to load. [optional]
     *
     * @return  void
     *
     * @since   2.5
     */
    public function display($tpl = null)
    {
        $app = Factory::getApplication();

        $params = ComponentHelper::getParams('com_finder');
       
$this->getDocument()->setShortName($params->get('opensearch_name',
$app->get('sitename', '')));
       
$this->getDocument()->setDescription($params->get('opensearch_description',
$app->get('MetaDesc', '')));

        // Prevent any output when OpenSearch Support is disabled
        if (!$params->get('opensearch', 1)) {
            return;
        }

        // Add the URL for the search
        $searchUri      =
'index.php?option=com_finder&view=search&q={searchTerms}';
        $suggestionsUri =
'index.php?option=com_finder&task=suggestions.opensearchsuggest&format=json&q={searchTerms}';
        $baseUrl        =
Uri::getInstance()->toString(['host', 'port',
'scheme']);
        $active         = $app->getMenu()->getActive();

        if ($active->component == 'com_finder') {
            $searchUri .= '&Itemid=' . $active->id;
            $suggestionsUri .= '&Itemid=' . $active->id;
        }

        // Add the HTML result view
        $htmlSearch           = new OpensearchUrl();
        $htmlSearch->template = $baseUrl . Route::_($searchUri, false);
        $this->getDocument()->addUrl($htmlSearch);

        // Add the RSS result view
        $htmlSearch           = new OpensearchUrl();
        $htmlSearch->template = $baseUrl . Route::_($searchUri .
'&format=feed&type=rss', false);
        $htmlSearch->type     = 'application/rss+xml';
        $this->getDocument()->addUrl($htmlSearch);

        // Add the Atom result view
        $htmlSearch           = new OpensearchUrl();
        $htmlSearch->template = $baseUrl . Route::_($searchUri .
'&format=feed&type=atom', false);
        $htmlSearch->type     = 'application/atom+xml';
        $this->getDocument()->addUrl($htmlSearch);

        // Add suggestions URL
        if ($params->get('show_autosuggest', 1)) {
            $htmlSearch           = new OpensearchUrl();
            $htmlSearch->template = $baseUrl . Route::_($suggestionsUri,
false);
            $htmlSearch->type     =
'application/x-suggestions+json';
            $this->getDocument()->addUrl($htmlSearch);
        }
    }
}
PKp��[�^�_(_(src/View/Search/HtmlView.phpnu�[���<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_finder
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\Component\Finder\Site\View\Search;

use Joomla\CMS\Event\Finder\ResultEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Pagination\Pagination;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Profiler\Profiler;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Router\SiteRouterAwareInterface;
use Joomla\CMS\Router\SiteRouterAwareTrait;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Finder\Administrator\Indexer\Query;
use Joomla\Component\Finder\Site\Helper\FinderHelper;
use Joomla\Filesystem\Path;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Search HTML view class for the Finder package.
 *
 * @since  2.5
 */
class HtmlView extends BaseHtmlView implements SiteRouterAwareInterface
{
    use SiteRouterAwareTrait;

    /**
     * The query indexer object
     *
     * @var    Query
     *
     * @since  4.0.0
     */
    protected $query;

    /**
     * The page parameters
     *
     * @var  \Joomla\Registry\Registry|null
     */
    protected $params = null;

    /**
     * The model state
     *
     * @var  \Joomla\Registry\Registry
     */
    protected $state;

    /**
     * The logged in user
     *
     * @var  \Joomla\CMS\User\User|null
     */
    protected $user = null;

    /**
     * The suggested search query
     *
     * @var   string|false
     *
     * @since 4.0.0
     */
    protected $suggested = false;

    /**
     * The explained (human-readable) search query
     *
     * @var   string|null
     *
     * @since 4.0.0
     */
    protected $explained = null;

    /**
     * The page class suffix
     *
     * @var    string
     *
     * @since  4.0.0
     */
    protected $pageclass_sfx = '';

    /**
     * An array of results
     *
     * @var    array
     *
     * @since  3.8.0
     */
    protected $results;

    /**
     * The total number of items
     *
     * @var    integer
     *
     * @since  3.8.0
     */
    protected $total;

    /**
     * The pagination object
     *
     * @var    Pagination
     *
     * @since  3.8.0
     */
    protected $pagination;

    /**
     * Method to display the view.
     *
     * @param   string  $tpl  A template file to load. [optional]
     *
     * @return  void
     *
     * @since   2.5
     */
    public function display($tpl = null)
    {
        $app          = Factory::getApplication();
        $this->params = $app->getParams();

        // Get view data.
        $this->state = $this->get('State');
        $this->query = $this->get('Query');
        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderQuery')
: null;
        $this->results = $this->get('Items');
        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderResults')
: null;
        $this->sortOrderFields =
$this->get('sortOrderFields');
        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderSortOrderFields')
: null;
        $this->total = $this->get('Total');
        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderTotal')
: null;
        $this->pagination = $this->get('Pagination');
        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderPagination')
: null;

        // Flag indicates to not add limitstart=0 to URL
        $this->pagination->hideEmptyLimitstart = true;

        // Check for errors.
        if (\count($errors = $this->get('Errors'))) {
            throw new GenericDataException(implode("\n",
$errors), 500);
        }

        // Configure the pathway.
        if (!empty($this->query->input)) {
           
$app->getPathway()->addItem($this->escape($this->query->input));
        }

        // Check for a double quote in the query string.
        if (strpos($this->query->input, '"')) {
            $router = $this->getSiteRouter();

            // Fix the q variable in the URL.
            if ($router->getVar('q') !==
$this->query->input) {
                $router->setVar('q',
$this->query->input);
            }
        }

        // Run an event on each result item
        if (\is_array($this->results)) {
            $dispatcher = $this->getDispatcher();

            // Import Finder plugins
            PluginHelper::importPlugin('finder', null, true,
$dispatcher);

            foreach ($this->results as $result) {
                $dispatcher->dispatch('onFinderResult', new
ResultEvent('onFinderResult', [
                    'subject' => $result,
                    'query'   => $this->query,
                ]));
            }
        }

        // Log the search
        FinderHelper::logSearch($this->query, $this->total);

        // Push out the query data.
        $this->suggested = HTMLHelper::_('query.suggested',
$this->query);
        $this->explained = HTMLHelper::_('query.explained',
$this->query);

        // Escape strings for HTML output
        $this->pageclass_sfx =
htmlspecialchars($this->params->get('pageclass_sfx',
''));

        // Check for layout override only if this is not the active menu
item
        // If it is the active menu item, then the view and category id
will match
        $active = $app->getMenu()->getActive();

        if (isset($active->query['layout'])) {
            // We need to set the layout in case this is an alternative
menu item (with an alternative layout)
            $this->setLayout($active->query['layout']);
        }

        $this->prepareDocument();

        \JDEBUG ?
Profiler::getInstance('Application')->mark('beforeFinderLayout')
: null;

        parent::display($tpl);

        \JDEBUG ?
Profiler::getInstance('Application')->mark('afterFinderLayout')
: null;
    }

    /**
     * Method to get hidden input fields for a get form so that control
variables
     * are not lost upon form submission
     *
     * @return  string  A string of hidden input form fields
     *
     * @since   2.5
     */
    protected function getFields()
    {
        $fields = null;

        // Get the URI.
        $uri = Uri::getInstance(Route::_($this->query->toUri()));
        $uri->delVar('q');
        $uri->delVar('o');
        $uri->delVar('t');
        $uri->delVar('d1');
        $uri->delVar('d2');
        $uri->delVar('w1');
        $uri->delVar('w2');
        $elements = $uri->getQuery(true);

        // Create hidden input elements for each part of the URI.
        foreach ($elements as $n => $v) {
            if (\is_scalar($v)) {
                $fields .= '<input type="hidden"
name="' . $n . '" value="' . $v .
'">';
            }
        }

        return $fields;
    }

    /**
     * Method to get the layout file for a search result object.
     *
     * @param   string  $layout  The layout file to check. [optional]
     *
     * @return  string  The layout file to use.
     *
     * @since   2.5
     */
    protected function getLayoutFile($layout = null)
    {
        // Create and sanitize the file name.
        $file = $this->_layout . '_' .
preg_replace('/[^A-Z0-9_\.-]/i', '', $layout);

        // Check if the file exists.
        $filetofind = $this->_createFileName('template',
['name' => $file]);
        $exists     = Path::find($this->_path['template'],
$filetofind);

        return ($exists ? $layout : 'result');
    }

    /**
     * Prepares the document
     *
     * @return  void
     *
     * @since   2.5
     */
    protected function prepareDocument()
    {
        $app   = Factory::getApplication();

        // Because the application sets a default page title,
        // we need to get it from the menu item itself
        $menu = $app->getMenu()->getActive();

        if ($menu) {
            $this->params->def('page_heading',
$this->params->get('page_title', $menu->title));
        } else {
            $this->params->def('page_heading',
Text::_('COM_FINDER_DEFAULT_PAGE_TITLE'));
        }

       
$this->setDocumentTitle($this->params->get('page_title',
''));

        if ($layout = $this->params->get('article_layout'))
{
            $this->setLayout($layout);
        }

        // Configure the document meta-description.
        if (!empty($this->explained)) {
            $explained =
$this->escape(html_entity_decode(strip_tags($this->explained),
ENT_QUOTES, 'UTF-8'));
            $this->getDocument()->setDescription($explained);
        } elseif
($this->params->get('menu-meta_description')) {
           
$this->getDocument()->setDescription($this->params->get('menu-meta_description'));
        }

        if ($this->params->get('robots')) {
            $this->getDocument()->setMetaData('robots',
$this->params->get('robots'));
        }

        // Check for OpenSearch
        if ($this->params->get('opensearch', 1)) {
            $ostitle = $this->params->get(
                'opensearch_name',
                Text::_('COM_FINDER_OPENSEARCH_NAME') . '
' . $app->get('sitename')
            );
            $this->getDocument()->addHeadLink(
                Uri::getInstance()->toString(['scheme',
'host', 'port']) .
Route::_('index.php?option=com_finder&view=search&format=opensearch'),
                'search',
                'rel',
                ['title' => $ostitle, 'type' =>
'application/opensearchdescription+xml']
            );
        }

        // Add feed link to the document head.
        if ($this->params->get('show_feed_link', 1) == 1) {
            // Add the RSS link.
            $props = ['type' =>
'application/rss+xml', 'title' =>
htmlspecialchars($this->getDocument()->getTitle())];
            $route = Route::_($this->query->toUri() .
'&format=feed&type=rss');
            $this->getDocument()->addHeadLink($route,
'alternate', 'rel', $props);

            // Add the ATOM link.
            $props = ['type' =>
'application/atom+xml', 'title' =>
htmlspecialchars($this->getDocument()->getTitle())];
            $route = Route::_($this->query->toUri() .
'&format=feed&type=atom');
            $this->getDocument()->addHeadLink($route,
'alternate', 'rel', $props);
        }
    }
}
PK[|I�33
access.xmlnu�[���<?xml version="1.0"
encoding="utf-8" ?>
<access component="com_finder">
	<section name="component">
		<action name="core.admin" title="JACTION_ADMIN"
description="JACTION_ADMIN_COMPONENT_DESC" />
		<action name="core.options"
title="JACTION_OPTIONS"
description="JACTION_OPTIONS_COMPONENT_DESC" />
		<action name="core.manage" title="JACTION_MANAGE"
description="JACTION_MANAGE_COMPONENT_DESC" />
		<action name="core.create" title="JACTION_CREATE"
description="JACTION_CREATE_COMPONENT_DESC" />
		<action name="core.delete" title="JACTION_DELETE"
description="JACTION_DELETE_COMPONENT_DESC" />
		<action name="core.edit" title="JACTION_EDIT"
description="JACTION_EDIT_COMPONENT_DESC" />
		<action name="core.edit.state"
title="JACTION_EDITSTATE"
description="JACTION_EDITSTATE_COMPONENT_DESC" />
	</section>
</access>
PK[T�:�d"d"
config.xmlnu�[���<?xml version="1.0"
encoding="utf-8"?>
<config>
	<fieldset
		name="search"
		label="COM_FINDER_FIELDSET_SEARCH_OPTIONS_LABEL"
		description="COM_FINDER_FIELDSET_SEARCH_OPTIONS_DESCRIPTION"
		>

		<field
			name="enabled"
			type="radio"
			label="COM_FINDER_CONFIG_GATHER_SEARCH_STATISTICS_LABEL"
			description="COM_FINDER_CONFIG_GATHER_SEARCH_STATISTICS_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="0"
			>
			<option value="1">JYES</option>
			<option value="0">JNO</option>
		</field>

		<field
			name="show_description"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_DESCRIPTION_LABEL"
			description="COM_FINDER_CONFIG_SHOW_DESCRIPTION_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="description_length"
			type="number"
			label="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_LABEL"
			description="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_DESCRIPTION"
			size="5"
			default="255"
			filter="integer"
			showon="show_description:1"
		/>


		<field
			name="allow_empty_query"
			type="radio"
			label="COM_FINDER_CONFIG_ALLOW_EMPTY_QUERY_LABEL"
			description="COM_FINDER_CONFIG_ALLOW_EMPTY_QUERY_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="0"
			>
			<option value="1">JYES</option>
			<option value="0">JNO</option>
		</field>

		<field
			name="show_url"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_URL_LABEL"
			description="COM_FINDER_CONFIG_SHOW_URL_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_autosuggest"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_AUTOSUGGEST_LABEL"
			description="COM_FINDER_CONFIG_SHOW_AUTOSUGGEST_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_suggested_query"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_LABEL"
			description="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_DESC"
			class="btn-group btn-group-yesno"
			default="1"
			validate="options"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_explained_query"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_LABEL"
			description="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_DESC"
			class="btn-group btn-group-yesno"
			default="1"
			validate="options"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_advanced"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_ADVANCED_LABEL"
			description="COM_FINDER_CONFIG_SHOW_ADVANCED_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_advanced_tips"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_ADVANCED_TIPS_LABEL"
			description="COM_FINDER_CONFIG_SHOW_ADVANCED_TIPS_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			showon="show_advanced:1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="expand_advanced"
			type="radio"
			label="COM_FINDER_CONFIG_EXPAND_ADVANCED_LABEL"
			description="COM_FINDER_CONFIG_EXPAND_ADVANCED_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="0"
			showon="show_advanced:1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="show_date_filters"
			type="radio"
			label="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_LABEL"
			description="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="0"
			showon="show_advanced:1"
			>
			<option value="1">JSHOW</option>
			<option value="0">JHIDE</option>
		</field>

		<field
			name="sort_order"
			type="list"
			label="COM_FINDER_CONFIG_SORT_ORDER_LABEL"
			description="COM_FINDER_CONFIG_SORT_ORDER_DESC"
			default="relevance"
			validate="options"
			>
			<option
value="relevance">COM_FINDER_CONFIG_SORT_OPTION_RELEVANCE</option>
			<option
value="date">COM_FINDER_CONFIG_SORT_OPTION_START_DATE</option>
			<option
value="price">COM_FINDER_CONFIG_SORT_OPTION_LIST_PRICE</option>
		</field>

		<field
			name="sort_direction"
			type="list"
			label="COM_FINDER_CONFIG_SORT_DIRECTION_LABEL"
			description="COM_FINDER_CONFIG_SORT_DIRECTION_DESC"
			default="desc"
			validate="options"
			>
			<option
value="desc">COM_FINDER_CONFIG_SORT_OPTION_DESCENDING</option>
			<option
value="asc">COM_FINDER_CONFIG_SORT_OPTION_ASCENDING</option>
		</field>

		<field
			name="highlight_terms"
			type="radio"
			label="COM_FINDER_CONFIG_HILIGHT_CONTENT_SEARCH_TERMS_LABEL"
			description="COM_FINDER_CONFIG_HILIGHT_CONTENT_SEARCH_TERMS_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JYES</option>
			<option value="0">JNO</option>
		</field>

		<field
			name="opensearch_name"
			type="text"
			label="COM_FINDER_CONFIG_FIELD_OPENSEARCH_NAME_LABEL"
			description="COM_FINDER_CONFIG_FIELD_OPENSEARCH_NAME_DESCRIPTION"
			default=""
		/>

		<field
			name="opensearch_description"
			type="textarea"
			label="COM_FINDER_CONFIG_FIELD_OPENSEARCH_DESCRIPTON_LABEL"
			description="COM_FINDER_CONFIG_FIELD_OPENSEARCH_DESCRIPTON_DESCRIPTION"
			default=""
			cols="30"
			rows="2"
		/>

	</fieldset>

	<fieldset
		name="index"
		label="COM_FINDER_FIELDSET_INDEX_OPTIONS_LABEL"
		description="COM_FINDER_FIELDSET_INDEX_OPTIONS_DESCRIPTION"
		>

		<field
			name="batch_size"
			type="list"
			label="COM_FINDER_CONFIG_BATCH_SIZE_LABEL"
			description="COM_FINDER_CONFIG_BATCH_SIZE_DESCRIPTION"
			default="50"
			validate="options"
			>
			<option value="5">J5</option>
			<option value="10">J10</option>
			<option value="25">J25</option>
			<option value="50">J50</option>
			<option value="75">J75</option>
			<option value="100">J100</option>
			<option value="150">J150</option>
			<option value="200">J200</option>
			<option value="250">J250</option>
			<option value="300">J300</option>
		</field>

		<field
			name="memory_table_limit"
			type="number"
			label="COM_FINDER_CONFIG_MEMORY_TABLE_LIMIT_LABEL"
			description="COM_FINDER_CONFIG_MEMORY_TABLE_LIMIT_DESCRIPTION"
			size="10"
			default="30000"
			filter="integer"
		/>

		<field
			name="title_multiplier"
			type="number"
			label="COM_FINDER_CONFIG_TITLE_MULTIPLIER_LABEL"
			description="COM_FINDER_CONFIG_TITLE_MULTIPLIER_DESCRIPTION"
			size="5"
			default="1.7"
		/>

		<field
			name="text_multiplier"
			type="number"
			label="COM_FINDER_CONFIG_TEXT_MULTIPLIER_LABEL"
			description="COM_FINDER_CONFIG_TEXT_MULTIPLIER_DESCRIPTION"
			size="5"
			default="0.7"
		/>

		<field
			name="meta_multiplier"
			type="number"
			label="COM_FINDER_CONFIG_META_MULTIPLIER_LABEL"
			description="COM_FINDER_CONFIG_META_MULTIPLIER_DESCRIPTION"
			size="5"
			default="1.2"
		/>

		<field
			name="path_multiplier"
			type="number"
			label="COM_FINDER_CONFIG_PATH_MULTIPLIER_LABEL"
			description="COM_FINDER_CONFIG_PATH_MULTIPLIER_DESCRIPTION"
			size="5"
			default="2.0"
		/>

		<field
			name="misc_multiplier"
			type="number"
			label="COM_FINDER_CONFIG_MISC_MULTIPLIER_LABEL"
			description="COM_FINDER_CONFIG_MISC_MULTIPLIER_DESCRIPTION"
			size="5"
			default="0.3"
		/>

		<field
			name="stem"
			type="radio"
			label="COM_FINDER_CONFIG_STEMMER_ENABLE_LABEL"
			description="COM_FINDER_CONFIG_STEMMER_ENABLE_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="1"
			>
			<option value="1">JYES</option>
			<option value="0">JNO</option>
		</field>

		<field
			name="stemmer"
			type="list"
			label="COM_FINDER_CONFIG_STEMMER_LABEL"
			description="COM_FINDER_CONFIG_STEMMER_DESCRIPTION"
			default="snowball"
			showon="stem:1" 
			>
			<option
value="porter_en">COM_FINDER_CONFIG_STEMMER_PORTER_EN</option>
			<option
value="fr">COM_FINDER_CONFIG_STEMMER_FR</option>
			<option
value="snowball">COM_FINDER_CONFIG_STEMMER_SNOWBALL</option>
		</field>

		<field
			name="enable_logging"
			type="radio"
			label="COM_FINDER_CONFIG_ENABLE_LOGGING_LABEL"
			description="COM_FINDER_CONFIG_ENABLE_LOGGING_DESCRIPTION"
			class="btn-group btn-group-yesno"
			default="0"
			>
			<option value="1">JYES</option>
			<option value="0">JNO</option>
		</field>

	</fieldset>

	<fieldset
		name="permissions"
		label="JCONFIG_PERMISSIONS_LABEL"
		description="JCONFIG_PERMISSIONS_DESC"
		>

		<field
			name="rules"
			type="rules"
			label="JCONFIG_PERMISSIONS_LABEL"
			filter="rules"
			validate="rules"
			component="com_finder"
			section="component"
		/>

	</fieldset>
</config>
PK[Eӓ�controllers/filter.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Utilities\ArrayHelper;

/**
 * Indexer controller class for Finder.
 *
 * @since  2.5
 */
class FinderControllerFilter extends JControllerForm
{
	/**
	 * Method to save a record.
	 *
	 * @param   string  $key     The name of the primary key of the URL
variable.
	 * @param   string  $urlVar  The name of the URL variable if different
from the primary key (sometimes required to avoid router collisions).
	 *
	 * @return  boolean  True if successful, false otherwise.
	 *
	 * @since   2.5
	 */
	public function save($key = null, $urlVar = null)
	{
		// Check for request forgeries.
		$this->checkToken();

		$app = JFactory::getApplication();
		$input = $app->input;
		$model = $this->getModel();
		$table = $model->getTable();
		$data = $input->post->get('jform', array(),
'array');
		$checkin = property_exists($table, 'checked_out');
		$context = "$this->option.edit.$this->context";
		$task = $this->getTask();

		// Determine the name of the primary key for the data.
		if (empty($key))
		{
			$key = $table->getKeyName();
		}

		// To avoid data collisions the urlVar may be different from the primary
key.
		if (empty($urlVar))
		{
			$urlVar = $key;
		}

		$recordId = $input->get($urlVar, '', 'int');

		if (!$this->checkEditId($context, $recordId))
		{
			// Somehow the person just went to the form and tried to save it. We
don't allow that.
			$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID',
$recordId));
			$this->setMessage($this->getError(), 'error');
			$this->setRedirect(JRoute::_('index.php?option=' .
$this->option . '&view=' . $this->view_list .
$this->getRedirectToListAppend(), false));

			return false;
		}

		// Populate the row id from the session.
		$data[$key] = $recordId;

		// The save2copy task needs to be handled slightly differently.
		if ($task === 'save2copy')
		{
			// Check-in the original row.
			if ($checkin && $model->checkin($data[$key]) === false)
			{
				// Check-in failed. Go back to the item and display a notice.
				$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED',
$model->getError()));
				$this->setMessage($this->getError(), 'error');
				$this->setRedirect('index.php?option=' . $this->option
. '&view=' . $this->view_item .
$this->getRedirectToItemAppend($recordId, $urlVar));

				return false;
			}

			// Reset the ID and then treat the request as for Apply.
			$data[$key] = 0;
			$task = 'apply';
		}

		// Access check.
		if (!$this->allowSave($data, $key))
		{
			$this->setError(JText::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'));
			$this->setMessage($this->getError(), 'error');
			$this->setRedirect(JRoute::_('index.php?option=' .
$this->option . '&view=' . $this->view_list .
$this->getRedirectToListAppend(), false));

			return false;
		}

		// Validate the posted data.
		// Sometimes the form needs some posted data, such as for plugins and
modules.
		$form = $model->getForm($data, false);

		if (!$form)
		{
			$app->enqueueMessage($model->getError(), 'error');

			return false;
		}

		// Test whether the data is valid.
		$validData = $model->validate($form, $data);

		// Check for validation errors.
		if ($validData === false)
		{
			// Get the validation messages.
			$errors = $model->getErrors();

			// Push up to three validation messages out to the user.
			for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
			{
				if ($errors[$i] instanceof Exception)
				{
					$app->enqueueMessage($errors[$i]->getMessage(),
'warning');
				}
				else
				{
					$app->enqueueMessage($errors[$i], 'warning');
				}
			}

			// Save the data in the session.
			$app->setUserState($context . '.data', $data);

			// Redirect back to the edit screen.
			$this->setRedirect(
				JRoute::_('index.php?option=' . $this->option .
'&view=' . $this->view_item .
$this->getRedirectToItemAppend($recordId, $key), false)
			);

			return false;
		}

		// Get and sanitize the filter data.
		$validData['data'] = $input->post->get('t',
array(), 'array');
		$validData['data'] =
array_unique($validData['data']);
		$validData['data'] =
ArrayHelper::toInteger($validData['data']);

		// Remove any values of zero.
		if (array_search(0, $validData['data'], true))
		{
			unset($validData['data'][array_search(0,
$validData['data'], true)]);
		}

		// Attempt to save the data.
		if (!$model->save($validData))
		{
			// Save the data in the session.
			$app->setUserState($context . '.data', $validData);

			// Redirect back to the edit screen.
			$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED',
$model->getError()));
			$this->setMessage($this->getError(), 'error');
			$this->setRedirect(
				JRoute::_('index.php?option=' . $this->option .
'&view=' . $this->view_item .
$this->getRedirectToItemAppend($recordId, $key), false)
			);

			return false;
		}

		// Save succeeded, so check-in the record.
		if ($checkin && $model->checkin($validData[$key]) === false)
		{
			// Save the data in the session.
			$app->setUserState($context . '.data', $validData);

			// Check-in failed, so go back to the record and display a notice.
			$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED',
$model->getError()));
			$this->setMessage($this->getError(), 'error');
			$this->setRedirect('index.php?option=' . $this->option .
'&view=' . $this->view_item .
$this->getRedirectToItemAppend($recordId, $key));

			return false;
		}

		$this->setMessage(
			JText::_(
				(JFactory::getLanguage()->hasKey($this->text_prefix . ($recordId
=== 0 && $app->isClient('site') ? '_SUBMIT'
: '') . '_SAVE_SUCCESS')
				? $this->text_prefix : 'JLIB_APPLICATION') . ($recordId
=== 0 && $app->isClient('site') ? '_SUBMIT'
: '') . '_SAVE_SUCCESS'
			)
		);

		// Redirect the user and adjust session state based on the chosen task.
		switch ($task)
		{
			case 'apply':
				// Set the record data in the session.
				$recordId = $model->getState($this->context . '.id');
				$this->holdEditId($context, $recordId);
				$app->setUserState($context . '.data', null);
				$model->checkout($recordId);

				// Redirect back to the edit screen.
				$this->setRedirect(
					JRoute::_('index.php?option=' . $this->option .
'&view=' . $this->view_item .
$this->getRedirectToItemAppend($recordId, $key), false)
				);

				break;

			case 'save2new':
				// Clear the record id and data from the session.
				$this->releaseEditId($context, $recordId);
				$app->setUserState($context . '.data', null);

				// Redirect back to the edit screen.
				$this->setRedirect(
					JRoute::_('index.php?option=' . $this->option .
'&view=' . $this->view_item .
$this->getRedirectToItemAppend(null, $key), false)
				);

				break;

			default:
				// Clear the record id and data from the session.
				$this->releaseEditId($context, $recordId);
				$app->setUserState($context . '.data', null);

				// Redirect to the list screen.
				$this->setRedirect(
					JRoute::_('index.php?option=' . $this->option .
'&view=' . $this->view_list .
$this->getRedirectToListAppend(), false)
				);

				break;
		}

		// Invoke the postSave method to allow for the child class to access the
model.
		$this->postSaveHook($model, $validData);

		return true;
	}
}
PK[.M||controllers/filters.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Filters controller class for Finder.
 *
 * @since  2.5
 */
class FinderControllerFilters extends JControllerAdmin
{
	/**
	 * Method to get a model object, loading it if required.
	 *
	 * @param   string  $name    The model name. Optional.
	 * @param   string  $prefix  The class prefix. Optional.
	 * @param   array   $config  Configuration array for model. Optional.
	 *
	 * @return  JModelLegacy  The model.
	 *
	 * @since   2.5
	 */
	public function getModel($name = 'Filter', $prefix =
'FinderModel', $config = array('ignore_request' =>
true))
	{
		return parent::getModel($name, $prefix, $config);
	}
}
PK[c�G'GGcontrollers/index.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Index controller class for Finder.
 *
 * @since  2.5
 */
class FinderControllerIndex extends JControllerAdmin
{
	/**
	 * Method to get a model object, loading it if required.
	 *
	 * @param   string  $name    The model name. Optional.
	 * @param   string  $prefix  The class prefix. Optional.
	 * @param   array   $config  Configuration array for model. Optional.
	 *
	 * @return  JModelLegacy  The model.
	 *
	 * @since   2.5
	 */
	public function getModel($name = 'Index', $prefix =
'FinderModel', $config = array('ignore_request' =>
true))
	{
		return parent::getModel($name, $prefix, $config);
	}

	/**
	 * Method to purge all indexed links from the database.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function purge()
	{
		$this->checkToken();

		// Remove the script time limit.
		@set_time_limit(0);

		$model = $this->getModel('Index', 'FinderModel');

		// Attempt to purge the index.
		$return = $model->purge();

		if (!$return)
		{
			$message = JText::_('COM_FINDER_INDEX_PURGE_FAILED',
$model->getError());
			$this->setRedirect('index.php?option=com_finder&view=index',
$message);

			return false;
		}
		else
		{
			$message = JText::_('COM_FINDER_INDEX_PURGE_SUCCESS');
			$this->setRedirect('index.php?option=com_finder&view=index',
$message);

			return true;
		}
	}
}
PK[��$��)�)controllers/indexer.json.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

// Register dependent classes.
JLoader::register('FinderIndexer', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/indexer/indexer.php');

/**
 * Indexer controller class for Finder.
 *
 * @since  2.5
 */
class FinderControllerIndexer extends JControllerLegacy
{
	/**
	 * Method to start the indexer.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function start()
	{
		$params = JComponentHelper::getParams('com_finder');

		if ($params->get('enable_logging', '0'))
		{
			$options['format'] =
'{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
			$options['text_file'] = 'indexer.php';
			JLog::addLogger($options);
		}

		// Log the start
		try
		{
			JLog::add('Starting the indexer', JLog::INFO);
		}
		catch (RuntimeException $exception)
		{
			// Informational log only
		}

		// We don't want this form to be cached.
		$app = JFactory::getApplication();
		$app->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00
GMT', true);
		$app->setHeader('Last-Modified', gmdate('D, d M Y
H:i:s') . ' GMT', true);
		$app->setHeader('Cache-Control', 'no-store, no-cache,
must-revalidate, post-check=0, pre-check=0', false);
		$app->setHeader('Pragma', 'no-cache');

		// Check for a valid token. If invalid, send a 403 with the error
message.
		JSession::checkToken('request') or static::sendResponse(new
Exception(JText::_('JINVALID_TOKEN_NOTICE'), 403));

		// Put in a buffer to silence noise.
		ob_start();

		// Reset the indexer state.
		FinderIndexer::resetState();

		// Import the finder plugins.
		JPluginHelper::importPlugin('finder');

		// Add the indexer language to JS
		JText::script('COM_FINDER_AN_ERROR_HAS_OCCURRED');
		JText::script('COM_FINDER_NO_ERROR_RETURNED');

		// Start the indexer.
		try
		{
			// Trigger the onStartIndex event.
			JEventDispatcher::getInstance()->trigger('onStartIndex');

			// Get the indexer state.
			$state = FinderIndexer::getState();
			$state->start = 1;

			// Send the response.
			static::sendResponse($state);
		}

		// Catch an exception and return the response.
		catch (Exception $e)
		{
			static::sendResponse($e);
		}
	}

	/**
	 * Method to run the next batch of content through the indexer.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function batch()
	{
		$params = JComponentHelper::getParams('com_finder');

		if ($params->get('enable_logging', '0'))
		{
			$options['format'] =
'{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
			$options['text_file'] = 'indexer.php';
			JLog::addLogger($options);
		}

		// Log the start
		try
		{
			JLog::add('Starting the indexer batch process', JLog::INFO);
		}
		catch (RuntimeException $exception)
		{
			// Informational log only
		}

		// We don't want this form to be cached.
		$app = JFactory::getApplication();
		$app->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00
GMT', true);
		$app->setHeader('Last-Modified', gmdate('D, d M Y
H:i:s') . ' GMT', true);
		$app->setHeader('Cache-Control', 'no-store, no-cache,
must-revalidate, post-check=0, pre-check=0', false);
		$app->setHeader('Pragma', 'no-cache');

		// Check for a valid token. If invalid, send a 403 with the error
message.
		JSession::checkToken('request') or static::sendResponse(new
Exception(JText::_('JINVALID_TOKEN_NOTICE'), 403));

		// Put in a buffer to silence noise.
		ob_start();

		// Remove the script time limit.
		@set_time_limit(0);

		// Get the indexer state.
		$state = FinderIndexer::getState();

		// Reset the batch offset.
		$state->batchOffset = 0;

		// Update the indexer state.
		FinderIndexer::setState($state);

		// Import the finder plugins.
		JPluginHelper::importPlugin('finder');

		/*
		 * We are going to swap out the raw document object with an HTML document
		 * in order to work around some plugins that don't do proper
environment
		 * checks before trying to use HTML document functions.
		 */
		$raw = clone JFactory::getDocument();
		$lang = JFactory::getLanguage();

		// Get the document properties.
		$attributes = array (
			'charset'   => 'utf-8',
			'lineend'   => 'unix',
			'tab'       => '  ',
			'language'  => $lang->getTag(),
			'direction' => $lang->isRtl() ? 'rtl' :
'ltr'
		);

		// Get the HTML document.
		$html = JDocument::getInstance('html', $attributes);

		// Todo: Why is this document fetched and immediately overwritten?
		$doc = JFactory::getDocument();

		// Swap the documents.
		$doc = $html;

		// Get the admin application.
		$admin = clone JFactory::getApplication();

		// Get the site app.
		$site = JApplicationCms::getInstance('site');

		// Swap the app.
		$app = JFactory::getApplication();

		// Todo: Why is the app fetched and immediately overwritten?
		$app = $site;

		// Start the indexer.
		try
		{
			// Trigger the onBeforeIndex event.
			JEventDispatcher::getInstance()->trigger('onBeforeIndex');

			// Trigger the onBuildIndex event.
			JEventDispatcher::getInstance()->trigger('onBuildIndex');

			// Get the indexer state.
			$state = FinderIndexer::getState();
			$state->start = 0;
			$state->complete = 0;

			// Swap the documents back.
			$doc = $raw;

			// Swap the applications back.
			$app = $admin;

			// Log batch completion and memory high-water mark.
			try
			{
				JLog::add('Batch completed, peak memory usage: ' .
number_format(memory_get_peak_usage(true)) . ' bytes',
JLog::INFO);
			}
			catch (RuntimeException $exception)
			{
				// Informational log only
			}

			// Send the response.
			static::sendResponse($state);
		}

		// Catch an exception and return the response.
		catch (Exception $e)
		{
			// Swap the documents back.
			$doc = $raw;

			// Send the response.
			static::sendResponse($e);
		}
	}

	/**
	 * Method to optimize the index and perform any necessary cleanup.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function optimize()
	{
		// We don't want this form to be cached.
		$app = JFactory::getApplication();
		$app->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00
GMT', true);
		$app->setHeader('Last-Modified', gmdate('D, d M Y
H:i:s') . ' GMT', true);
		$app->setHeader('Cache-Control', 'no-store, no-cache,
must-revalidate, post-check=0, pre-check=0', false);
		$app->setHeader('Pragma', 'no-cache');

		// Check for a valid token. If invalid, send a 403 with the error
message.
		JSession::checkToken('request') or static::sendResponse(new
Exception(JText::_('JINVALID_TOKEN_NOTICE'), 403));

		// Put in a buffer to silence noise.
		ob_start();

		// Import the finder plugins.
		JPluginHelper::importPlugin('finder');

		try
		{
			// Optimize the index
			FinderIndexer::getInstance()->optimize();

			// Get the indexer state.
			$state = FinderIndexer::getState();
			$state->start = 0;
			$state->complete = 1;

			// Send the response.
			static::sendResponse($state);
		}

		// Catch an exception and return the response.
		catch (Exception $e)
		{
			static::sendResponse($e);
		}
	}

	/**
	 * Method to handle a send a JSON response. The body parameter
	 * can be an Exception object for when an error has occurred or
	 * a JObject for a good response.
	 *
	 * @param   mixed  $data  JObject on success, Exception on error.
[optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public static function sendResponse($data = null)
	{
		// This method always sends a JSON response
		$app = JFactory::getApplication();
		$app->mimeType = 'application/json';

		$params = JComponentHelper::getParams('com_finder');

		if ($params->get('enable_logging', '0'))
		{
			$options['format'] =
'{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
			$options['text_file'] = 'indexer.php';
			JLog::addLogger($options);
		}

		// Send the assigned error code if we are catching an exception.
		if ($data instanceof Exception)
		{
			try
			{
				JLog::add($data->getMessage(), JLog::ERROR);
			}
			catch (RuntimeException $exception)
			{
				// Informational log only
			}

			$app->setHeader('status', $data->getCode());
		}

		// Create the response object.
		$response = new FinderIndexerResponse($data);

		// Add the buffer.
		$response->buffer = JDEBUG ? ob_get_contents() : ob_end_clean();

		// Send the JSON response.
		$app->setHeader('Content-Type', $app->mimeType . ';
charset=' . $app->charSet);
		$app->sendHeaders();
		echo json_encode($response);

		// Close the application.
		$app->close();
	}
}

/**
 * Finder Indexer JSON Response Class
 *
 * @since  2.5
 */
class FinderIndexerResponse
{
	/**
	 * Class Constructor
	 *
	 * @param   mixed  $state  The processing state for the indexer
	 *
	 * @since   2.5
	 */
	public function __construct($state)
	{
		$params = JComponentHelper::getParams('com_finder');

		if ($params->get('enable_logging', '0'))
		{
			$options['format'] =
'{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
			$options['text_file'] = 'indexer.php';
			JLog::addLogger($options);
		}

		// The old token is invalid so send a new one.
		$this->token = JFactory::getSession()->getFormToken();

		// Check if we are dealing with an error.
		if ($state instanceof Exception)
		{
			// Log the error
			try
			{
				JLog::add($state->getMessage(), JLog::ERROR);
			}
			catch (RuntimeException $exception)
			{
				// Informational log only
			}

			// Prepare the error response.
			$this->error = true;
			$this->header =
JText::_('COM_FINDER_INDEXER_HEADER_ERROR');
			$this->message = $state->getMessage();
		}
		else
		{
			// Prepare the response data.
			$this->batchSize = (int) $state->batchSize;
			$this->batchOffset = (int) $state->batchOffset;
			$this->totalItems = (int) $state->totalItems;

			$this->startTime = $state->startTime;
			$this->endTime = JFactory::getDate()->toSql();

			$this->start = !empty($state->start) ? (int) $state->start : 0;
			$this->complete = !empty($state->complete) ? (int)
$state->complete : 0;

			// Set the appropriate messages.
			if ($this->totalItems <= 0 && $this->complete)
			{
				$this->header =
JText::_('COM_FINDER_INDEXER_HEADER_COMPLETE');
				$this->message =
JText::_('COM_FINDER_INDEXER_MESSAGE_COMPLETE');
			}
			elseif ($this->totalItems <= 0)
			{
				$this->header =
JText::_('COM_FINDER_INDEXER_HEADER_OPTIMIZE');
				$this->message =
JText::_('COM_FINDER_INDEXER_MESSAGE_OPTIMIZE');
			}
			else
			{
				$this->header =
JText::_('COM_FINDER_INDEXER_HEADER_RUNNING');
				$this->message =
JText::_('COM_FINDER_INDEXER_MESSAGE_RUNNING');
			}
		}
	}
}

// Register the error handler.
JError::setErrorHandling(E_ALL, 'callback',
array('FinderControllerIndexer', 'sendResponse'));
PK[0ɭ�ttcontrollers/maps.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Maps controller class for Finder.
 *
 * @since  2.5
 */
class FinderControllerMaps extends JControllerAdmin
{
	/**
	 * Method to get a model object, loading it if required.
	 *
	 * @param   string  $name    The model name. Optional.
	 * @param   string  $prefix  The class prefix. Optional.
	 * @param   array   $config  Configuration array for model. Optional.
	 *
	 * @return  JModelLegacy  The model.
	 *
	 * @since   1.6
	 */
	public function getModel($name = 'Maps', $prefix =
'FinderModel', $config = array('ignore_request' =>
true))
	{
		return parent::getModel($name, $prefix, $config);
	}
}
PK[ŜA
finder.xmlnu�[���<?xml version="1.0"
encoding="utf-8"?>
<extension type="component" version="3.1"
method="upgrade">
	<name>com_finder</name>
	<author>Joomla! Project</author>
	<copyright>(C) 2005 - 2020 Open Source Matters. All rights
reserved.</copyright>
	<creationDate>August 2011</creationDate>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>COM_FINDER_XML_DESCRIPTION</description>
	<menu link="option=com_finder">COM_FINDER</menu>
	<files folder="site">
		<filename>controller.php</filename>
		<filename>finder.php</filename>
		<filename>router.php</filename>
		<folder>controllers</folder>
		<folder>helpers</folder>
		<folder>models</folder>
		<folder>views</folder>
	</files>
	<media destination="com_finder" folder="media">
		<folder>js</folder>
		<folder>css</folder>
	</media>
	<install>
		<sql>
			<file charset="utf8"
driver="mysql">sql/install.mysql.sql</file>
			<file charset="utf8"
driver="postgresql">sql/install.postgresql.sql</file>
		</sql>
	</install>
	<uninstall>
		<sql>
			<file charset="utf8"
driver="mysql">sql/uninstall.mysql.sql</file>
			<file charset="utf8"
driver="postgresql">sql/uninstall.postgresql.sql</file>
		</sql>
	</uninstall>
	<languages folder="site">
		<language
tag="en-GB">language/en-GB.com_finder.ini</language>
	</languages>
	<administration>
		<files folder="admin">
			<filename>access.xml</filename>
			<filename>config.xml</filename>
			<filename>controller.php</filename>
			<filename>finder.php</filename>
			<folder>controllers</folder>
			<folder>helpers</folder>
			<folder>models</folder>
			<folder>sql</folder>
			<folder>tables</folder>
			<folder>views</folder>
		</files>
		<languages folder="admin">
			<language
tag="en-GB">language/en-GB.com_finder.ini</language>
			<language
tag="en-GB">language/en-GB.com_finder.sys.ini</language>
		</languages>
		<menu img="class:finder"
link="option=com_finder">COM_FINDER</menu>
	</administration>
</extension>
PK[f���P	P	helpers/finder.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Helper class for Finder.
 *
 * @since  2.5
 */
class FinderHelper
{
	/**
	 * The extension name.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public static $extension = 'com_finder';

	/**
	 * Configure the Linkbar.
	 *
	 * @param   string  $vName  The name of the active view.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public static function addSubmenu($vName)
	{
		JHtmlSidebar::addEntry(
			JText::_('COM_FINDER_SUBMENU_INDEX'),
			'index.php?option=com_finder&view=index',
			$vName === 'index'
		);
		JHtmlSidebar::addEntry(
			JText::_('COM_FINDER_SUBMENU_MAPS'),
			'index.php?option=com_finder&view=maps',
			$vName === 'maps'
		);
		JHtmlSidebar::addEntry(
			JText::_('COM_FINDER_SUBMENU_FILTERS'),
			'index.php?option=com_finder&view=filters',
			$vName === 'filters'
		);
	}

	/**
	 * Gets the finder system plugin extension id.
	 *
	 * @return  integer  The finder system plugin extension id.
	 *
	 * @since   3.6.0
	 */
	public static function getFinderPluginId()
	{
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select($db->quoteName('extension_id'))
			->from($db->quoteName('#__extensions'))
			->where($db->quoteName('folder') . ' = ' .
$db->quote('content'))
			->where($db->quoteName('element') . ' = ' .
$db->quote('finder'));
		$db->setQuery($query);

		try
		{
			$result = (int) $db->loadResult();
		}
		catch (RuntimeException $e)
		{
			JError::raiseWarning(500, $e->getMessage());
		}

		return $result;
	}

	/**
	 * Gets a list of the actions that can be performed.
	 *
	 * @return  JObject  A JObject containing the allowed actions.
	 *
	 * @since   2.5
	 * @deprecated  3.2  Use JHelperContent::getActions() instead
	 */
	public static function getActions()
	{
		// Log usage of deprecated function
		try
		{
			JLog::add(
				sprintf('%s() is deprecated. Use JHelperContent::getActions() with
new arguments order instead.', __METHOD__),
				JLog::WARNING,
				'deprecated'
			);
		}
		catch (RuntimeException $exception)
		{
			// Informational log only
		}

		// Get list of actions
		return JHelperContent::getActions('com_finder');
	}
}
PK[L&Sߢ�helpers/html/finder.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

use Joomla\Utilities\ArrayHelper;

/**
 * HTML behavior class for Finder.
 *
 * @since  2.5
 */
abstract class JHtmlFinder
{
	/**
	 * Creates a list of types to filter on.
	 *
	 * @return  array  An array containing the types that can be selected.
	 *
	 * @since   2.5
	 */
	public static function typeslist()
	{
		// Load the finder types.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select('DISTINCT t.title AS text, t.id AS value')
			->from($db->quoteName('#__finder_types') . ' AS
t')
			->join('LEFT',
$db->quoteName('#__finder_links') . ' AS l ON l.type_id =
t.id')
			->order('t.title ASC');
		$db->setQuery($query);

		try
		{
			$rows = $db->loadObjectList();
		}
		catch (RuntimeException $e)
		{
			return array();
		}

		// Compile the options.
		$options = array();

		$lang = JFactory::getLanguage();

		foreach ($rows as $row)
		{
			$key       =
$lang->hasKey(FinderHelperLanguage::branchPlural($row->text)) ?
FinderHelperLanguage::branchPlural($row->text) : $row->text;
			$options[] = JHtml::_('select.option', $row->value,
JText::sprintf('COM_FINDER_ITEM_X_ONLY', JText::_($key)));
		}

		return $options;
	}

	/**
	 * Creates a list of maps.
	 *
	 * @return  array  An array containing the maps that can be selected.
	 *
	 * @since   2.5
	 */
	public static function mapslist()
	{
		// Load the finder types.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select($db->quoteName('title', 'text'))
			->select($db->quoteName('id', 'value'))
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = 1');
		$db->setQuery($query);

		try
		{
			$branches = $db->loadObjectList();
		}
		catch (RuntimeException $e)
		{
			JError::raiseWarning(500, $db->getMessage());
		}

		// Translate.
		$lang = JFactory::getLanguage();

		foreach ($branches as $branch)
		{
			$key = FinderHelperLanguage::branchPlural($branch->text);
			$branch->translatedText = $lang->hasKey($key) ? JText::_($key) :
$branch->text;
		}

		// Order by title.
		$branches = ArrayHelper::sortObjects($branches,
'translatedText', 1, true, true);

		// Compile the options.
		$options = array();
		$options[] = JHtml::_('select.option', '',
JText::_('COM_FINDER_MAPS_SELECT_BRANCH'));

		// Convert the values to options.
		foreach ($branches as $branch)
		{
			$options[] = JHtml::_('select.option', $branch->value,
$branch->translatedText);
		}

		return $options;
	}

	/**
	 * Creates a list of published states.
	 *
	 * @return  array  An array containing the states that can be selected.
	 *
	 * @since   2.5
	 */
	public static function statelist()
	{
		return array(
			JHtml::_('select.option', '1',
JText::sprintf('COM_FINDER_ITEM_X_ONLY',
JText::_('JPUBLISHED'))),
			JHtml::_('select.option', '0',
JText::sprintf('COM_FINDER_ITEM_X_ONLY',
JText::_('JUNPUBLISHED')))
		);
	}
}
PK[X��!�U�Uhelpers/indexer/adapter.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Utilities\ArrayHelper;

JLoader::register('FinderIndexer', __DIR__ .
'/indexer.php');
JLoader::register('FinderIndexerHelper', __DIR__ .
'/helper.php');
JLoader::register('FinderIndexerResult', __DIR__ .
'/result.php');
JLoader::register('FinderIndexerTaxonomy', __DIR__ .
'/taxonomy.php');

/**
 * Prototype adapter class for the Finder indexer package.
 *
 * @since  2.5
 */
abstract class FinderIndexerAdapter extends JPlugin
{
	/**
	 * The context is somewhat arbitrary but it must be unique or there will
be
	 * conflicts when managing plugin/indexer state. A good best practice is
to
	 * use the plugin name suffix as the context. For example, if the plugin
is
	 * named 'plgFinderContent', the context could be
'Content'.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $context;

	/**
	 * The extension name.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $extension;

	/**
	 * The sublayout to use when rendering the results.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $layout;

	/**
	 * The mime type of the content the adapter indexes.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $mime;

	/**
	 * The access level of an item before save.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	protected $old_access;

	/**
	 * The access level of a category before save.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	protected $old_cataccess;

	/**
	 * The type of content the adapter indexes.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $type_title;

	/**
	 * The type id of the content.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	protected $type_id;

	/**
	 * The database object.
	 *
	 * @var    object
	 * @since  2.5
	 */
	protected $db;

	/**
	 * The table name.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $table;

	/**
	 * The indexer object.
	 *
	 * @var    FinderIndexer
	 * @since  3.0
	 */
	protected $indexer;

	/**
	 * The field the published state is stored in.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $state_field = 'state';

	/**
	 * Method to instantiate the indexer adapter.
	 *
	 * @param   object  $subject  The object to observe.
	 * @param   array   $config   An array that holds the plugin
configuration.
	 *
	 * @since   2.5
	 */
	public function __construct(&$subject, $config)
	{
		// Get the database object.
		$this->db = JFactory::getDbo();

		// Call the parent constructor.
		parent::__construct($subject, $config);

		// Get the type id.
		$this->type_id = $this->getTypeId();

		// Add the content type if it doesn't exist and is set.
		if (empty($this->type_id) && !empty($this->type_title))
		{
			$this->type_id =
FinderIndexerHelper::addContentType($this->type_title, $this->mime);
		}

		// Check for a layout override.
		if ($this->params->get('layout'))
		{
			$this->layout = $this->params->get('layout');
		}

		// Get the indexer object
		$this->indexer = FinderIndexer::getInstance();
	}

	/**
	 * Method to get the adapter state and push it into the indexer.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 * @throws  Exception on error.
	 */
	public function onStartIndex()
	{
		// Get the indexer state.
		$iState = FinderIndexer::getState();

		// Get the number of content items.
		$total = (int) $this->getContentCount();

		// Add the content count to the total number of items.
		$iState->totalItems += $total;

		// Populate the indexer state information for the adapter.
		$iState->pluginState[$this->context]['total'] = $total;
		$iState->pluginState[$this->context]['offset'] = 0;

		// Set the indexer state.
		FinderIndexer::setState($iState);
	}

	/**
	 * Method to prepare for the indexer to be run. This method will often
	 * be used to include dependencies and things of that nature.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on error.
	 */
	public function onBeforeIndex()
	{
		// Get the indexer and adapter state.
		$iState = FinderIndexer::getState();
		$aState = $iState->pluginState[$this->context];

		// Check the progress of the indexer and the adapter.
		if ($iState->batchOffset == $iState->batchSize ||
$aState['offset'] == $aState['total'])
		{
			return true;
		}

		// Run the setup method.
		return $this->setup();
	}

	/**
	 * Method to index a batch of content items. This method can be called by
	 * the indexer many times throughout the indexing process depending on how
	 * much content is available for indexing. It is important to track the
	 * progress correctly so we can display it to the user.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on error.
	 */
	public function onBuildIndex()
	{
		// Get the indexer and adapter state.
		$iState = FinderIndexer::getState();
		$aState = $iState->pluginState[$this->context];

		// Check the progress of the indexer and the adapter.
		if ($iState->batchOffset == $iState->batchSize ||
$aState['offset'] == $aState['total'])
		{
			return true;
		}

		// Get the batch offset and size.
		$offset = (int) $aState['offset'];
		$limit = (int) ($iState->batchSize - $iState->batchOffset);

		// Get the content items to index.
		$items = $this->getItems($offset, $limit);

		// Iterate through the items and index them.
		for ($i = 0, $n = count($items); $i < $n; $i++)
		{
			// Index the item.
			$this->index($items[$i]);

			// Adjust the offsets.
			$offset++;
			$iState->batchOffset++;
			$iState->totalItems--;
		}

		// Update the indexer state.
		$aState['offset'] = $offset;
		$iState->pluginState[$this->context] = $aState;
		FinderIndexer::setState($iState);

		return true;
	}

	/**
	 * Method to change the value of a content item's property in the
links
	 * table. This is used to synchronize published and access states that
	 * are changed when not editing an item directly.
	 *
	 * @param   string   $id        The ID of the item to change.
	 * @param   string   $property  The property that is being changed.
	 * @param   integer  $value     The new value of that property.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function change($id, $property, $value)
	{
		// Check for a property we know how to handle.
		if ($property !== 'state' && $property !==
'access')
		{
			return true;
		}

		// Get the URL for the content id.
		$item = $this->db->quote($this->getUrl($id, $this->extension,
$this->layout));

		// Update the content items.
		$query = $this->db->getQuery(true)
			->update($this->db->quoteName('#__finder_links'))
			->set($this->db->quoteName($property) . ' = ' . (int)
$value)
			->where($this->db->quoteName('url') . ' = '
. $item);
		$this->db->setQuery($query);
		$this->db->execute();

		return true;
	}

	/**
	 * Method to index an item.
	 *
	 * @param   FinderIndexerResult  $item  The item to index as a
FinderIndexerResult object.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	abstract protected function index(FinderIndexerResult $item);

	/**
	 * Method to reindex an item.
	 *
	 * @param   integer  $id  The ID of the item to reindex.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function reindex($id)
	{
		// Run the setup method.
		$this->setup();

		// Remove the old item.
		$this->remove($id);

		// Get the item.
		$item = $this->getItem($id);

		// Index the item.
		$this->index($item);
	}

	/**
	 * Method to remove an item from the index.
	 *
	 * @param   string  $id  The ID of the item to remove.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function remove($id)
	{
		// Get the item's URL
		$url = $this->db->quote($this->getUrl($id, $this->extension,
$this->layout));

		// Get the link ids for the content items.
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('link_id'))
			->from($this->db->quoteName('#__finder_links'))
			->where($this->db->quoteName('url') . ' = '
. $url);
		$this->db->setQuery($query);
		$items = $this->db->loadColumn();

		// Check the items.
		if (empty($items))
		{
			return true;
		}

		// Remove the items.
		foreach ($items as $item)
		{
			$this->indexer->remove($item);
		}

		return true;
	}

	/**
	 * Method to setup the adapter before indexing.
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	abstract protected function setup();

	/**
	 * Method to update index data on category access level changes
	 *
	 * @param   JTable  $row  A JTable object
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function categoryAccessChange($row)
	{
		$query = clone $this->getStateQuery();
		$query->where('c.id = ' . (int) $row->id);

		// Get the access level.
		$this->db->setQuery($query);
		$items = $this->db->loadObjectList();

		// Adjust the access level for each item within the category.
		foreach ($items as $item)
		{
			// Set the access level.
			$temp = max($item->access, $row->access);

			// Update the item.
			$this->change((int) $item->id, 'access', $temp);

			// Reindex the item
			$this->reindex($row->id);
		}
	}

	/**
	 * Method to update index data on category access level changes
	 *
	 * @param   array    $pks    A list of primary key ids of the content that
has changed state.
	 * @param   integer  $value  The value of the state that the content has
been changed to.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function categoryStateChange($pks, $value)
	{
		/*
		 * The item's published state is tied to the category
		 * published state so we need to look up all published states
		 * before we change anything.
		 */
		foreach ($pks as $pk)
		{
			$query = clone $this->getStateQuery();
			$query->where('c.id = ' . (int) $pk);

			// Get the published states.
			$this->db->setQuery($query);
			$items = $this->db->loadObjectList();

			// Adjust the state for each item within the category.
			foreach ($items as $item)
			{
				// Translate the state.
				$temp = $this->translateState($item->state, $value);

				// Update the item.
				$this->change($item->id, 'state', $temp);

				// Reindex the item
				$this->reindex($item->id);
			}
		}
	}

	/**
	 * Method to check the existing access level for categories
	 *
	 * @param   JTable  $row  A JTable object
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function checkCategoryAccess($row)
	{
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('access'))
			->from($this->db->quoteName('#__categories'))
			->where($this->db->quoteName('id') . ' = '
. (int) $row->id);
		$this->db->setQuery($query);

		// Store the access level to determine if it changes
		$this->old_cataccess = $this->db->loadResult();
	}

	/**
	 * Method to check the existing access level for items
	 *
	 * @param   JTable  $row  A JTable object
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function checkItemAccess($row)
	{
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('access'))
			->from($this->db->quoteName($this->table))
			->where($this->db->quoteName('id') . ' = '
. (int) $row->id);
		$this->db->setQuery($query);

		// Store the access level to determine if it changes
		$this->old_access = $this->db->loadResult();
	}

	/**
	 * Method to get the number of content items available to index.
	 *
	 * @return  integer  The number of content items available to index.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getContentCount()
	{
		$return = 0;

		// Get the list query.
		$query = $this->getListQuery();

		// Check if the query is valid.
		if (empty($query))
		{
			return $return;
		}

		// Tweak the SQL query to make the total lookup faster.
		if ($query instanceof JDatabaseQuery)
		{
			$query = clone $query;
			$query->clear('select')
				->select('COUNT(*)')
				->clear('order');
		}

		// Get the total number of content items to index.
		$this->db->setQuery($query);

		return (int) $this->db->loadResult();
	}

	/**
	 * Method to get a content item to index.
	 *
	 * @param   integer  $id  The id of the content item.
	 *
	 * @return  FinderIndexerResult  A FinderIndexerResult object.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getItem($id)
	{
		// Get the list query and add the extra WHERE clause.
		$query = $this->getListQuery();
		$query->where('a.id = ' . (int) $id);

		// Get the item to index.
		$this->db->setQuery($query);
		$row = $this->db->loadAssoc();

		// Convert the item to a result object.
		$item = ArrayHelper::toObject((array) $row,
'FinderIndexerResult');

		// Set the item type.
		$item->type_id = $this->type_id;

		// Set the item layout.
		$item->layout = $this->layout;

		return $item;
	}

	/**
	 * Method to get a list of content items to index.
	 *
	 * @param   integer         $offset  The list offset.
	 * @param   integer         $limit   The list limit.
	 * @param   JDatabaseQuery  $query   A JDatabaseQuery object. [optional]
	 *
	 * @return  array  An array of FinderIndexerResult objects.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getItems($offset, $limit, $query = null)
	{
		$items = array();

		// Get the content items to index.
		$this->db->setQuery($this->getListQuery($query), $offset,
$limit);
		$rows = $this->db->loadAssocList();

		// Convert the items to result objects.
		foreach ($rows as $row)
		{
			// Convert the item to a result object.
			$item = ArrayHelper::toObject((array) $row,
'FinderIndexerResult');

			// Set the item type.
			$item->type_id = $this->type_id;

			// Set the mime type.
			$item->mime = $this->mime;

			// Set the item layout.
			$item->layout = $this->layout;

			// Set the extension if present
			if (isset($row->extension))
			{
				$item->extension = $row->extension;
			}

			// Add the item to the stack.
			$items[] = $item;
		}

		return $items;
	}

	/**
	 * Method to get the SQL query used to retrieve the list of content items.
	 *
	 * @param   mixed  $query  A JDatabaseQuery object. [optional]
	 *
	 * @return  JDatabaseQuery  A database object.
	 *
	 * @since   2.5
	 */
	protected function getListQuery($query = null)
	{
		// Check if we can use the supplied SQL query.
		return $query instanceof JDatabaseQuery ? $query :
$this->db->getQuery(true);
	}

	/**
	 * Method to get the plugin type
	 *
	 * @param   integer  $id  The plugin ID
	 *
	 * @return  string  The plugin type
	 *
	 * @since   2.5
	 */
	protected function getPluginType($id)
	{
		// Prepare the query
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('element'))
			->from($this->db->quoteName('#__extensions'))
			->where($this->db->quoteName('extension_id') . '
= ' . (int) $id);
		$this->db->setQuery($query);

		return $this->db->loadResult();
	}

	/**
	 * Method to get a SQL query to load the published and access states for
	 * an article and category.
	 *
	 * @return  JDatabaseQuery  A database object.
	 *
	 * @since   2.5
	 */
	protected function getStateQuery()
	{
		$query = $this->db->getQuery(true);

		// Item ID
		$query->select('a.id');

		// Item and category published state
		$query->select('a.' . $this->state_field . ' AS
state, c.published AS cat_state');

		// Item and category access levels
		$query->select('a.access, c.access AS cat_access')
			->from($this->table . ' AS a')
			->join('LEFT', '#__categories AS c ON c.id =
a.catid');

		return $query;
	}

	/**
	 * Method to get the query clause for getting items to update by time.
	 *
	 * @param   string  $time  The modified timestamp.
	 *
	 * @return  JDatabaseQuery  A database object.
	 *
	 * @since   2.5
	 */
	protected function getUpdateQueryByTime($time)
	{
		// Build an SQL query based on the modified time.
		$query = $this->db->getQuery(true)
			->where('a.modified >= ' .
$this->db->quote($time));

		return $query;
	}

	/**
	 * Method to get the query clause for getting items to update by id.
	 *
	 * @param   array  $ids  The ids to load.
	 *
	 * @return  JDatabaseQuery  A database object.
	 *
	 * @since   2.5
	 */
	protected function getUpdateQueryByIds($ids)
	{
		// Build an SQL query based on the item ids.
		$query = $this->db->getQuery(true)
			->where('a.id IN(' . implode(',', $ids) .
')');

		return $query;
	}

	/**
	 * Method to get the type id for the adapter content.
	 *
	 * @return  integer  The numeric type id for the content.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getTypeId()
	{
		// Get the type id from the database.
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('id'))
			->from($this->db->quoteName('#__finder_types'))
			->where($this->db->quoteName('title') . ' =
' . $this->db->quote($this->type_title));
		$this->db->setQuery($query);

		return (int) $this->db->loadResult();
	}

	/**
	 * Method to get the URL for the item. The URL is how we look up the link
	 * in the Finder index.
	 *
	 * @param   integer  $id         The id of the item.
	 * @param   string   $extension  The extension the category is in.
	 * @param   string   $view       The view for the URL.
	 *
	 * @return  string  The URL of the item.
	 *
	 * @since   2.5
	 */
	protected function getUrl($id, $extension, $view)
	{
		return 'index.php?option=' . $extension .
'&view=' . $view . '&id=' . $id;
	}

	/**
	 * Method to get the page title of any menu item that is linked to the
	 * content item, if it exists and is set.
	 *
	 * @param   string  $url  The URL of the item.
	 *
	 * @return  mixed  The title on success, null if not found.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getItemMenuTitle($url)
	{
		$return = null;

		// Set variables
		$user = JFactory::getUser();
		$groups = implode(',', $user->getAuthorisedViewLevels());

		// Build a query to get the menu params.
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName('params'))
			->from($this->db->quoteName('#__menu'))
			->where($this->db->quoteName('link') . ' =
' . $this->db->quote($url))
			->where($this->db->quoteName('published') . ' =
1')
			->where($this->db->quoteName('access') . ' IN
(' . $groups . ')');

		// Get the menu params from the database.
		$this->db->setQuery($query);
		$params = $this->db->loadResult();

		// Check the results.
		if (empty($params))
		{
			return $return;
		}

		// Instantiate the params.
		$params = json_decode($params);

		// Get the page title if it is set.
		if (isset($params->page_title) && $params->page_title)
		{
			$return = $params->page_title;
		}

		return $return;
	}

	/**
	 * Method to update index data on access level changes
	 *
	 * @param   JTable  $row  A JTable object
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function itemAccessChange($row)
	{
		$query = clone $this->getStateQuery();
		$query->where('a.id = ' . (int) $row->id);

		// Get the access level.
		$this->db->setQuery($query);
		$item = $this->db->loadObject();

		// Set the access level.
		$temp = max($row->access, $item->cat_access);

		// Update the item.
		$this->change((int) $row->id, 'access', $temp);
	}

	/**
	 * Method to update index data on published state changes
	 *
	 * @param   array    $pks    A list of primary key ids of the content that
has changed state.
	 * @param   integer  $value  The value of the state that the content has
been changed to.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function itemStateChange($pks, $value)
	{
		/*
		 * The item's published state is tied to the category
		 * published state so we need to look up all published states
		 * before we change anything.
		 */
		foreach ($pks as $pk)
		{
			$query = clone $this->getStateQuery();
			$query->where('a.id = ' . (int) $pk);

			// Get the published states.
			$this->db->setQuery($query);
			$item = $this->db->loadObject();

			// Translate the state.
			$temp = $this->translateState($value, $item->cat_state);

			// Update the item.
			$this->change($pk, 'state', $temp);

			// Reindex the item
			$this->reindex($pk);
		}
	}

	/**
	 * Method to update index data when a plugin is disabled
	 *
	 * @param   array  $pks  A list of primary key ids of the content that has
changed state.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function pluginDisable($pks)
	{
		// Since multiple plugins may be disabled at a time, we need to check
first
		// that we're handling the appropriate one for the context
		foreach ($pks as $pk)
		{
			if ($this->getPluginType($pk) == strtolower($this->context))
			{
				// Get all of the items to unindex them
				$query = clone $this->getStateQuery();
				$this->db->setQuery($query);
				$items = $this->db->loadColumn();

				// Remove each item
				foreach ($items as $item)
				{
					$this->remove($item);
				}
			}
		}
	}

	/**
	 * Method to translate the native content states into states that the
	 * indexer can use.
	 *
	 * @param   integer  $item      The item state.
	 * @param   integer  $category  The category state. [optional]
	 *
	 * @return  integer  The translated indexer state.
	 *
	 * @since   2.5
	 */
	protected function translateState($item, $category = null)
	{
		// If category is present, factor in its states as well
		if ($category !== null && $category == 0)
		{
			$item = 0;
		}

		// Translate the state
		switch ($item)
		{
			// Published and archived items only should return a published state
			case 1;
			case 2:
				return 1;

			// All other states should return an unpublished state
			default:
			case 0:
				return 0;
		}
	}
}
PK[�o���K�K
helpers/indexer/driver/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

jimport('joomla.filesystem.file');

/**
 * Indexer class supporting MySQL(i) for the Finder indexer package.
 *
 * The indexer class provides the core functionality of the Finder
 * search engine. It is responsible for adding and updating the
 * content links table; extracting and scoring tokens; and maintaining
 * all referential information for the content.
 *
 * Note: All exceptions thrown from within this class should be caught
 * by the controller.
 *
 * @since  3.0
 */
class FinderIndexerDriverMysql extends FinderIndexer
{
	/**
	 * Method to index a content item.
	 *
	 * @param   FinderIndexerResult  $item    The content item to index.
	 * @param   string               $format  The format of the content.
[optional]
	 *
	 * @return  integer  The ID of the record in the links table.
	 *
	 * @since   3.0
	 * @throws  Exception on database error.
	 */
	public function index($item, $format = 'html')
	{
		// Mark beforeIndexing in the profiler.
		static::$profiler ?
static::$profiler->mark('beforeIndexing') : null;
		$db = $this->db;
		$nd = $db->getNullDate();

		// Check if the item is in the database.
		$query = $db->getQuery(true)
			->select($db->quoteName('link_id') . ', ' .
$db->quoteName('md5sum'))
			->from($db->quoteName('#__finder_links'))
			->where($db->quoteName('url') . ' = ' .
$db->quote($item->url));

		// Load the item  from the database.
		$db->setQuery($query);
		$link = $db->loadObject();

		// Get the indexer state.
		$state = static::getState();

		// Get the signatures of the item.
		$curSig = static::getSignature($item);
		$oldSig = isset($link->md5sum) ? $link->md5sum : null;

		// Get the other item information.
		$linkId = empty($link->link_id) ? null : $link->link_id;
		$isNew = empty($link->link_id) ? true : false;

		// Check the signatures. If they match, the item is up to date.
		if (!$isNew && $curSig == $oldSig)
		{
			return $linkId;
		}

		/*
		 * If the link already exists, flush all the term maps for the item.
		 * Maps are stored in 16 tables so we need to iterate through and flush
		 * each table one at a time.
		 */
		if (!$isNew)
		{
			for ($i = 0; $i <= 15; $i++)
			{
				// Flush the maps for the link.
				$query->clear()
					->delete($db->quoteName('#__finder_links_terms' .
dechex($i)))
					->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
				$db->setQuery($query);
				$db->execute();
			}

			// Remove the taxonomy maps.
			FinderIndexerTaxonomy::removeMaps($linkId);
		}

		// Mark afterUnmapping in the profiler.
		static::$profiler ?
static::$profiler->mark('afterUnmapping') : null;

		// Perform cleanup on the item data.
		$item->publish_start_date = (int) $item->publish_start_date != 0 ?
$item->publish_start_date : $nd;
		$item->publish_end_date = (int) $item->publish_end_date != 0 ?
$item->publish_end_date : $nd;
		$item->start_date = (int) $item->start_date != 0 ?
$item->start_date : $nd;
		$item->end_date = (int) $item->end_date != 0 ? $item->end_date :
$nd;

		// Prepare the item description.
		$item->description = FinderIndexerHelper::parse($item->summary);

		/*
		 * Now, we need to enter the item into the links table. If the item
		 * already exists in the database, we need to use an UPDATE query.
		 * Otherwise, we need to use an INSERT to get the link id back.
		 */

		if ($isNew)
		{
			$columnsArray = array(
				$db->quoteName('url'),
$db->quoteName('route'), $db->quoteName('title'),
$db->quoteName('description'),
				$db->quoteName('indexdate'),
$db->quoteName('published'),
$db->quoteName('state'),
$db->quoteName('access'),
				$db->quoteName('language'),
$db->quoteName('type_id'),
$db->quoteName('object'),
$db->quoteName('publish_start_date'),
				$db->quoteName('publish_end_date'),
$db->quoteName('start_date'),
$db->quoteName('end_date'),
$db->quoteName('list_price'),
				$db->quoteName('sale_price')
			);

			// Insert the link.
			$query->clear()
				->insert($db->quoteName('#__finder_links'))
				->columns($columnsArray)
				->values(
					$db->quote($item->url) . ', '
					. $db->quote($item->route) . ', '
					. $db->quote($item->title) . ', '
					. $db->quote($item->description) . ', '
					. $query->currentTimestamp() . ', '
					. '1, '
					. (int) $item->state . ', '
					. (int) $item->access . ', '
					. $db->quote($item->language) . ', '
					. (int) $item->type_id . ', '
					. $db->quote(serialize($item)) . ', '
					. $db->quote($item->publish_start_date) . ', '
					. $db->quote($item->publish_end_date) . ', '
					. $db->quote($item->start_date) . ', '
					. $db->quote($item->end_date) . ', '
					. (double) ($item->list_price ?: 0) . ', '
					. (double) ($item->sale_price ?: 0)
				);
			$db->setQuery($query);
			$db->execute();

			// Get the link id.
			$linkId = (int) $db->insertid();
		}
		else
		{
			// Update the link.
			$query->clear()
				->update($db->quoteName('#__finder_links'))
				->set($db->quoteName('route') . ' = ' .
$db->quote($item->route))
				->set($db->quoteName('title') . ' = ' .
$db->quote($item->title))
				->set($db->quoteName('description') . ' = ' .
$db->quote($item->description))
				->set($db->quoteName('indexdate') . ' = ' .
$query->currentTimestamp())
				->set($db->quoteName('state') . ' = ' . (int)
$item->state)
				->set($db->quoteName('access') . ' = ' .
(int) $item->access)
				->set($db->quoteName('language') . ' = ' .
$db->quote($item->language))
				->set($db->quoteName('type_id') . ' = ' .
(int) $item->type_id)
				->set($db->quoteName('object') . ' = ' .
$db->quote(serialize($item)))
				->set($db->quoteName('publish_start_date') . ' =
' . $db->quote($item->publish_start_date))
				->set($db->quoteName('publish_end_date') . ' =
' . $db->quote($item->publish_end_date))
				->set($db->quoteName('start_date') . ' = ' .
$db->quote($item->start_date))
				->set($db->quoteName('end_date') . ' = ' .
$db->quote($item->end_date))
				->set($db->quoteName('list_price') . ' = ' .
(double) ($item->list_price ?: 0))
				->set($db->quoteName('sale_price') . ' = ' .
(double) ($item->sale_price ?: 0))
				->where('link_id = ' . (int) $linkId);
			$db->setQuery($query);
			$db->execute();
		}

		// Set up the variables we will need during processing.
		$count = 0;

		// Mark afterLinking in the profiler.
		static::$profiler ? static::$profiler->mark('afterLinking')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		/*
		 * Process the item's content. The items can customize their
		 * processing instructions to define extra properties to process
		 * or rearrange how properties are weighted.
		 */
		foreach ($item->getInstructions() as $group => $properties)
		{
			// Iterate through the properties of the group.
			foreach ($properties as $property)
			{
				// Check if the property exists in the item.
				if (empty($item->$property))
				{
					continue;
				}

				// Tokenize the property.
				if (is_array($item->$property))
				{
					// Tokenize an array of content and add it to the database.
					foreach ($item->$property as $ip)
					{
						/*
						 * If the group is path, we need to a few extra processing
						 * steps to strip the extension and convert slashes and dashes
						 * to spaces.
						 */
						if ($group === static::PATH_CONTEXT)
						{
							$ip = JFile::stripExt($ip);
							$ip = str_replace(array('/', '-'), '
', $ip);
						}

						// Tokenize a string of content and add it to the database.
						$count += $this->tokenizeToDb($ip, $group, $item->language,
$format);

						// Check if we're approaching the memory limit of the token
table.
						if ($count >
static::$state->options->get('memory_table_limit', 30000))
						{
							$this->toggleTables(false);
						}
					}
				}
				else
				{
					/*
					 * If the group is path, we need to a few extra processing
					 * steps to strip the extension and convert slashes and dashes
					 * to spaces.
					 */
					if ($group === static::PATH_CONTEXT)
					{
						$item->$property = JFile::stripExt($item->$property);
						$item->$property = str_replace('/', ' ',
$item->$property);
						$item->$property = str_replace('-', ' ',
$item->$property);
					}

					// Tokenize a string of content and add it to the database.
					$count += $this->tokenizeToDb($item->$property, $group,
$item->language, $format);

					// Check if we're approaching the memory limit of the token
table.
					if ($count >
static::$state->options->get('memory_table_limit', 30000))
					{
						$this->toggleTables(false);
					}
				}
			}
		}

		/*
		 * Process the item's taxonomy. The items can customize their
		 * taxonomy mappings to define extra properties to map.
		 */
		foreach ($item->getTaxonomy() as $branch => $nodes)
		{
			// Iterate through the nodes and map them to the branch.
			foreach ($nodes as $node)
			{
				// Add the node to the tree.
				$nodeId = FinderIndexerTaxonomy::addNode($branch, $node->title,
$node->state, $node->access);

				// Add the link => node map.
				FinderIndexerTaxonomy::addMap($linkId, $nodeId);
			}
		}

		// Mark afterProcessing in the profiler.
		static::$profiler ?
static::$profiler->mark('afterProcessing') : null;

		/*
		 * At this point, all of the item's content has been parsed,
tokenized
		 * and inserted into the #__finder_tokens table. Now, we need to
		 * aggregate all the data into that table into a more usable form. The
		 * aggregated data will be inserted into #__finder_tokens_aggregate
		 * table.
		 */
		$query = 'INSERT INTO ' .
$db->quoteName('#__finder_tokens_aggregate') .
			' (' . $db->quoteName('term_id') .
			', ' . $db->quoteName('map_suffix') .
				', ' . $db->quoteName('term') .
			', ' . $db->quoteName('stem') .
			', ' . $db->quoteName('common') .
			', ' . $db->quoteName('phrase') .
			', ' . $db->quoteName('term_weight') .
			', ' . $db->quoteName('context') .
			', ' . $db->quoteName('context_weight') .
			', ' . $db->quoteName('total_weight') .
				', ' . $db->quoteName('language') .
')' .
			' SELECT' .
			' COALESCE(t.term_id, 0), \'\', t1.term, t1.stem,
t1.common, t1.phrase, t1.weight, t1.context,' .
			' ROUND( t1.weight * COUNT( t2.term ) * %F, 8 ) AS context_weight,
0, t1.language' .
			' FROM (' .
			'   SELECT DISTINCT t1.term, t1.stem, t1.common, t1.phrase,
t1.weight, t1.context, t1.language' .
			'   FROM ' . $db->quoteName('#__finder_tokens') .
' AS t1' .
			'   WHERE t1.context = %d' .
			' ) AS t1' .
			' JOIN ' . $db->quoteName('#__finder_tokens') .
' AS t2 ON t2.term = t1.term' .
			' LEFT JOIN ' . $db->quoteName('#__finder_terms')
. ' AS t ON t.term = t1.term' .
			' WHERE t2.context = %d' .
			' GROUP BY t1.term, t.term_id, t1.term, t1.stem, t1.common,
t1.phrase, t1.weight, t1.context, t1.language' .
			' ORDER BY t1.term DESC';

		// Iterate through the contexts and aggregate the tokens per context.
		foreach ($state->weights as $context => $multiplier)
		{
			// Run the query to aggregate the tokens for this context..
			$db->setQuery(sprintf($query, $multiplier, $context, $context));
			$db->execute();
		}

		// Mark afterAggregating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterAggregating') : null;

		/*
		 * When we pulled down all of the aggregate data, we did a LEFT JOIN
		 * over the terms table to try to find all the term ids that
		 * already exist for our tokens. If any of the rows in the aggregate
		 * table have a term of 0, then no term record exists for that
		 * term so we need to add it to the terms table.
		 */
		$db->setQuery(
			'INSERT IGNORE INTO ' .
$db->quoteName('#__finder_terms') .
			' (' . $db->quoteName('term') .
			', ' . $db->quoteName('stem') .
			', ' . $db->quoteName('common') .
			', ' . $db->quoteName('phrase') .
			', ' . $db->quoteName('weight') .
			', ' . $db->quoteName('soundex') .
			', ' . $db->quoteName('language') . ')'
.
			' SELECT ta.term, ta.stem, ta.common, ta.phrase, ta.term_weight,
SOUNDEX(ta.term), ta.language' .
			' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') . ' AS
ta' .
			' WHERE ta.term_id = 0' .
			' GROUP BY ta.term, ta.stem, ta.common, ta.phrase, ta.term_weight,
SOUNDEX(ta.term), ta.language'
		);
		$db->execute();

		/*
		 * Now, we just inserted a bunch of new records into the terms table
		 * so we need to go back and update the aggregate table with all the
		 * new term ids.
		 */
		$query = $db->getQuery(true)
			->update($db->quoteName('#__finder_tokens_aggregate') .
' AS ta')
			->join('INNER',
$db->quoteName('#__finder_terms') . ' AS t ON t.term =
ta.term')
			->set('ta.term_id = t.term_id')
			->where('ta.term_id = 0');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * After we've made sure that all of the terms are in the terms
table
		 * and the aggregate table has the correct term ids, we need to update
		 * the links counter for each term by one.
		 */
		$query->clear()
			->update($db->quoteName('#__finder_terms') . ' AS
t')
			->join('INNER',
$db->quoteName('#__finder_tokens_aggregate') . ' AS ta ON
ta.term_id = t.term_id')
			->set('t.' . $db->quoteName('links') . '
= t.links + 1');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * Before we can insert all of the mapping rows, we have to figure out
		 * which mapping table the rows need to be inserted into. The mapping
		 * table for each term is based on the first character of the md5 of
		 * the first character of the term. In php, it would be expressed as
		 * substr(md5(substr($token, 0, 1)), 0, 1)
		 */
		$query->clear()
			->update($db->quoteName('#__finder_tokens_aggregate'))
			->set($db->quoteName('map_suffix') . ' =
SUBSTR(MD5(SUBSTR(' . $db->quoteName('term') . ', 1,
1)), 1, 1)');
		$db->setQuery($query);
		$db->execute();

		/*
		 * At this point, the aggregate table contains a record for each
		 * term in each context. So, we're going to pull down all of that
		 * data while grouping the records by term and add all of the
		 * sub-totals together to arrive at the final total for each token for
		 * this link. Then, we insert all of that data into the appropriate
		 * mapping table.
		 */
		for ($i = 0; $i <= 15; $i++)
		{
			// Get the mapping table suffix.
			$suffix = dechex($i);

			/*
			 * We have to run this query 16 times, one for each link => term
			 * mapping table.
			 */
			$db->setQuery(
				'INSERT INTO ' .
$db->quoteName('#__finder_links_terms' . $suffix) .
				' (' . $db->quoteName('link_id') .
				', ' . $db->quoteName('term_id') .
				', ' . $db->quoteName('weight') . ')'
.
				' SELECT ' . (int) $linkId . ', ' .
$db->quoteName('term_id') . ',' .
				' ROUND(SUM(' . $db->quoteName('context_weight')
. '), 8)' .
				' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') .
				' WHERE ' . $db->quoteName('map_suffix') .
' = ' . $db->quote($suffix) .
				' GROUP BY ' . $db->quoteName('term') . ',
' . $db->quoteName('term_id') .
				' ORDER BY ' . $db->quoteName('term') . '
DESC'
			);
			$db->execute();
		}

		// Mark afterMapping in the profiler.
		static::$profiler ? static::$profiler->mark('afterMapping')
: null;

		// Update the signature.
		$query->clear()
			->update($db->quoteName('#__finder_links'))
			->set($db->quoteName('md5sum') . ' = ' .
$db->quote($curSig))
			->where($db->quoteName('link_id') . ' = ' .
$db->quote($linkId));
		$db->setQuery($query);
		$db->execute();

		// Mark afterSigning in the profiler.
		static::$profiler ? static::$profiler->mark('afterSigning')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		// Toggle the token tables back to memory tables.
		$this->toggleTables(true);

		// Mark afterTruncating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterTruncating') : null;

		return $linkId;
	}

	/**
	 * Method to optimize the index. We use this method to remove unused terms
	 * and any other optimizations that might be necessary.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0
	 * @throws  Exception on database error.
	 */
	public function optimize()
	{
		// Get the database object.
		$db = $this->db;
		$query = $db->getQuery(true);

		// Delete all orphaned terms.
		$query->delete($db->quoteName('#__finder_terms'))
			->where($db->quoteName('links') . ' <= 0');
		$db->setQuery($query);
		$db->execute();

		// Optimize the links table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_links'));
		$db->execute();

		for ($i = 0; $i <= 15; $i++)
		{
			// Optimize the terms mapping table.
			$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_links_terms' . dechex($i)));
			$db->execute();
		}

		// Optimize the filters table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_filters'));
		$db->execute();

		// Optimize the terms common table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_terms_common'));
		$db->execute();

		// Optimize the types table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_types'));
		$db->execute();

		// Remove the orphaned taxonomy nodes.
		FinderIndexerTaxonomy::removeOrphanNodes();

		// Optimize the taxonomy mapping table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_taxonomy_map'));
		$db->execute();

		// Optimize the taxonomy table.
		$db->setQuery('OPTIMIZE TABLE ' .
$db->quoteName('#__finder_taxonomy'));
		$db->execute();

		return true;
	}


	/**
	 * Method to switch the token tables from Memory tables to MyISAM tables
	 * when they are close to running out of memory.
	 *
	 * @param   boolean  $memory  Flag to control how they should be toggled.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0
	 * @throws  Exception on database error.
	 */
	protected function toggleTables($memory)
	{
		static $state;

		// Get the database adapter.
		$db = $this->db;

		// Check if we are setting the tables to the Memory engine.
		if ($memory === true && $state !== true)
		{
			// Set the tokens table to Memory.
			$db->setQuery('ALTER TABLE ' .
$db->quoteName('#__finder_tokens') . ' ENGINE =
MEMORY');
			$db->execute();

			// Set the tokens aggregate table to Memory.
			$db->setQuery('ALTER TABLE ' .
$db->quoteName('#__finder_tokens_aggregate') . ' ENGINE =
MEMORY');
			$db->execute();

			// Set the internal state.
			$state = $memory;
		}
		// We must be setting the tables to the MyISAM engine.
		elseif ($memory === false && $state !== false)
		{
			// Set the tokens table to MyISAM.
			$db->setQuery('ALTER TABLE ' .
$db->quoteName('#__finder_tokens') . ' ENGINE =
MYISAM');
			$db->execute();

			// Set the tokens aggregate table to MyISAM.
			$db->setQuery('ALTER TABLE ' .
$db->quoteName('#__finder_tokens_aggregate') . ' ENGINE =
MYISAM');
			$db->execute();

			// Set the internal state.
			$state = $memory;
		}

		return true;
	}
}
PK[�RC��?�?%helpers/indexer/driver/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\String\StringHelper;

jimport('joomla.filesystem.file');

/**
 * Indexer class supporting PostgreSQL for the Finder indexer package.
 *
 * @since  3.0
 */
class FinderIndexerDriverPostgresql extends FinderIndexer
{
	/**
	 * Method to index a content item.
	 *
	 * @param   FinderIndexerResult  $item    The content item to index.
	 * @param   string               $format  The format of the content.
[optional]
	 *
	 * @return  integer  The ID of the record in the links table.
	 *
	 * @since   3.0
	 * @throws  Exception on database error.
	 */
	public function index($item, $format = 'html')
	{
		// Mark beforeIndexing in the profiler.
		static::$profiler ?
static::$profiler->mark('beforeIndexing') : null;
		$db = $this->db;
		$nd = $db->getNullDate();

		// Check if the item is in the database.
		$query = $db->getQuery(true)
			->select($db->quoteName('link_id') . ', ' .
$db->quoteName('md5sum'))
			->from($db->quoteName('#__finder_links'))
			->where($db->quoteName('url') . ' = ' .
$db->quote($item->url));

		// Load the item  from the database.
		$db->setQuery($query);
		$link = $db->loadObject();

		// Get the indexer state.
		$state = static::getState();

		// Get the signatures of the item.
		$curSig = static::getSignature($item);
		$oldSig = isset($link->md5sum) ? $link->md5sum : null;

		// Get the other item information.
		$linkId = empty($link->link_id) ? null : $link->link_id;
		$isNew = empty($link->link_id) ? true : false;

		// Check the signatures. If they match, the item is up to date.
		if (!$isNew && $curSig === $oldSig)
		{
			return $linkId;
		}

		/*
		 * If the link already exists, flush all the term maps for the item.
		 * Maps are stored in 16 tables so we need to iterate through and flush
		 * each table one at a time.
		 */
		if (!$isNew)
		{
			for ($i = 0; $i <= 15; $i++)
			{
				// Flush the maps for the link.
				$query->clear()
					->delete($db->quoteName('#__finder_links_terms' .
dechex($i)))
					->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
				$db->setQuery($query);
				$db->execute();
			}

			// Remove the taxonomy maps.
			FinderIndexerTaxonomy::removeMaps($linkId);
		}

		// Mark afterUnmapping in the profiler.
		static::$profiler ?
static::$profiler->mark('afterUnmapping') : null;

		// Perform cleanup on the item data.
		$item->publish_start_date = (int) $item->publish_start_date != 0 ?
$item->publish_start_date : $nd;
		$item->publish_end_date = (int) $item->publish_end_date != 0 ?
$item->publish_end_date : $nd;
		$item->start_date = (int) $item->start_date != 0 ?
$item->start_date : $nd;
		$item->end_date = (int) $item->end_date != 0 ? $item->end_date :
$nd;

		// Prepare the item description.
		$item->description = FinderIndexerHelper::parse($item->summary);

		/*
		 * Now, we need to enter the item into the links table. If the item
		 * already exists in the database, we need to use an UPDATE query.
		 * Otherwise, we need to use an INSERT to get the link id back.
		 */

		$entry = new stdClass;
		$entry->url = $item->url;
		$entry->route = $item->route;
		$entry->title = $item->title;

		// We are shortening the description in order to not run into length
issues with this field
		$entry->description = StringHelper::substr($item->description, 0,
32000);
		$entry->indexdate = Factory::getDate()->toSql();
		$entry->state = (int) $item->state;
		$entry->access = (int) $item->access;
		$entry->language = $item->language;
		$entry->type_id = (int) $item->type_id;
		$entry->object = '';
		$entry->publish_start_date = $item->publish_start_date;
		$entry->publish_end_date = $item->publish_end_date;
		$entry->start_date = $item->start_date;
		$entry->end_date = $item->end_date;
		$entry->list_price = (double) ($item->list_price ?: 0);
		$entry->sale_price = (double) ($item->sale_price ?: 0);

		if ($isNew)
		{
			// Insert the link and get its id.
			$db->insertObject('#__finder_links', $entry);
			$linkId = (int) $db->insertid();
		}
		else
		{
			// Update the link.
			$entry->link_id = $linkId;
			$db->updateObject('#__finder_links', $entry,
'link_id');
		}

		// Set up the variables we will need during processing.
		$count = 0;

		// Mark afterLinking in the profiler.
		static::$profiler ? static::$profiler->mark('afterLinking')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		/*
		 * Process the item's content. The items can customize their
		 * processing instructions to define extra properties to process
		 * or rearrange how properties are weighted.
		 */
		foreach ($item->getInstructions() as $group => $properties)
		{
			// Iterate through the properties of the group.
			foreach ($properties as $property)
			{
				// Check if the property exists in the item.
				if (empty($item->$property))
				{
					continue;
				}

				// Tokenize the property.
				if (is_array($item->$property))
				{
					// Tokenize an array of content and add it to the database.
					foreach ($item->$property as $ip)
					{
						/*
						 * If the group is path, we need to a few extra processing
						 * steps to strip the extension and convert slashes and dashes
						 * to spaces.
						 */
						if ($group === static::PATH_CONTEXT)
						{
							$ip = JFile::stripExt($ip);
							$ip = str_replace(array('/', '-'), '
', $ip);
						}

						// Tokenize a string of content and add it to the database.
						$count += $this->tokenizeToDb($ip, $group, $item->language,
$format);

						// Check if we're approaching the memory limit of the token
table.
						if ($count >
static::$state->options->get('memory_table_limit', 30000))
						{
							$this->toggleTables(false);
						}
					}
				}
				else
				{
					/*
					 * If the group is path, we need to a few extra processing
					 * steps to strip the extension and convert slashes and dashes
					 * to spaces.
					 */
					if ($group === static::PATH_CONTEXT)
					{
						$item->$property = JFile::stripExt($item->$property);
						$item->$property = str_replace('/', ' ',
$item->$property);
						$item->$property = str_replace('-', ' ',
$item->$property);
					}

					// Tokenize a string of content and add it to the database.
					$count += $this->tokenizeToDb($item->$property, $group,
$item->language, $format);

					// Check if we're approaching the memory limit of the token
table.
					if ($count >
static::$state->options->get('memory_table_limit', 30000))
					{
						$this->toggleTables(false);
					}
				}
			}
		}

		/*
		 * Process the item's taxonomy. The items can customize their
		 * taxonomy mappings to define extra properties to map.
		 */
		foreach ($item->getTaxonomy() as $branch => $nodes)
		{
			// Iterate through the nodes and map them to the branch.
			foreach ($nodes as $node)
			{
				// Add the node to the tree.
				$nodeId = FinderIndexerTaxonomy::addNode($branch, $node->title,
$node->state, $node->access);

				// Add the link => node map.
				FinderIndexerTaxonomy::addMap($linkId, $nodeId);
			}
		}

		// Mark afterProcessing in the profiler.
		static::$profiler ?
static::$profiler->mark('afterProcessing') : null;

		/*
		 * At this point, all of the item's content has been parsed,
tokenized
		 * and inserted into the #__finder_tokens table. Now, we need to
		 * aggregate all the data into that table into a more usable form. The
		 * aggregated data will be inserted into #__finder_tokens_aggregate
		 * table.
		 */
		$query = 'INSERT INTO ' .
$db->quoteName('#__finder_tokens_aggregate') .
				' (' . $db->quoteName('term_id') .
				', ' . $db->quoteName('term') .
				', ' . $db->quoteName('stem') .
				', ' . $db->quoteName('common') .
				', ' . $db->quoteName('phrase') .
				', ' . $db->quoteName('term_weight') .
				', ' . $db->quoteName('context') .
				', ' . $db->quoteName('context_weight') .
				', ' . $db->quoteName('language') .
')' .
				' SELECT' .
				' t.term_id, t1.term, t1.stem, t1.common, t1.phrase, t1.weight,
t1.context,' .
				' ROUND( t1.weight * COUNT( t2.term ) * %F, 8 ) AS context_weight,
t1.language' .
				' FROM (' .
				'   SELECT DISTINCT t1.term, t1.stem, t1.common, t1.phrase,
t1.weight, t1.context, t1.language' .
				'   FROM ' . $db->quoteName('#__finder_tokens')
. ' AS t1' .
				'   WHERE t1.context = %d' .
				' ) AS t1' .
				' JOIN ' . $db->quoteName('#__finder_tokens') .
' AS t2 ON t2.term = t1.term' .
				' LEFT JOIN ' .
$db->quoteName('#__finder_terms') . ' AS t ON t.term =
t1.term' .
				' WHERE t2.context = %d AND t.term_id IS NOT NULL' .
				' GROUP BY t1.term, t.term_id, t1.term, t1.stem, t1.common,
t1.phrase, t1.weight, t1.context, t1.language' .
				' ORDER BY t1.term DESC';

		// Iterate through the contexts and aggregate the tokens per context.
		foreach ($state->weights as $context => $multiplier)
		{
			// Run the query to aggregate the tokens for this context..
			$db->setQuery(sprintf($query, $multiplier, $context, $context));
			$db->execute();
		}

		// Mark afterAggregating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterAggregating') : null;

		/*
		 * When we pulled down all of the aggregate data, we did a LEFT JOIN
		 * over the terms table to try to find all the term ids that
		 * already exist for our tokens. If any of the rows in the aggregate
		 * table have a term of 0, then no term record exists for that
		 * term so we need to add it to the terms table.
		 */

		// Emulation of IGNORE INTO behaviour
		$db->setQuery(
			' SELECT ta.term' .
			' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') . ' AS
ta' .
			' WHERE ta.term_id = 0'
		);

		if ($db->loadRow() === null)
		{
			$db->setQuery(
				'INSERT INTO ' .
$db->quoteName('#__finder_terms') .
				' (' . $db->quoteName('term') .
				', ' . $db->quoteName('stem') .
				', ' . $db->quoteName('common') .
				', ' . $db->quoteName('phrase') .
				', ' . $db->quoteName('weight') .
				', ' . $db->quoteName('soundex') .
				', ' . $db->quoteName('language') .
')' .
				' SELECT ta.term, ta.stem, ta.common, ta.phrase, ta.term_weight,
SOUNDEX(ta.term), ta.language' .
				' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') . ' AS
ta' .
				' WHERE ta.term_id = 0' .
				' GROUP BY ta.term, ta.stem, ta.common, ta.phrase, ta.term_weight,
SOUNDEX(ta.term), ta.language'
			);
			$db->execute();
		}

		/*
		 * Now, we just inserted a bunch of new records into the terms table
		 * so we need to go back and update the aggregate table with all the
		 * new term ids.
		 */
		$query = $db->getQuery(true)
			->update($db->quoteName('#__finder_tokens_aggregate') .
' AS ta')
			->join('INNER',
$db->quoteName('#__finder_terms') . ' AS t ON t.term =
ta.term')
			->set('term_id = t.term_id')
			->where('ta.term_id = 0');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * After we've made sure that all of the terms are in the terms
table
		 * and the aggregate table has the correct term ids, we need to update
		 * the links counter for each term by one.
		 */
		$query->clear()
			->update($db->quoteName('#__finder_terms') . ' AS
t')
			->join('INNER',
$db->quoteName('#__finder_tokens_aggregate') . ' AS ta ON
ta.term_id = t.term_id')
			->set($db->quoteName('links') . ' = t.links +
1');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * Before we can insert all of the mapping rows, we have to figure out
		 * which mapping table the rows need to be inserted into. The mapping
		 * table for each term is based on the first character of the md5 of
		 * the first character of the term. In php, it would be expressed as
		 * substr(md5(substr($token, 0, 1)), 0, 1)
		 */
		$query->clear()
			->update($db->quoteName('#__finder_tokens_aggregate'))
			->set($db->quoteName('map_suffix') . ' =
SUBSTR(MD5(SUBSTR(' . $db->quoteName('term') . ', 1,
1)), 1, 1)');
		$db->setQuery($query);
		$db->execute();

		/*
		 * At this point, the aggregate table contains a record for each
		 * term in each context. So, we're going to pull down all of that
		 * data while grouping the records by term and add all of the
		 * sub-totals together to arrive at the final total for each token for
		 * this link. Then, we insert all of that data into the appropriate
		 * mapping table.
		 */
		for ($i = 0; $i <= 15; $i++)
		{
			// Get the mapping table suffix.
			$suffix = dechex($i);

			/*
			 * We have to run this query 16 times, one for each link => term
			 * mapping table.
			 */
			$db->setQuery(
				'INSERT INTO ' .
$db->quoteName('#__finder_links_terms' . $suffix) .
				' (' . $db->quoteName('link_id') .
				', ' . $db->quoteName('term_id') .
				', ' . $db->quoteName('weight') . ')'
.
				' SELECT ' . (int) $linkId . ', ' .
$db->quoteName('term_id') . ',' .
				' ROUND(SUM(' . $db->quoteName('context_weight')
. '), 8)' .
				' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') .
				' WHERE ' . $db->quoteName('map_suffix') .
' = ' . $db->quote($suffix) .
				' GROUP BY ' . $db->quoteName('term') . ',
' . $db->quoteName('term_id') .
				' ORDER BY ' . $db->quoteName('term') . '
DESC'
			);
			$db->execute();
		}

		// Mark afterMapping in the profiler.
		static::$profiler ? static::$profiler->mark('afterMapping')
: null;

		// Update the signature.
		$query->clear()
			->update($db->quoteName('#__finder_links'))
			->set($db->quoteName('md5sum') . ' = ' .
$db->quote($curSig))
			->where($db->quoteName('link_id') . ' = ' .
$db->quote($linkId));
		$db->setQuery($query);
		$db->execute();

		// Mark afterSigning in the profiler.
		static::$profiler ? static::$profiler->mark('afterSigning')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		// Toggle the token tables back to memory tables.
		$this->toggleTables(true);

		// Mark afterTruncating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterTruncating') : null;

		return $linkId;
	}

	/**
	 * Method to optimize the index. We use this method to remove unused terms
	 * and any other optimizations that might be necessary.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public function optimize()
	{
		// Get the database object.
		$db = $this->db;
		$query = $db->getQuery(true);

		// Delete all orphaned terms.
		$query->delete($db->quoteName('#__finder_terms'))
			->where($db->quoteName('links') . ' <= 0');
		$db->setQuery($query);
		$db->execute();

		// Optimize the links table.
		$db->setQuery('VACUUM ' .
$db->quoteName('#__finder_links'));
		$db->execute();
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_links'));
		$db->execute();

		for ($i = 0; $i <= 15; $i++)
		{
			// Optimize the terms mapping table.
			$db->setQuery('VACUUM ' .
$db->quoteName('#__finder_links_terms' . dechex($i)));
			$db->execute();
			$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_links_terms' . dechex($i)));
			$db->execute();
		}

		// Optimize the filters table.
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_filters'));
		$db->execute();

		// Optimize the terms common table.
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_terms_common'));
		$db->execute();

		// Optimize the types table.
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_types'));
		$db->execute();

		// Remove the orphaned taxonomy nodes.
		FinderIndexerTaxonomy::removeOrphanNodes();

		// Optimize the taxonomy mapping table.
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_taxonomy_map'));
		$db->execute();

		// Optimize the taxonomy table.
		$db->setQuery('REINDEX TABLE ' .
$db->quoteName('#__finder_taxonomy'));
		$db->execute();

		return true;
	}
}
PK[f�X�GGGG!helpers/indexer/driver/sqlsrv.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

jimport('joomla.filesystem.file');

/**
 * Indexer class supporting SQL Server for the Finder indexer package.
 *
 * The indexer class provides the core functionality of the Finder
 * search engine. It is responsible for adding and updating the
 * content links table; extracting and scoring tokens; and maintaining
 * all referential information for the content.
 *
 * Note: All exceptions thrown from within this class should be caught
 * by the controller.
 *
 * @since  3.1
 */
class FinderIndexerDriverSqlsrv extends FinderIndexer
{
	/**
	 * Method to index a content item.
	 *
	 * @param   FinderIndexerResult  $item    The content item to index.
	 * @param   string               $format  The format of the content.
[optional]
	 *
	 * @return  integer  The ID of the record in the links table.
	 *
	 * @since   3.1
	 * @throws  Exception on database error.
	 */
	public function index($item, $format = 'html')
	{
		// Mark beforeIndexing in the profiler.
		static::$profiler ?
static::$profiler->mark('beforeIndexing') : null;
		$db = $this->db;
		$nd = $db->getNullDate();

		// Check if the item is in the database.
		$query = $db->getQuery(true)
			->select($db->quoteName('link_id') . ', ' .
$db->quoteName('md5sum'))
			->from($db->quoteName('#__finder_links'))
			->where($db->quoteName('url') . ' = ' .
$db->quote($item->url));

		// Load the item  from the database.
		$db->setQuery($query);
		$link = $db->loadObject();

		// Get the indexer state.
		$state = static::getState();

		// Get the signatures of the item.
		$curSig = static::getSignature($item);
		$oldSig = isset($link->md5sum) ? $link->md5sum : null;

		// Get the other item information.
		$linkId = empty($link->link_id) ? null : $link->link_id;
		$isNew = empty($link->link_id) ? true : false;

		// Check the signatures. If they match, the item is up to date.
		if (!$isNew && $curSig === $oldSig)
		{
			return $linkId;
		}

		/*
		 * If the link already exists, flush all the term maps for the item.
		 * Maps are stored in 16 tables so we need to iterate through and flush
		 * each table one at a time.
		 */
		if (!$isNew)
		{
			for ($i = 0; $i <= 15; $i++)
			{
				// Flush the maps for the link.
				$query->clear()
					->delete($db->quoteName('#__finder_links_terms' .
dechex($i)))
					->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
				$db->setQuery($query);
				$db->execute();
			}

			// Remove the taxonomy maps.
			FinderIndexerTaxonomy::removeMaps($linkId);
		}

		// Mark afterUnmapping in the profiler.
		static::$profiler ?
static::$profiler->mark('afterUnmapping') : null;

		// Perform cleanup on the item data.
		$item->publish_start_date = (int) $item->publish_start_date != 0 ?
$item->publish_start_date : $nd;
		$item->publish_end_date = (int) $item->publish_end_date != 0 ?
$item->publish_end_date : $nd;
		$item->start_date = (int) $item->start_date != 0 ?
$item->start_date : $nd;
		$item->end_date = (int) $item->end_date != 0 ? $item->end_date :
$nd;

		// Prepare the item description.
		$item->description = FinderIndexerHelper::parse($item->summary);

		/*
		 * Now, we need to enter the item into the links table. If the item
		 * already exists in the database, we need to use an UPDATE query.
		 * Otherwise, we need to use an INSERT to get the link id back.
		 */

		if ($isNew)
		{
			$columnsArray = array(
				$db->quoteName('url'),
$db->quoteName('route'), $db->quoteName('title'),
$db->quoteName('description'),
				$db->quoteName('indexdate'),
$db->quoteName('published'),
$db->quoteName('state'),
$db->quoteName('access'),
				$db->quoteName('language'),
$db->quoteName('type_id'),
$db->quoteName('object'),
$db->quoteName('publish_start_date'),
				$db->quoteName('publish_end_date'),
$db->quoteName('start_date'),
$db->quoteName('end_date'),
$db->quoteName('list_price'),
				$db->quoteName('sale_price')
			);

			// Insert the link.
			$query->clear()
				->insert($db->quoteName('#__finder_links'))
				->columns($columnsArray)
				->values(
					$db->quote($item->url) . ', '
					. $db->quote($item->route) . ', '
					. $db->quote($item->title) . ', '
					. $db->quote($item->description) . ', '
					. $query->currentTimestamp() . ', '
					. '1, '
					. (int) $item->state . ', '
					. (int) $item->access . ', '
					. $db->quote($item->language) . ', '
					. (int) $item->type_id . ', '
					. $db->quote(serialize($item)) . ', '
					. $db->quote($item->publish_start_date) . ', '
					. $db->quote($item->publish_end_date) . ', '
					. $db->quote($item->start_date) . ', '
					. $db->quote($item->end_date) . ', '
					. (double) ($item->list_price ?: 0) . ', '
					. (double) ($item->sale_price ?: 0)
				);
			$db->setQuery($query);
			$db->execute();

			// Get the link id.
			$linkId = (int) $db->insertid();
		}
		else
		{
			// Update the link.
			$query->clear()
				->update($db->quoteName('#__finder_links'))
				->set($db->quoteName('route') . ' = ' .
$db->quote($item->route))
				->set($db->quoteName('title') . ' = ' .
$db->quote($item->title))
				->set($db->quoteName('description') . ' = ' .
$db->quote($item->description))
				->set($db->quoteName('indexdate') . ' = ' .
$query->currentTimestamp())
				->set($db->quoteName('state') . ' = ' . (int)
$item->state)
				->set($db->quoteName('access') . ' = ' .
(int) $item->access)
				->set($db->quoteName('language') . ' = ' .
$db->quote($item->language))
				->set($db->quoteName('type_id') . ' = ' .
(int) $item->type_id)
				->set($db->quoteName('object') . ' = ' .
$db->quote(serialize($item)))
				->set($db->quoteName('publish_start_date') . ' =
' . $db->quote($item->publish_start_date))
				->set($db->quoteName('publish_end_date') . ' =
' . $db->quote($item->publish_end_date))
				->set($db->quoteName('start_date') . ' = ' .
$db->quote($item->start_date))
				->set($db->quoteName('end_date') . ' = ' .
$db->quote($item->end_date))
				->set($db->quoteName('list_price') . ' = ' .
(double) ($item->list_price ?: 0))
				->set($db->quoteName('sale_price') . ' = ' .
(double) ($item->sale_price ?: 0))
				->where('link_id = ' . (int) $linkId);
			$db->setQuery($query);
			$db->execute();
		}

		// Set up the variables we will need during processing.
		$count = 0;

		// Mark afterLinking in the profiler.
		static::$profiler ? static::$profiler->mark('afterLinking')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		/*
		 * Process the item's content. The items can customize their
		 * processing instructions to define extra properties to process
		 * or rearrange how properties are weighted.
		 */
		foreach ($item->getInstructions() as $group => $properties)
		{
			// Iterate through the properties of the group.
			foreach ($properties as $property)
			{
				// Check if the property exists in the item.
				if (empty($item->$property))
				{
					continue;
				}

				// Tokenize the property.
				if (is_array($item->$property))
				{
					// Tokenize an array of content and add it to the database.
					foreach ($item->$property as $ip)
					{
						/*
						 * If the group is path, we need to a few extra processing
						 * steps to strip the extension and convert slashes and dashes
						 * to spaces.
						 */
						if ($group === static::PATH_CONTEXT)
						{
							$ip = JFile::stripExt($ip);
							$ip = str_replace(array('/', '-'), '
', $ip);
						}

						// Tokenize a string of content and add it to the database.
						$count += $this->tokenizeToDb($ip, $group, $item->language,
$format);

						// Check if we're approaching the memory limit of the token
table.
						if ($count >
static::$state->options->get('memory_table_limit', 30000))
						{
							$this->toggleTables(false);
						}
					}
				}
				else
				{
					/*
					 * If the group is path, we need to a few extra processing
					 * steps to strip the extension and convert slashes and dashes
					 * to spaces.
					 */
					if ($group === static::PATH_CONTEXT)
					{
						$item->$property = JFile::stripExt($item->$property);
						$item->$property = str_replace('/', ' ',
$item->$property);
						$item->$property = str_replace('-', ' ',
$item->$property);
					}

					// Tokenize a string of content and add it to the database.
					$count += $this->tokenizeToDb($item->$property, $group,
$item->language, $format);

					// Check if we're approaching the memory limit of the token
table.
					if ($count >
static::$state->options->get('memory_table_limit', 30000))
					{
						$this->toggleTables(false);
					}
				}
			}
		}

		/*
		 * Process the item's taxonomy. The items can customize their
		 * taxonomy mappings to define extra properties to map.
		 */
		foreach ($item->getTaxonomy() as $branch => $nodes)
		{
			// Iterate through the nodes and map them to the branch.
			foreach ($nodes as $node)
			{
				// Add the node to the tree.
				$nodeId = FinderIndexerTaxonomy::addNode($branch, $node->title,
$node->state, $node->access);

				// Add the link => node map.
				FinderIndexerTaxonomy::addMap($linkId, $nodeId);
			}
		}

		// Mark afterProcessing in the profiler.
		static::$profiler ?
static::$profiler->mark('afterProcessing') : null;

		/*
		 * At this point, all of the item's content has been parsed,
tokenized
		 * and inserted into the #__finder_tokens table. Now, we need to
		 * aggregate all the data into that table into a more usable form. The
		 * aggregated data will be inserted into #__finder_tokens_aggregate
		 * table.
		 */
		$query = 'INSERT INTO ' .
$db->quoteName('#__finder_tokens_aggregate') .
				' (' . $db->quoteName('term_id') .
				', ' . $db->quoteName('term') .
				', ' . $db->quoteName('stem') .
				', ' . $db->quoteName('common') .
				', ' . $db->quoteName('phrase') .
				', ' . $db->quoteName('term_weight') .
				', ' . $db->quoteName('context') .
				', ' . $db->quoteName('context_weight') .
				', ' . $db->quoteName('language') .
')' .
				' SELECT' .
				' t.term_id, t1.term, t1.stem, t1.common, t1.phrase, t1.weight,
t1.context,' .
				' ROUND( t1.weight * COUNT( t2.term ) * %F, 8 ) AS context_weight,
t1.language' .
				' FROM (' .
				'   SELECT DISTINCT t1.term, t1.stem, t1.common, t1.phrase,
t1.weight, t1.context, t1.language' .
				'   FROM ' . $db->quoteName('#__finder_tokens')
. ' AS t1' .
				'   WHERE t1.context = %d' .
				' ) AS t1' .
				' JOIN ' . $db->quoteName('#__finder_tokens') .
' AS t2 ON t2.term = t1.term' .
				' LEFT JOIN ' .
$db->quoteName('#__finder_terms') . ' AS t ON t.term =
t1.term' .
				' WHERE t2.context = %d' .
				' GROUP BY t1.term, t.term_id, t1.term, t1.stem, t1.common,
t1.phrase, t1.weight, t1.context, t1.language' .
				' ORDER BY t1.term DESC';

		// Iterate through the contexts and aggregate the tokens per context.
		foreach ($state->weights as $context => $multiplier)
		{
			// Run the query to aggregate the tokens for this context..
			$db->setQuery(sprintf($query, $multiplier, $context, $context));
			$db->execute();
		}

		// Mark afterAggregating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterAggregating') : null;

		/*
		 * When we pulled down all of the aggregate data, we did a LEFT JOIN
		 * over the terms table to try to find all the term ids that
		 * already exist for our tokens. If any of the rows in the aggregate
		 * table have a term of 0, then no term record exists for that
		 * term so we need to add it to the terms table.
		 */
		$db->setQuery(
			'INSERT INTO ' .
$db->quoteName('#__finder_terms') .
			' (' . $db->quoteName('term') .
			', ' . $db->quoteName('stem') .
			', ' . $db->quoteName('common') .
			', ' . $db->quoteName('phrase') .
			', ' . $db->quoteName('weight') .
			', ' . $db->quoteName('soundex') . ')'
.
			' SELECT ta.term, ta.stem, ta.common, ta.phrase, ta.term_weight,
SOUNDEX(ta.term)' .
			' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') . ' AS
ta' .
			' WHERE ta.term_id IS NULL' .
			' GROUP BY ta.term, ta.stem, ta.common, ta.phrase,
ta.term_weight'
		);
		$db->execute();

		/*
		 * Now, we just inserted a bunch of new records into the terms table
		 * so we need to go back and update the aggregate table with all the
		 * new term ids.
		 */
		$query = $db->getQuery(true)
			->update('ta')
			->set('ta.term_id = t.term_id from #__finder_tokens_aggregate AS
ta INNER JOIN #__finder_terms AS t ON t.term = ta.term')
			->where('ta.term_id IS NULL');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * After we've made sure that all of the terms are in the terms
table
		 * and the aggregate table has the correct term ids, we need to update
		 * the links counter for each term by one.
		 */
		$query->clear()
			->update('t')
			->set('t.links = t.links + 1 FROM #__finder_terms AS t INNER
JOIN #__finder_tokens_aggregate AS ta ON ta.term_id = t.term_id');
		$db->setQuery($query);
		$db->execute();

		// Mark afterTerms in the profiler.
		static::$profiler ? static::$profiler->mark('afterTerms') :
null;

		/*
		 * Before we can insert all of the mapping rows, we have to figure out
		 * which mapping table the rows need to be inserted into. The mapping
		 * table for each term is based on the first character of the md5 of
		 * the first character of the term. In php, it would be expressed as
		 * substr(md5(substr($token, 0, 1)), 0, 1)
		 */
		$query->clear()
			->update($db->quoteName('#__finder_tokens_aggregate'))
			->set($db->quoteName('map_suffix') . " =
SUBSTRING(HASHBYTES('MD5', SUBSTRING(" .
$db->quoteName('term') . ', 1, 1)), 1, 1)');
		$db->setQuery($query);
		$db->execute();

		/*
		 * At this point, the aggregate table contains a record for each
		 * term in each context. So, we're going to pull down all of that
		 * data while grouping the records by term and add all of the
		 * sub-totals together to arrive at the final total for each token for
		 * this link. Then, we insert all of that data into the appropriate
		 * mapping table.
		 */
		for ($i = 0; $i <= 15; $i++)
		{
			// Get the mapping table suffix.
			$suffix = dechex($i);

			/*
			 * We have to run this query 16 times, one for each link => term
			 * mapping table.
			 */
			$db->setQuery(
				'INSERT INTO ' .
$db->quoteName('#__finder_links_terms' . $suffix) .
				' (' . $db->quoteName('link_id') .
				', ' . $db->quoteName('term_id') .
				', ' . $db->quoteName('weight') . ')'
.
				' SELECT ' . (int) $linkId . ', ' .
$db->quoteName('term_id') . ',' .
				' ROUND(SUM(' . $db->quoteName('context_weight')
. '), 8)' .
				' FROM ' .
$db->quoteName('#__finder_tokens_aggregate') .
				' WHERE ' . $db->quoteName('map_suffix') .
' = ' . $db->quote($suffix) .
				' GROUP BY term, term_id' .
				' ORDER BY ' . $db->quoteName('term') . '
DESC'
			);
			$db->execute();
		}

		// Mark afterMapping in the profiler.
		static::$profiler ? static::$profiler->mark('afterMapping')
: null;

		// Update the signature.
		$query->clear()
			->update($db->quoteName('#__finder_links'))
			->set($db->quoteName('md5sum') . ' = ' .
$db->quote($curSig))
			->where($db->quoteName('link_id') . ' = ' .
$db->quote($linkId));
		$db->setQuery($query);
		$db->execute();

		// Mark afterSigning in the profiler.
		static::$profiler ? static::$profiler->mark('afterSigning')
: null;

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		// Toggle the token tables back to memory tables.
		$this->toggleTables(true);

		// Mark afterTruncating in the profiler.
		static::$profiler ?
static::$profiler->mark('afterTruncating') : null;

		return $linkId;
	}

	/**
	 * Method to remove a link from the index.
	 *
	 * @param   integer  $linkId  The id of the link.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.1
	 * @throws  Exception on database error.
	 */
	public function remove($linkId)
	{
		$db = $this->db;
		$query = $db->getQuery(true);

		// Update the link counts and remove the mapping records.
		for ($i = 0; $i <= 15; $i++)
		{
			// Update the link counts for the terms.
			$query->update('t')
				->set('t.links = t.links - 1 from #__finder_terms AS t INNER
JOIN #__finder_links_terms' . dechex($i) . ' AS m ON m.term_id =
t.term_id')
				->where('m.link_id = ' . $db->quote((int) $linkId));
			$db->setQuery($query);
			$db->execute();

			// Remove all records from the mapping tables.
			$query->clear()
				->delete($db->quoteName('#__finder_links_terms' .
dechex($i)))
				->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
			$db->setQuery($query);
			$db->execute();
		}

		// Delete all orphaned terms.
		$query->clear()
			->delete($db->quoteName('#__finder_terms'))
			->where($db->quoteName('links') . ' <= 0');
		$db->setQuery($query);
		$db->execute();

		// Delete the link from the index.
		$query->clear()
			->delete($db->quoteName('#__finder_links'))
			->where($db->quoteName('link_id') . ' = ' .
$db->quote((int) $linkId));
		$db->setQuery($query);
		$db->execute();

		// Remove the taxonomy maps.
		FinderIndexerTaxonomy::removeMaps($linkId);

		// Remove the orphaned taxonomy nodes.
		FinderIndexerTaxonomy::removeOrphanNodes();

		return true;
	}

	/**
	 * Method to optimize the index. We use this method to remove unused terms
	 * and any other optimizations that might be necessary.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.1
	 * @throws  Exception on database error.
	 */
	public function optimize()
	{
		// Get the database object.
		$db = $this->db;
		$query = $db->getQuery(true);

		// Delete all orphaned terms.
		$query->delete($db->quoteName('#__finder_terms'))
			->where($db->quoteName('links') . ' <= 0');
		$db->setQuery($query);
		$db->execute();

		// Remove the orphaned taxonomy nodes.
		FinderIndexerTaxonomy::removeOrphanNodes();

		return true;
	}
}
PK[:��̴8�8helpers/indexer/helper.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;
use Joomla\String\StringHelper;

JLoader::register('FinderIndexerParser', __DIR__ .
'/parser.php');
JLoader::register('FinderIndexerStemmer', __DIR__ .
'/stemmer.php');
JLoader::register('FinderIndexerToken', __DIR__ .
'/token.php');

/**
 * Helper class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerHelper
{
	/**
	 * The token stemmer object. The stemmer is set by whatever class
	 * wishes to use it but it must be an instance of FinderIndexerStemmer.
	 *
	 * @var		FinderIndexerStemmer
	 * @since	2.5
	 */
	public static $stemmer;

	/**
	 * A state flag, in order to not constantly check if the stemmer is an
instance of FinderIndexerStemmer
	 *
	 * @var		boolean
	 * @since	3.7.0
	 */
	protected static $stemmerOK;

	/**
	 * Method to parse input into plain text.
	 *
	 * @param   string  $input   The raw input.
	 * @param   string  $format  The format of the input. [optional]
	 *
	 * @return  string  The parsed input.
	 *
	 * @since   2.5
	 * @throws  Exception on invalid parser.
	 */
	public static function parse($input, $format = 'html')
	{
		// Get a parser for the specified format and parse the input.
		return FinderIndexerParser::getInstance($format)->parse($input);
	}

	/**
	 * Method to tokenize a text string.
	 *
	 * @param   string   $input   The input to tokenize.
	 * @param   string   $lang    The language of the input.
	 * @param   boolean  $phrase  Flag to indicate whether input could be a
phrase. [optional]
	 *
	 * @return  array|FinderIndexerToken  An array of FinderIndexerToken
objects or a single FinderIndexerToken object.
	 *
	 * @since   2.5
	 */
	public static function tokenize($input, $lang, $phrase = false)
	{
		static $cache;
		$store = StringHelper::strlen($input) < 128 ? md5($input .
'::' . $lang . '::' . $phrase) : null;

		// Check if the string has been tokenized already.
		if ($store && isset($cache[$store]))
		{
			return $cache[$store];
		}

		$tokens = array();
		$quotes = html_entity_decode('&#8216;&#8217;&#39;',
ENT_QUOTES, 'UTF-8');

		// Get the simple language key.
		$lang = static::getPrimaryLanguage($lang);

		/*
		 * Parsing the string input into terms is a multi-step process.
		 *
		 * Regexes:
		 *  1. Remove everything except letters, numbers, quotes, apostrophe,
plus, dash, period, and comma.
		 *  2. Remove plus, dash, period, and comma characters located before
letter characters.
		 *  3. Remove plus, dash, period, and comma characters located after
other characters.
		 *  4. Remove plus, period, and comma characters enclosed in alphabetical
characters. Ungreedy.
		 *  5. Remove orphaned apostrophe, plus, dash, period, and comma
characters.
		 *  6. Remove orphaned quote characters.
		 *  7. Replace the assorted single quotation marks with the ASCII
standard single quotation.
		 *  8. Remove multiple space characters and replaces with a single space.
		 */
		$input = StringHelper::strtolower($input);
		$input =
preg_replace('#[^\pL\pM\pN\p{Pi}\p{Pf}\'+-.,]+#mui', '
', $input);
		$input = preg_replace('#(^|\s)[+-.,]+([\pL\pM]+)#mui', '
$1', $input);
		$input = preg_replace('#([\pL\pM\pN]+)[+-.,]+(\s|$)#mui',
'$1 ', $input);
		$input = preg_replace('#([\pL\pM]+)[+.,]+([\pL\pM]+)#muiU',
'$1 $2', $input);
		$input = preg_replace('#(^|\s)[\'+-.,]+(\s|$)#mui', '
', $input);
		$input = preg_replace('#(^|\s)[\p{Pi}\p{Pf}]+(\s|$)#mui',
' ', $input);
		$input = preg_replace('#[' . $quotes . ']+#mui',
'\'', $input);
		$input = preg_replace('#\s+#mui', ' ', $input);
		$input = trim($input);

		// Explode the normalized string to get the terms.
		$terms = explode(' ', $input);

		/*
		 * If we have Unicode support and are dealing with Chinese text, Chinese
		 * has to be handled specially because there are not necessarily any
spaces
		 * between the "words". So, we have to test if the words belong
to the Chinese
		 * character set and if so, explode them into single glyphs or
"words".
		 */
		if ($lang === 'zh')
		{
			// Iterate through the terms and test if they contain Chinese.
			for ($i = 0, $n = count($terms); $i < $n; $i++)
			{
				$charMatches = array();
				$charCount   = preg_match_all('#[\p{Han}]#mui', $terms[$i],
$charMatches);

				// Split apart any groups of Chinese characters.
				for ($j = 0; $j < $charCount; $j++)
				{
					$tSplit = StringHelper::str_ireplace($charMatches[0][$j],
'', $terms[$i], false);

					if ((bool) $tSplit)
					{
						$terms[$i] = $tSplit;
					}
					else
					{
						unset($terms[$i]);
					}

					$terms[] = $charMatches[0][$j];
				}
			}

			// Reset array keys.
			$terms = array_values($terms);
		}

		/*
		 * If we have to handle the input as a phrase, that means we don't
		 * tokenize the individual terms and we do not create the two and three
		 * term combinations. The phrase must contain more than one word!
		 */
		if ($phrase === true && count($terms) > 1)
		{
			// Create tokens from the phrase.
			$tokens[] = new FinderIndexerToken($terms, $lang);
		}
		else
		{
			// Create tokens from the terms.
			for ($i = 0, $n = count($terms); $i < $n; $i++)
			{
				$tokens[] = new FinderIndexerToken($terms[$i], $lang);
			}

			// Create two and three word phrase tokens from the individual words.
			for ($i = 0, $n = count($tokens); $i < $n; $i++)
			{
				// Setup the phrase positions.
				$i2 = $i + 1;
				$i3 = $i + 2;

				// Create the two word phrase.
				if ($i2 < $n && isset($tokens[$i2]))
				{
					// Tokenize the two word phrase.
					$token          = new FinderIndexerToken(
						array(
							$tokens[$i]->term,
							$tokens[$i2]->term
						), $lang, $lang === 'zh' ? '' : ' '
					);
					$token->derived = true;

					// Add the token to the stack.
					$tokens[] = $token;
				}

				// Create the three word phrase.
				if ($i3 < $n && isset($tokens[$i3]))
				{
					// Tokenize the three word phrase.
					$token          = new FinderIndexerToken(
						array(
							$tokens[$i]->term,
							$tokens[$i2]->term,
							$tokens[$i3]->term
						), $lang, $lang === 'zh' ? '' : ' '
					);
					$token->derived = true;

					// Add the token to the stack.
					$tokens[] = $token;
				}
			}
		}

		if ($store)
		{
			$cache[$store] = count($tokens) > 1 ? $tokens : array_shift($tokens);

			return $cache[$store];
		}
		else
		{
			return count($tokens) > 1 ? $tokens : array_shift($tokens);
		}
	}

	/**
	 * Method to get the base word of a token. This method uses the public
	 * {@link FinderIndexerHelper::$stemmer} object if it is set. If no
stemmer is set,
	 * the original token is returned.
	 *
	 * @param   string  $token  The token to stem.
	 * @param   string  $lang   The language of the token.
	 *
	 * @return  string  The root token.
	 *
	 * @since   2.5
	 */
	public static function stem($token, $lang)
	{
		// Trim apostrophes at either end of the token.
		$token = trim($token, '\'');

		// Trim everything after any apostrophe in the token.
		if ($res = explode('\'', $token))
		{
			$token = $res[0];
		}

		if (static::$stemmerOK === true)
		{
			return static::$stemmer->stem($token, $lang);
		}
		else
		{
			// Stem the token if we have a valid stemmer to use.
			if (static::$stemmer instanceof FinderIndexerStemmer)
			{
				static::$stemmerOK = true;

				return static::$stemmer->stem($token, $lang);
			}
		}

		return $token;
	}

	/**
	 * Method to add a content type to the database.
	 *
	 * @param   string  $title  The type of content. For example: PDF
	 * @param   string  $mime   The mime type of the content. For example: PDF
[optional]
	 *
	 * @return  integer  The id of the content type.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function addContentType($title, $mime = null)
	{
		static $types;

		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);

		// Check if the types are loaded.
		if (empty($types))
		{
			// Build the query to get the types.
			$query->select('*')
				->from($db->quoteName('#__finder_types'));

			// Get the types.
			$db->setQuery($query);
			$types = $db->loadObjectList('title');
		}

		// Check if the type already exists.
		if (isset($types[$title]))
		{
			return (int) $types[$title]->id;
		}

		// Add the type.
		$query->clear()
			->insert($db->quoteName('#__finder_types'))
			->columns(array($db->quoteName('title'),
$db->quoteName('mime')))
			->values($db->quote($title) . ', ' .
$db->quote($mime));
		$db->setQuery($query);
		$db->execute();

		// Return the new id.
		return (int) $db->insertid();
	}

	/**
	 * Method to check if a token is common in a language.
	 *
	 * @param   string  $token  The token to test.
	 * @param   string  $lang   The language to reference.
	 *
	 * @return  boolean  True if common, false otherwise.
	 *
	 * @since   2.5
	 */
	public static function isCommon($token, $lang)
	{
		static $data;
		static $default;

		$langCode = $lang;

		// If language requested is wildcard, use the default language.
		if ($default === null && $lang === '*')
		{
			$default = strstr(self::getDefaultLanguage(), '-', true);
			$langCode = $default;
		}

		// Load the common tokens for the language if necessary.
		if (!isset($data[$langCode]))
		{
			$data[$langCode] = self::getCommonWords($langCode);
		}

		// Check if the token is in the common array.
		return in_array($token, $data[$langCode], true);
	}

	/**
	 * Method to get an array of common terms for a language.
	 *
	 * @param   string  $lang  The language to use.
	 *
	 * @return  array  Array of common terms.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function getCommonWords($lang)
	{
		$db = JFactory::getDbo();

		// Create the query to load all the common terms for the language.
		$query = $db->getQuery(true)
			->select($db->quoteName('term'))
			->from($db->quoteName('#__finder_terms_common'))
			->where($db->quoteName('language') . ' = ' .
$db->quote($lang));

		// Load all of the common terms for the language.
		$db->setQuery($query);

		return $db->loadColumn();
	}

	/**
	 * Method to get the default language for the site.
	 *
	 * @return  string  The default language string.
	 *
	 * @since   2.5
	 */
	public static function getDefaultLanguage()
	{
		static $lang;

		// We need to go to com_languages to get the site default language,
it's the best we can guess.
		if (empty($lang))
		{
			$lang =
JComponentHelper::getParams('com_languages')->get('site',
'en-GB');
		}

		return $lang;
	}

	/**
	 * Method to parse a language/locale key and return a simple language
string.
	 *
	 * @param   string  $lang  The language/locale key. For example: en-GB
	 *
	 * @return  string  The simple language string. For example: en
	 *
	 * @since   2.5
	 */
	public static function getPrimaryLanguage($lang)
	{
		static $data;

		// Only parse the identifier if necessary.
		if (!isset($data[$lang]))
		{
			if (is_callable(array('Locale',
'getPrimaryLanguage')))
			{
				// Get the language key using the Locale package.
				$data[$lang] = Locale::getPrimaryLanguage($lang);
			}
			else
			{
				// Get the language key using string position.
				$data[$lang] = StringHelper::substr($lang, 0,
StringHelper::strpos($lang, '-'));
			}
		}

		return $data[$lang];
	}

	/**
	 * Method to get the path (SEF route) for a content item.
	 *
	 * @param   string  $url  The non-SEF route to the content item.
	 *
	 * @return  string  The path for the content item.
	 *
	 * @since       2.5
	 * @deprecated  4.0
	 */
	public static function getContentPath($url)
	{
		static $router;

		// Only get the router once.
		if (!($router instanceof JRouter))
		{
			// Get and configure the site router.
			$config = JFactory::getConfig();
			$router = JRouter::getInstance('site');
			$router->setMode($config->get('sef', 1));
		}

		// Build the relative route.
		$uri   = $router->build($url);
		$route = $uri->toString(array('path', 'query',
'fragment'));
		$route = str_replace(JUri::base(true) . '/', '',
$route);

		return $route;
	}

	/**
	 * Method to get extra data for a content before being indexed. This is
how
	 * we add Comments, Tags, Labels, etc. that should be available to Finder.
	 *
	 * @param   FinderIndexerResult  $item  The item to index as a
FinderIndexerResult object.
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function getContentExtras(FinderIndexerResult $item)
	{
		// Get the event dispatcher.
		$dispatcher = JEventDispatcher::getInstance();

		// Load the finder plugin group.
		JPluginHelper::importPlugin('finder');

		// Trigger the event.
		$results = $dispatcher->trigger('onPrepareFinderContent',
array(&$item));

		// Check the returned results. This is for plugins that don't throw
		// exceptions when they encounter serious errors.
		if (in_array(false, $results))
		{
			throw new Exception($dispatcher->getError(), 500);
		}

		return true;
	}

	/**
	 * Method to process content text using the onContentPrepare event
trigger.
	 *
	 * @param   string               $text    The content to process.
	 * @param   Registry             $params  The parameters object.
[optional]
	 * @param   FinderIndexerResult  $item    The item which get prepared.
[optional]
	 *
	 * @return  string  The processed content.
	 *
	 * @since   2.5
	 */
	public static function prepareContent($text, $params = null,
FinderIndexerResult $item = null)
	{
		static $loaded;

		// Get the dispatcher.
		$dispatcher = JEventDispatcher::getInstance();

		// Load the content plugins if necessary.
		if (empty($loaded))
		{
			JPluginHelper::importPlugin('content');
			$loaded = true;
		}

		// Instantiate the parameter object if necessary.
		if (!($params instanceof Registry))
		{
			$registry = new Registry($params);
			$params = $registry;
		}

		// Create a mock content object.
		$content       = JTable::getInstance('Content');
		$content->text = $text;

		if ($item)
		{
			$content->bind((array) $item);
			$content->bind($item->getElements());
		}

		if ($item && !empty($item->context))
		{
			$content->context = $item->context;
		}

		// Fire the onContentPrepare event.
		$dispatcher->trigger('onContentPrepare',
array('com_finder.indexer', &$content, &$params, 0));

		return $content->text;
	}
}
PK[.!���:�:helpers/indexer/indexer.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;

JLoader::register('FinderIndexerHelper', __DIR__ .
'/helper.php');
JLoader::register('FinderIndexerParser', __DIR__ .
'/parser.php');
JLoader::register('FinderIndexerStemmer', __DIR__ .
'/stemmer.php');
JLoader::register('FinderIndexerTaxonomy', __DIR__ .
'/taxonomy.php');
JLoader::register('FinderIndexerToken', __DIR__ .
'/token.php');

jimport('joomla.filesystem.file');

/**
 * Main indexer class for the Finder indexer package.
 *
 * The indexer class provides the core functionality of the Finder
 * search engine. It is responsible for adding and updating the
 * content links table; extracting and scoring tokens; and maintaining
 * all referential information for the content.
 *
 * Note: All exceptions thrown from within this class should be caught
 * by the controller.
 *
 * @since  2.5
 */
abstract class FinderIndexer
{
	/**
	 * The title context identifier.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	const TITLE_CONTEXT = 1;

	/**
	 * The text context identifier.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	const TEXT_CONTEXT = 2;

	/**
	 * The meta context identifier.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	const META_CONTEXT = 3;

	/**
	 * The path context identifier.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	const PATH_CONTEXT = 4;

	/**
	 * The misc context identifier.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	const MISC_CONTEXT = 5;

	/**
	 * The indexer state object.
	 *
	 * @var    JObject
	 * @since  2.5
	 */
	public static $state;

	/**
	 * The indexer profiler object.
	 *
	 * @var    JProfiler
	 * @since  2.5
	 */
	public static $profiler;

	/**
	 * Database driver cache.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.8.0
	 */
	protected $db;

	/**
	 * Reusable Query Template. To be used with clone.
	 *
	 * @var    JDatabaseQuery
	 * @since  3.8.0
	 */
	protected $addTokensToDbQueryTemplate;

	/**
	 * FinderIndexer constructor.
	 *
	 * @since  3.8.0
	 */
	public function __construct()
	{
		$this->db = JFactory::getDbo();

		$db = $this->db;

		/**
		 * Set up query template for addTokensToDb, we will be cloning this
template when needed.
		 * This is about twice as fast as calling the clear function or setting
up a new object.
		 */
		$this->addTokensToDbQueryTemplate =
$db->getQuery(true)->insert($db->quoteName('#__finder_tokens'))
			->columns(
				array(
					$db->quoteName('term'),
					$db->quoteName('stem'),
					$db->quoteName('common'),
					$db->quoteName('phrase'),
					$db->quoteName('weight'),
					$db->quoteName('context'),
					$db->quoteName('language')
				)
			);
	}

	/**
	 * Returns a reference to the FinderIndexer object.
	 *
	 * @return  FinderIndexer instance based on the database driver
	 *
	 * @since   3.0
	 * @throws  RuntimeException if driver class for indexer not present.
	 */
	public static function getInstance()
	{
		// Setup the adapter for the indexer.
		$serverType = JFactory::getDbo()->getServerType();

		// For `mssql` server types, convert the type to `sqlsrv`
		if ($serverType === 'mssql')
		{
			$serverType = 'sqlsrv';
		}

		$path = __DIR__ . '/driver/' . $serverType . '.php';
		$class = 'FinderIndexerDriver' . ucfirst($serverType);

		// Check if a parser exists for the format.
		if (file_exists($path))
		{
			// Instantiate the parser.
			JLoader::register($class, $path);

			return new $class;
		}

		// Throw invalid format exception.
		throw new
RuntimeException(JText::sprintf('COM_FINDER_INDEXER_INVALID_DRIVER',
$serverType));
	}

	/**
	 * Method to get the indexer state.
	 *
	 * @return  object  The indexer state object.
	 *
	 * @since   2.5
	 */
	public static function getState()
	{
		// First, try to load from the internal state.
		if ((bool) static::$state)
		{
			return static::$state;
		}

		// If we couldn't load from the internal state, try the session.
		$session = JFactory::getSession();
		$data = $session->get('_finder.state', null);

		// If the state is empty, load the values for the first time.
		if (empty($data))
		{
			$data = new JObject;

			// Load the default configuration options.
			$data->options = JComponentHelper::getParams('com_finder');

			// Setup the weight lookup information.
			$data->weights = array(
				self::TITLE_CONTEXT =>
round($data->options->get('title_multiplier', 1.7), 2),
				self::TEXT_CONTEXT  =>
round($data->options->get('text_multiplier', 0.7), 2),
				self::META_CONTEXT  =>
round($data->options->get('meta_multiplier', 1.2), 2),
				self::PATH_CONTEXT  =>
round($data->options->get('path_multiplier', 2.0), 2),
				self::MISC_CONTEXT  =>
round($data->options->get('misc_multiplier', 0.3), 2)
			);

			// Set the current time as the start time.
			$data->startTime = JFactory::getDate()->toSql();

			// Set the remaining default values.
			$data->batchSize   = (int)
$data->options->get('batch_size', 50);
			$data->batchOffset = 0;
			$data->totalItems  = 0;
			$data->pluginState = array();
		}

		// Setup the profiler if debugging is enabled.
		if (JFactory::getApplication()->get('debug'))
		{
			static::$profiler = JProfiler::getInstance('FinderIndexer');
		}

		// Setup the stemmer.
		if ($data->options->get('stem', 1) &&
$data->options->get('stemmer', 'porter_en'))
		{
			FinderIndexerHelper::$stemmer =
FinderIndexerStemmer::getInstance($data->options->get('stemmer',
'porter_en'));
		}

		// Set the state.
		static::$state = $data;

		return static::$state;
	}

	/**
	 * Method to set the indexer state.
	 *
	 * @param   object  $data  A new indexer state object.
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 */
	public static function setState($data)
	{
		// Check the state object.
		if (empty($data) || !$data instanceof JObject)
		{
			return false;
		}

		// Set the new internal state.
		static::$state = $data;

		// Set the new session state.
		JFactory::getSession()->set('_finder.state', $data);

		return true;
	}

	/**
	 * Method to reset the indexer state.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public static function resetState()
	{
		// Reset the internal state to null.
		self::$state = null;

		// Reset the session state to null.
		JFactory::getSession()->set('_finder.state', null);
	}

	/**
	 * Method to index a content item.
	 *
	 * @param   FinderIndexerResult  $item    The content item to index.
	 * @param   string               $format  The format of the content.
[optional]
	 *
	 * @return  integer  The ID of the record in the links table.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	abstract public function index($item, $format = 'html');

	/**
	 * Method to remove a link from the index.
	 *
	 * @param   integer  $linkId  The id of the link.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public function remove($linkId)
	{
		$db    = $this->db;
		$query = $db->getQuery(true);

		// Update the link counts and remove the mapping records.
		for ($i = 0; $i <= 15; $i++)
		{
			// Update the link counts for the terms.
			$query->clear()
				->update($db->quoteName('#__finder_terms',
't'))
				->join('INNER',
$db->quoteName('#__finder_links_terms' . dechex($i),
'm') .
					' ON ' . $db->quoteName('m.term_id') . ' =
' . $db->quoteName('t.term_id')
				)
				->set($db->quoteName('links') . ' = ' .
$db->quoteName('links') . ' - 1')
				->where($db->quoteName('m.link_id') . ' = ' .
(int) $linkId);
			$db->setQuery($query)->execute();

			// Remove all records from the mapping tables.
			$query->clear()
				->delete($db->quoteName('#__finder_links_terms' .
dechex($i)))
				->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
			$db->setQuery($query)->execute();
		}

		// Delete all orphaned terms.
		$query->clear()
			->delete($db->quoteName('#__finder_terms'))
			->where($db->quoteName('links') . ' <= 0');
		$db->setQuery($query)->execute();

		// Delete the link from the index.
		$query->clear()
			->delete($db->quoteName('#__finder_links'))
			->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
		$db->setQuery($query)->execute();

		// Remove the taxonomy maps.
		FinderIndexerTaxonomy::removeMaps($linkId);

		// Remove the orphaned taxonomy nodes.
		FinderIndexerTaxonomy::removeOrphanNodes();

		return true;
	}

	/**
	 * Method to optimize the index. We use this method to remove unused terms
	 * and any other optimizations that might be necessary.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	abstract public function optimize();

	/**
	 * Method to get a content item's signature.
	 *
	 * @param   object  $item  The content item to index.
	 *
	 * @return  string  The content item's signature.
	 *
	 * @since   2.5
	 */
	protected static function getSignature($item)
	{
		// Get the indexer state.
		$state = static::getState();

		// Get the relevant configuration variables.
		$config = array(
			$state->weights,
			$state->options->get('stem', 1),
			$state->options->get('stemmer', 'porter_en')
		);

		return md5(serialize(array($item, $config)));
	}

	/**
	 * Method to parse input, tokenize it, and then add it to the database.
	 *
	 * @param   mixed    $input    String or resource to use as input. A
resource input will automatically be chunked to conserve
	 *                             memory. Strings will be chunked if longer
than 2K in size.
	 * @param   integer  $context  The context of the input. See context
constants.
	 * @param   string   $lang     The language of the input.
	 * @param   string   $format   The format of the input.
	 *
	 * @return  integer  The number of tokens extracted from the input.
	 *
	 * @since   2.5
	 */
	protected function tokenizeToDb($input, $context, $lang, $format)
	{
		$count = 0;
		$buffer = null;

		if (empty($input))
		{
			return $count;
		}

		// If the input is a resource, batch the process out.
		if (is_resource($input))
		{
			// Batch the process out to avoid memory limits.
			while (!feof($input))
			{
				// Read into the buffer.
				$buffer .= fread($input, 2048);

				/*
				 * If we haven't reached the end of the file, seek to the last
				 * space character and drop whatever is after that to make sure
				 * we didn't truncate a term while reading the input.
				 */
				if (!feof($input))
				{
					// Find the last space character.
					$ls = strrpos($buffer, ' ');

					// Adjust string based on the last space character.
					if ($ls)
					{
						// Truncate the string to the last space character.
						$string = substr($buffer, 0, $ls);

						// Adjust the buffer based on the last space for the next iteration
and trim.
						$buffer = StringHelper::trim(substr($buffer, $ls));
					}
					// No space character was found.
					else
					{
						$string = $buffer;
					}
				}
				// We've reached the end of the file, so parse whatever remains.
				else
				{
					$string = $buffer;
				}

				// Parse, tokenise and add tokens to the database.
				$count = $this->tokenizeToDbShort($string, $context, $lang, $format,
$count);

				unset($string, $tokens);
			}

			return $count;
		}

		// Parse, tokenise and add tokens to the database.
		$count = $this->tokenizeToDbShort($input, $context, $lang, $format,
$count);

		return $count;
	}

	/**
	 * Method to parse input, tokenise it, then add the tokens to the
database.
	 *
	 * @param   string   $input    String to parse, tokenise and add to
database.
	 * @param   integer  $context  The context of the input. See context
constants.
	 * @param   string   $lang     The language of the input.
	 * @param   string   $format   The format of the input.
	 * @param   integer  $count    The number of tokens processed so far.
	 *
	 * @return  integer  Cumulative number of tokens extracted from the input
so far.
	 *
	 * @since   3.7.0
	 */
	private function tokenizeToDbShort($input, $context, $lang, $format,
$count)
	{
		// Parse the input.
		$input = FinderIndexerHelper::parse($input, $format);

		// Check the input.
		if (empty($input))
		{
			return $count;
		}

		// Tokenize the input.
		$tokens = FinderIndexerHelper::tokenize($input, $lang);

		// Add the tokens to the database.
		$count += $this->addTokensToDb($tokens, $context);

		// Check if we're approaching the memory limit of the token table.
		if ($count >
static::$state->options->get('memory_table_limit', 30000))
		{
			$this->toggleTables(false);
		}

		return $count;
	}

	/**
	 * Method to add a set of tokens to the database.
	 *
	 * @param   mixed  $tokens   An array or single FinderIndexerToken object.
	 * @param   mixed  $context  The context of the tokens. See context
constants. [optional]
	 *
	 * @return  integer  The number of tokens inserted into the database.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function addTokensToDb($tokens, $context = '')
	{
		// Get the database object.
		$db = $this->db;

		// Count the number of token values.
		$values = 0;

		if (($tokens instanceof FinderIndexerToken) === false)
		{
			// Break into chunks of no more than 1000 items
			$chunks = count($tokens) > 1000
				? array_chunk($tokens, 1000)
				: array($tokens);

			foreach ($chunks as $chunkTokens)
			{
				// Cloning a new query template is twice as fast as calling the clear
function
				$query = clone $this->addTokensToDbQueryTemplate;

				// Iterate through the tokens to create SQL value sets.
				foreach ($chunkTokens as $token)
				{
					$query->values(
						$db->quote($token->term) . ', '
						. $db->quote($token->stem) . ', '
						. (int) $token->common . ', '
						. (int) $token->phrase . ', '
						. $db->escape((float) $token->weight) . ', '
						. (int) $context . ', '
						. $db->quote($token->language)
					);
					++$values;
				}

				$db->setQuery($query)->execute();
			}
		}
		else
		{
			$query = clone $this->addTokensToDbQueryTemplate;

			$query->values(
				$db->quote($tokens->term) . ', '
				. $db->quote($tokens->stem) . ', '
				. (int) $tokens->common . ', '
				. (int) $tokens->phrase . ', '
				. $db->escape((float) $tokens->weight) . ', '
				. (int) $context . ', '
				. $db->quote($tokens->language)
			);
			++$values;

			$db->setQuery($query)->execute();
		}

		return $values;
	}

	/**
	 * Method to switch the token tables from Memory tables to Disk tables
	 * when they are close to running out of memory.
	 * Since this is not supported/implemented in all DB-drivers, the default
is a stub method, which simply returns true.
	 *
	 * @param   boolean  $memory  Flag to control how they should be toggled.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function toggleTables($memory)
	{
		return true;
	}
}
PK[�:�{{helpers/indexer/parser/html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerParser', dirname(__DIR__) .
'/parser.php');

/**
 * HTML Parser class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerParserHtml extends FinderIndexerParser
{
	/**
	 * Method to parse input and extract the plain text. Because this method
is
	 * called from both inside and outside the indexer, it needs to be able to
	 * batch out its parsing functionality to deal with the inefficiencies of
	 * regular expressions. We will parse recursively in 2KB chunks.
	 *
	 * @param   string  $input  The input to parse.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	public function parse($input)
	{
		// Strip invalid UTF-8 characters.
		$input = iconv('utf-8', 'utf-8//IGNORE', $input);

		// Remove anything between <head> and </head> tags.  Do this
first
		// because there might be <script> or <style> tags nested
inside.
		$input = $this->removeBlocks($input, '<head>',
'</head>');

		// Convert <style> and <noscript> tags to <script> tags
		// so we can remove them efficiently.
		$search = array(
			'<style', '</style',
			'<noscript', '</noscript',
		);
		$replace = array(
			'<script', '</script',
			'<script', '</script',
		);
		$input = str_replace($search, $replace, $input);

		// Strip all script blocks.
		$input = $this->removeBlocks($input, '<script',
'</script>');

		// Decode HTML entities.
		$input = html_entity_decode($input, ENT_QUOTES, 'UTF-8');

		// Convert entities equivalent to spaces to actual spaces.
		$input = str_replace(array('&nbsp;',
'&#160;'), ' ', $input);

		// Add a space before both the OPEN and CLOSE tags of BLOCK and LINE
BREAKING elements,
		// e.g. 'all<h1><em>m</em>obile 
List</h1>' will become 'all mobile  List'
		$input = preg_replace('/(<|<\/)(' .
			'address|article|aside|blockquote|br|canvas|dd|div|dl|dt|' .
			'fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|li|'
.
			'main|nav|noscript|ol|output|p|pre|section|table|tfoot|ul|video'
.
			')\b/i', ' $1$2', $input
		);

		// Strip HTML tags.
		$input = strip_tags($input);

		return parent::parse($input);
	}

	/**
	 * Method to process HTML input and extract the plain text.
	 *
	 * @param   string  $input  The input to process.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	protected function process($input)
	{
		// Replace any amount of white space with a single space.
		return preg_replace('#\s+#u', ' ', $input);
	}

	/**
	 * Method to remove blocks of text between a start and an end tag.
	 * Each block removed is effectively replaced by a single space.
	 *
	 * Note: The start tag and the end tag must be different.
	 * Note: Blocks must not be nested.
	 * Note: This method will function correctly with multi-byte strings.
	 *
	 * @param   string  $input     String to be processed.
	 * @param   string  $startTag  String representing the start tag.
	 * @param   string  $endTag    String representing the end tag.
	 *
	 * @return  string with blocks removed.
	 *
	 * @since   3.4
	 */
	private function removeBlocks($input, $startTag, $endTag)
	{
		$return = '';
		$offset = 0;
		$startTagLength = strlen($startTag);
		$endTagLength = strlen($endTag);

		// Find the first start tag.
		$start = stripos($input, $startTag);

		// If no start tags were found, return the string unchanged.
		if ($start === false)
		{
			return $input;
		}

		// Look for all blocks defined by the start and end tags.
		while ($start !== false)
		{
			// Accumulate the substring up to the start tag.
			$return .= substr($input, $offset, $start - $offset) . ' ';

			// Look for an end tag corresponding to the start tag.
			$end = stripos($input, $endTag, $start + $startTagLength);

			// If no corresponding end tag, leave the string alone.
			if ($end === false)
			{
				// Fix the offset so part of the string is not duplicated.
				$offset = $start;
				break;
			}

			// Advance the start position.
			$offset = $end + $endTagLength;

			// Look for the next start tag and loop.
			$start = stripos($input, $startTag, $offset);
		}

		// Add in the final substring after the last end tag.
		$return .= substr($input, $offset);

		return $return;
	}
}
PK[�>LO--helpers/indexer/parser/rtf.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerParser', dirname(__DIR__) .
'/parser.php');

/**
 * RTF Parser class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerParserRtf extends FinderIndexerParser
{
	/**
	 * Method to process RTF input and extract the plain text.
	 *
	 * @param   string  $input  The input to process.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	protected function process($input)
	{
		// Remove embedded pictures.
		$input = preg_replace('#{\\\pict[^}]*}#mi', '',
$input);

		// Remove control characters.
		$input = str_replace(array('{', '}',
"\\\n"), array(' ', ' ', "\n"),
$input);
		$input = preg_replace('#\\\([^;]+?);#m', ' ',
$input);
		$input = preg_replace('#\\\[\'a-zA-Z0-9]+#mi', '
', $input);

		return $input;
	}
}
PK[�/AB��helpers/indexer/parser/txt.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerParser', dirname(__DIR__) .
'/parser.php');

/**
 * Text Parser class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerParserTxt extends FinderIndexerParser
{
	/**
	 * Method to process Text input and extract the plain text.
	 *
	 * @param   string  $input  The input to process.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	protected function process($input)
	{
		return $input;
	}
}
PK[1
�G��helpers/indexer/parser.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Parser base class for the Finder indexer package.
 *
 * @since  2.5
 */
abstract class FinderIndexerParser
{
	/**
	 * Method to get a parser, creating it if necessary.
	 *
	 * @param   string  $format  The type of parser to load.
	 *
	 * @return  FinderIndexerParser  A FinderIndexerParser instance.
	 *
	 * @since   2.5
	 * @throws  Exception on invalid parser.
	 */
	public static function getInstance($format)
	{
		static $instances;

		// Only create one parser for each format.
		if (isset($instances[$format]))
		{
			return $instances[$format];
		}

		// Create an array of instances if necessary.
		if (!is_array($instances))
		{
			$instances = array();
		}

		// Setup the adapter for the parser.
		$format = JFilterInput::getInstance()->clean($format,
'cmd');
		$path = __DIR__ . '/parser/' . $format . '.php';
		$class = 'FinderIndexerParser' . ucfirst($format);

		// Check if a parser exists for the format.
		if (!file_exists($path))
		{
			// Throw invalid format exception.
			throw new
Exception(JText::sprintf('COM_FINDER_INDEXER_INVALID_PARSER',
$format));
		}

		// Instantiate the parser.
		JLoader::register($class, $path);
		$instances[$format] = new $class;

		return $instances[$format];
	}

	/**
	 * Method to parse input and extract the plain text. Because this method
is
	 * called from both inside and outside the indexer, it needs to be able to
	 * batch out its parsing functionality to deal with the inefficiencies of
	 * regular expressions. We will parse recursively in 2KB chunks.
	 *
	 * @param   string  $input  The input to parse.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	public function parse($input)
	{
		// If the input is less than 2KB we can parse it in one go.
		if (strlen($input) <= 2048)
		{
			return $this->process($input);
		}

		// Input is longer than 2Kb so parse it in chunks of 2Kb or less.
		$start = 0;
		$end = strlen($input);
		$chunk = 2048;
		$return = null;

		while ($start < $end)
		{
			// Setup the string.
			$string = substr($input, $start, $chunk);

			// Find the last space character if we aren't at the end.
			$ls = (($start + $chunk) < $end ? strrpos($string, ' ') :
false);

			// Truncate to the last space character.
			if ($ls !== false)
			{
				$string = substr($string, 0, $ls);
			}

			// Adjust the start position for the next iteration.
			$start += ($ls !== false ? ($ls + 1 - $chunk) + $chunk : $chunk);

			// Parse the chunk.
			$return .= $this->process($string);
		}

		return $return;
	}

	/**
	 * Method to process input and extract the plain text.
	 *
	 * @param   string  $input  The input to process.
	 *
	 * @return  string  The plain text input.
	 *
	 * @since   2.5
	 */
	abstract protected function process($input);
}
PK[��,̋̋helpers/indexer/query.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;
use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;

JLoader::register('FinderIndexerHelper', __DIR__ .
'/helper.php');
JLoader::register('FinderIndexerTaxonomy', __DIR__ .
'/taxonomy.php');
JLoader::register('FinderHelperRoute', JPATH_SITE .
'/components/com_finder/helpers/route.php');
JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

/**
 * Query class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerQuery
{
	/**
	 * Flag to show whether the query can return results.
	 *
	 * @var    boolean
	 * @since  2.5
	 */
	public $search;

	/**
	 * The query input string.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $input;

	/**
	 * The language of the query.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $language;

	/**
	 * The query string matching mode.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $mode;

	/**
	 * The included tokens.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $included = array();

	/**
	 * The excluded tokens.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $excluded = array();

	/**
	 * The tokens to ignore because no matches exist.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $ignored = array();

	/**
	 * The operators used in the query input string.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $operators = array();

	/**
	 * The terms to highlight as matches.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $highlight = array();

	/**
	 * The number of matching terms for the query input.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $terms;

	/**
	 * The static filter id.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $filter;

	/**
	 * The taxonomy filters. This is a multi-dimensional array of taxonomy
	 * branches as the first level and then the taxonomy nodes as the values.
	 *
	 * For example:
	 * $filters = array(
	 *     'Type' = array(10, 32, 29, 11, ...);
	 *     'Label' = array(20, 314, 349, 91, 82, ...);
	 *        ...
	 * );
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $filters = array();

	/**
	 * The start date filter.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $date1;

	/**
	 * The end date filter.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $date2;

	/**
	 * The start date filter modifier.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $when1;

	/**
	 * The end date filter modifier.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $when2;

	/**
	 * Method to instantiate the query object.
	 *
	 * @param   array  $options  An array of query options.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public function __construct($options)
	{
		// Get the input string.
		$this->input = isset($options['input']) ?
$options['input'] : null;

		// Get the empty query setting.
		$this->empty = isset($options['empty']) ? (bool)
$options['empty'] : false;

		// Get the input language.
		$this->language = !empty($options['language']) ?
$options['language'] : FinderIndexerHelper::getDefaultLanguage();
		$this->language =
FinderIndexerHelper::getPrimaryLanguage($this->language);

		// Get the matching mode.
		$this->mode = 'AND';

		// Initialize the temporary date storage.
		$this->dates = new Registry;

		// Populate the temporary date storage.
		if (!empty($options['date1']))
		{
			$this->dates->set('date1', $options['date1']);
		}

		if (!empty($options['date2']))
		{
			$this->dates->set('date2', $options['date2']);
		}

		if (!empty($options['when1']))
		{
			$this->dates->set('when1', $options['when1']);
		}

		if (!empty($options['when2']))
		{
			$this->dates->set('when2', $options['when2']);
		}

		// Process the static taxonomy filters.
		if (!empty($options['filter']))
		{
			$this->processStaticTaxonomy($options['filter']);
		}

		// Process the dynamic taxonomy filters.
		if (!empty($options['filters']))
		{
			$this->processDynamicTaxonomy($options['filters']);
		}

		// Get the date filters.
		$d1 = $this->dates->get('date1');
		$d2 = $this->dates->get('date2');
		$w1 = $this->dates->get('when1');
		$w2 = $this->dates->get('when2');

		// Process the date filters.
		if (!empty($d1) || !empty($d2))
		{
			$this->processDates($d1, $d2, $w1, $w2);
		}

		// Process the input string.
		$this->processString($this->input, $this->language,
$this->mode);

		// Get the number of matching terms.
		foreach ($this->included as $token)
		{
			$this->terms += count($token->matches);
		}

		// Remove the temporary date storage.
		unset($this->dates);

		// Lastly, determine whether this query can return a result set.

		// Check if we have a query string.
		if (!empty($this->input))
		{
			$this->search = true;
		}
		// Check if we can search without a query string.
		elseif ($this->empty && (!empty($this->filter) ||
!empty($this->filters) || !empty($this->date1) ||
!empty($this->date2)))
		{
			$this->search = true;
		}
		// We do not have a valid search query.
		else
		{
			$this->search = false;
		}
	}

	/**
	 * Method to convert the query object into a URI string.
	 *
	 * @param   string  $base  The base URI. [optional]
	 *
	 * @return  string  The complete query URI.
	 *
	 * @since   2.5
	 */
	public function toUri($base = '')
	{
		// Set the base if not specified.
		if ($base === '')
		{
			$base = 'index.php?option=com_finder&view=search';
		}

		// Get the base URI.
		$uri = JUri::getInstance($base);

		// Add the static taxonomy filter if present.
		if ((bool) $this->filter)
		{
			$uri->setVar('f', $this->filter);
		}

		// Get the filters in the request.
		$t =
JFactory::getApplication()->input->request->get('t',
array(), 'array');

		// Add the dynamic taxonomy filters if present.
		if ((bool) $this->filters)
		{
			foreach ($this->filters as $nodes)
			{
				foreach ($nodes as $node)
				{
					if (!in_array($node, $t))
					{
						continue;
					}

					$uri->setVar('t[]', $node);
				}
			}
		}

		// Add the input string if present.
		if (!empty($this->input))
		{
			$uri->setVar('q', $this->input);
		}

		// Add the start date if present.
		if (!empty($this->date1))
		{
			$uri->setVar('d1', $this->date1);
		}

		// Add the end date if present.
		if (!empty($this->date2))
		{
			$uri->setVar('d2', $this->date2);
		}

		// Add the start date modifier if present.
		if (!empty($this->when1))
		{
			$uri->setVar('w1', $this->when1);
		}

		// Add the end date modifier if present.
		if (!empty($this->when2))
		{
			$uri->setVar('w2', $this->when2);
		}

		// Add a menu item id if one is not present.
		if (!$uri->getVar('Itemid'))
		{
			// Get the menu item id.
			$query = array(
				'view' => $uri->getVar('view'),
				'f'    => $uri->getVar('f'),
				'q'    => $uri->getVar('q'),
			);

			$item = FinderHelperRoute::getItemid($query);

			// Add the menu item id if present.
			if ($item !== null)
			{
				$uri->setVar('Itemid', $item);
			}
		}

		return $uri->toString(array('path', 'query'));
	}

	/**
	 * Method to get a list of excluded search term ids.
	 *
	 * @return  array  An array of excluded term ids.
	 *
	 * @since   2.5
	 */
	public function getExcludedTermIds()
	{
		$results = array();

		// Iterate through the excluded tokens and compile the matching terms.
		for ($i = 0, $c = count($this->excluded); $i < $c; $i++)
		{
			$results = array_merge($results, $this->excluded[$i]->matches);
		}

		// Sanitize the terms.
		$results = array_unique($results);

		return ArrayHelper::toInteger($results);
	}

	/**
	 * Method to get a list of included search term ids.
	 *
	 * @return  array  An array of included term ids.
	 *
	 * @since   2.5
	 */
	public function getIncludedTermIds()
	{
		$results = array();

		// Iterate through the included tokens and compile the matching terms.
		for ($i = 0, $c = count($this->included); $i < $c; $i++)
		{
			// Check if we have any terms.
			if (empty($this->included[$i]->matches))
			{
				continue;
			}

			// Get the term.
			$term = $this->included[$i]->term;

			// Prepare the container for the term if necessary.
			if (!array_key_exists($term, $results))
			{
				$results[$term] = array();
			}

			// Add the matches to the stack.
			$results[$term] = array_merge($results[$term],
$this->included[$i]->matches);
		}

		// Sanitize the terms.
		foreach ($results as $key => $value)
		{
			$results[$key] = array_unique($results[$key]);
			$results[$key] = ArrayHelper::toInteger($results[$key]);
		}

		return $results;
	}

	/**
	 * Method to get a list of required search term ids.
	 *
	 * @return  array  An array of required term ids.
	 *
	 * @since   2.5
	 */
	public function getRequiredTermIds()
	{
		$results = array();

		// Iterate through the included tokens and compile the matching terms.
		for ($i = 0, $c = count($this->included); $i < $c; $i++)
		{
			// Check if the token is required.
			if ($this->included[$i]->required)
			{
				// Get the term.
				$term = $this->included[$i]->term;

				// Prepare the container for the term if necessary.
				if (!array_key_exists($term, $results))
				{
					$results[$term] = array();
				}

				// Add the matches to the stack.
				$results[$term] = array_merge($results[$term],
$this->included[$i]->matches);
			}
		}

		// Sanitize the terms.
		foreach ($results as $key => $value)
		{
			$results[$key] = array_unique($results[$key]);
			$results[$key] = ArrayHelper::toInteger($results[$key]);
		}

		return $results;
	}

	/**
	 * Method to process the static taxonomy input. The static taxonomy input
	 * comes in the form of a pre-defined search filter that is assigned to
the
	 * search form.
	 *
	 * @param   integer  $filterId  The id of static filter.
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function processStaticTaxonomy($filterId)
	{
		// Get the database object.
		$db = JFactory::getDbo();

		// Initialize user variables
		$groups = implode(',',
JFactory::getUser()->getAuthorisedViewLevels());

		// Load the predefined filter.
		$query = $db->getQuery(true)
			->select('f.data, f.params')
			->from($db->quoteName('#__finder_filters') . ' AS
f')
			->where('f.filter_id = ' . (int) $filterId);

		$db->setQuery($query);
		$return = $db->loadObject();

		// Check the returned filter.
		if (empty($return))
		{
			return false;
		}

		// Set the filter.
		$this->filter = (int) $filterId;

		// Get a parameter object for the filter date options.
		$registry = new Registry($return->params);
		$params = $registry;

		// Set the dates if not already set.
		$this->dates->def('d1', $params->get('d1'));
		$this->dates->def('d2', $params->get('d2'));
		$this->dates->def('w1', $params->get('w1'));
		$this->dates->def('w2', $params->get('w2'));

		// Remove duplicates and sanitize.
		$filters = explode(',', $return->data);
		$filters = array_unique($filters);
		$filters = ArrayHelper::toInteger($filters);

		// Remove any values of zero.
		if (in_array(0, $filters, true) !== false)
		{
			unset($filters[array_search(0, $filters, true)]);
		}

		// Check if we have any real input.
		if (empty($filters))
		{
			return true;
		}

		/*
		 * Create the query to get filters from the database. We do this for
		 * two reasons: one, it allows us to ensure that the filters being used
		 * are real; two, we need to sort the filters by taxonomy branch.
		 */
		$query->clear()
			->select('t1.id, t1.title, t2.title AS branch')
			->from($db->quoteName('#__finder_taxonomy') . ' AS
t1')
			->join('INNER',
$db->quoteName('#__finder_taxonomy') . ' AS t2 ON t2.id =
t1.parent_id')
			->where('t1.state = 1')
			->where('t1.access IN (' . $groups . ')')
			->where('t1.id IN (' . implode(',', $filters) .
')')
			->where('t2.state = 1')
			->where('t2.access IN (' . $groups . ')');

		// Load the filters.
		$db->setQuery($query);
		$results = $db->loadObjectList();

		// Sort the filter ids by branch.
		foreach ($results as $result)
		{
			$this->filters[$result->branch][$result->title] = (int)
$result->id;
		}

		return true;
	}

	/**
	 * Method to process the dynamic taxonomy input. The dynamic taxonomy
input
	 * comes in the form of select fields that the user chooses from. The
	 * dynamic taxonomy input is processed AFTER the static taxonomy input
	 * because the dynamic options can be used to further narrow a static
	 * taxonomy filter.
	 *
	 * @param   array  $filters  An array of taxonomy node ids.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function processDynamicTaxonomy($filters)
	{
		// Initialize user variables
		$groups = implode(',',
JFactory::getUser()->getAuthorisedViewLevels());

		// Remove duplicates and sanitize.
		$filters = array_unique($filters);
		$filters = ArrayHelper::toInteger($filters);

		// Remove any values of zero.
		if (in_array(0, $filters, true) !== false)
		{
			unset($filters[array_search(0, $filters, true)]);
		}

		// Check if we have any real input.
		if (empty($filters))
		{
			return true;
		}

		// Get the database object.
		$db = JFactory::getDbo();

		$query = $db->getQuery(true);

		/*
		 * Create the query to get filters from the database. We do this for
		 * two reasons: one, it allows us to ensure that the filters being used
		 * are real; two, we need to sort the filters by taxonomy branch.
		 */
		$query->select('t1.id, t1.title, t2.title AS branch')
			->from($db->quoteName('#__finder_taxonomy') . ' AS
t1')
			->join('INNER',
$db->quoteName('#__finder_taxonomy') . ' AS t2 ON t2.id =
t1.parent_id')
			->where('t1.state = 1')
			->where('t1.access IN (' . $groups . ')')
			->where('t1.id IN (' . implode(',', $filters) .
')')
			->where('t2.state = 1')
			->where('t2.access IN (' . $groups . ')');

		// Load the filters.
		$db->setQuery($query);
		$results = $db->loadObjectList();

		// Cleared filter branches.
		$cleared = array();

		/*
		 * Sort the filter ids by branch. Because these filters are designed to
		 * override and further narrow the items selected in the static filter,
		 * we will clear the values from the static filter on a branch by
		 * branch basis before adding the dynamic filters. So, if the static
		 * filter defines a type filter of "articles" and three
"category"
		 * filters but the user only limits the category further, the category
		 * filters will be flushed but the type filters will not.
		 */
		foreach ($results as $result)
		{
			// Check if the branch has been cleared.
			if (!in_array($result->branch, $cleared, true))
			{
				// Clear the branch.
				$this->filters[$result->branch] = array();

				// Add the branch to the cleared list.
				$cleared[] = $result->branch;
			}

			// Add the filter to the list.
			$this->filters[$result->branch][$result->title] = (int)
$result->id;
		}

		return true;
	}

	/**
	 * Method to process the query date filters to determine start and end
	 * date limitations.
	 *
	 * @param   string  $date1  The first date filter.
	 * @param   string  $date2  The second date filter.
	 * @param   string  $when1  The first date modifier.
	 * @param   string  $when2  The second date modifier.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	protected function processDates($date1, $date2, $when1, $when2)
	{
		// Clean up the inputs.
		$date1 = trim(StringHelper::strtolower($date1));
		$date2 = trim(StringHelper::strtolower($date2));
		$when1 = trim(StringHelper::strtolower($when1));
		$when2 = trim(StringHelper::strtolower($when2));

		// Get the time offset.
		$offset = JFactory::getApplication()->get('offset');

		// Array of allowed when values.
		$whens = array('before', 'after', 'exact');

		// The value of 'today' is a special case that we need to
handle.
		if ($date1 ===
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_FILTER_TODAY')))
		{
			$date1 = JFactory::getDate('now',
$offset)->format('%Y-%m-%d');
		}

		// Try to parse the date string.
		$date = JFactory::getDate($date1, $offset);

		// Check if the date was parsed successfully.
		if ($date->toUnix() !== null)
		{
			// Set the date filter.
			$this->date1 = $date->toSql();
			$this->when1 = in_array($when1, $whens, true) ? $when1 :
'before';
		}

		// The value of 'today' is a special case that we need to
handle.
		if ($date2 ===
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_FILTER_TODAY')))
		{
			$date2 = JFactory::getDate('now',
$offset)->format('%Y-%m-%d');
		}

		// Try to parse the date string.
		$date = JFactory::getDate($date2, $offset);

		// Check if the date was parsed successfully.
		if ($date->toUnix() !== null)
		{
			// Set the date filter.
			$this->date2 = $date->toSql();
			$this->when2 = in_array($when2, $whens, true) ? $when2 :
'before';
		}

		return true;
	}

	/**
	 * Method to process the query input string and extract required,
optional,
	 * and excluded tokens; taxonomy filters; and date filters.
	 *
	 * @param   string  $input  The query input string.
	 * @param   string  $lang   The query input language.
	 * @param   string  $mode   The query matching mode.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function processString($input, $lang, $mode)
	{
		// Clean up the input string.
		$input = html_entity_decode($input, ENT_QUOTES, 'UTF-8');
		$input = StringHelper::strtolower($input);
		$input = preg_replace('#\s+#mi', ' ', $input);
		$input = trim($input);
		$debug = JFactory::getConfig()->get('debug_lang');

		/*
		 * First, we need to handle string based modifiers. String based
		 * modifiers could potentially include things like
"category:blah" or
		 * "before:2009-10-21" or "type:article", etc.
		 */
		$patterns = array(
			'before' =>
JText::_('COM_FINDER_FILTER_WHEN_BEFORE'),
			'after'  =>
JText::_('COM_FINDER_FILTER_WHEN_AFTER'),
		);

		// Add the taxonomy branch titles to the possible patterns.
		foreach (FinderIndexerTaxonomy::getBranchTitles() as $branch)
		{
			// Add the pattern.
			$patterns[$branch] =
StringHelper::strtolower(JText::_(FinderHelperLanguage::branchSingular($branch)));
		}

		// Container for search terms and phrases.
		$terms   = array();
		$phrases = array();

		// Cleared filter branches.
		$cleared = array();

		/*
		 * Compile the suffix pattern. This is used to match the values of the
		 * filter input string. Single words can be input directly, multi-word
		 * values have to be wrapped in double quotes.
		 */
		$quotes = html_entity_decode('&#8216;&#8217;&#39;',
ENT_QUOTES, 'UTF-8');
		$suffix = '(([\w\d' . $quotes .
'-]+)|\"([\w\d\s' . $quotes . '-]+)\")';

		/*
		 * Iterate through the possible filter patterns and search for matches.
		 * We need to match the key, colon, and a value pattern for the match
		 * to be valid.
		 */
		foreach ($patterns as $modifier => $pattern)
		{
			$matches = array();

			if ($debug)
			{
				$pattern = substr($pattern, 2, -2);
			}

			// Check if the filter pattern is in the input string.
			if (preg_match('#' . $pattern . '\s*:\s*' . $suffix
. '#mi', $input, $matches))
			{
				// Get the value given to the modifier.
				$value = isset($matches[3]) ? $matches[3] : $matches[1];

				// Now we have to handle the filter string.
				switch ($modifier)
				{
					// Handle a before and after date filters.
					case 'before':
					case 'after':
					{
						// Get the time offset.
						$offset = JFactory::getApplication()->get('offset');

						// Array of allowed when values.
						$whens = array('before', 'after',
'exact');

						// The value of 'today' is a special case that we need to
handle.
						if ($value ===
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_FILTER_TODAY')))
						{
							$value = JFactory::getDate('now',
$offset)->format('%Y-%m-%d');
						}

						// Try to parse the date string.
						$date = JFactory::getDate($value, $offset);

						// Check if the date was parsed successfully.
						if ($date->toUnix() !== null)
						{
							// Set the date filter.
							$this->date1 = $date->toSql();
							$this->when1 = in_array($modifier, $whens, true) ? $modifier :
'before';
						}

						break;
					}

					// Handle a taxonomy branch filter.
					default:
					{
						// Try to find the node id.
						$return = FinderIndexerTaxonomy::getNodeByTitle($modifier, $value);

						// Check if the node id was found.
						if ($return)
						{
							// Check if the branch has been cleared.
							if (!in_array($modifier, $cleared, true))
							{
								// Clear the branch.
								$this->filters[$modifier] = array();

								// Add the branch to the cleared list.
								$cleared[] = $modifier;
							}

							// Add the filter to the list.
							$this->filters[$modifier][$return->title] = (int)
$return->id;
						}

						break;
					}
				}

				// Clean up the input string again.
				$input = str_replace($matches[0], '', $input);
				$input = preg_replace('#\s+#mi', ' ', $input);
				$input = trim($input);
			}
		}

		/*
		 * Extract the tokens enclosed in double quotes so that we can handle
		 * them as phrases.
		 */
		if (StringHelper::strpos($input, '"') !== false)
		{
			$matches = array();

			// Extract the tokens enclosed in double quotes.
			if (preg_match_all('#\"([^"]+)\"#m', $input,
$matches))
			{
				/*
				 * One or more phrases were found so we need to iterate through
				 * them, tokenize them as phrases, and remove them from the raw
				 * input string before we move on to the next processing step.
				 */
				foreach ($matches[1] as $key => $match)
				{
					// Find the complete phrase in the input string.
					$pos = StringHelper::strpos($input, $matches[0][$key]);
					$len = StringHelper::strlen($matches[0][$key]);

					// Add any terms that are before this phrase to the stack.
					if (trim(StringHelper::substr($input, 0, $pos)))
					{
						$terms = array_merge($terms, explode(' ',
trim(StringHelper::substr($input, 0, $pos))));
					}

					// Strip out everything up to and including the phrase.
					$input = StringHelper::substr($input, $pos + $len);

					// Clean up the input string again.
					$input = preg_replace('#\s+#mi', ' ', $input);
					$input = trim($input);

					// Get the number of words in the phrase.
					$parts = explode(' ', $match);

					// Check if the phrase is longer than three words.
					if (count($parts) > 3)
					{
						/*
						 * If the phrase is longer than three words, we need to
						 * break it down into smaller chunks of phrases that
						 * are less than or equal to three words. We overlap
						 * the chunks so that we can ensure that a match is
						 * found for the complete phrase and not just portions
						 * of it.
						 */
						for ($i = 0, $c = count($parts); $i < $c; $i += 2)
						{
							// Set up the chunk.
							$chunk = array();

							// The chunk has to be assembled based on how many
							// pieces are available to use.
							switch ($c - $i)
							{
								/*
								 * If only one word is left, we can break from
								 * the switch and loop because the last word
								 * was already used at the end of the last
								 * chunk.
								 */
								case 1:
									break 2;

								// If there words are left, we use them both as
								// the last chunk of the phrase and we're done.
								case 2:
									$chunk[] = $parts[$i];
									$chunk[] = $parts[$i + 1];
									break;

								// If there are three or more words left, we
								// build a three word chunk and continue on.
								default:
									$chunk[] = $parts[$i];
									$chunk[] = $parts[$i + 1];
									$chunk[] = $parts[$i + 2];
									break;
							}

							// If the chunk is not empty, add it as a phrase.
							if (count($chunk))
							{
								$phrases[] = implode(' ', $chunk);
								$terms[]   = implode(' ', $chunk);
							}
						}
					}
					else
					{
						// The phrase is <= 3 words so we can use it as is.
						$phrases[] = $match;
						$terms[]   = $match;
					}
				}
			}
		}

		// Add the remaining terms if present.
		if ((bool) $input)
		{
			$terms = array_merge($terms, explode(' ', $input));
		}

		// An array of our boolean operators. $operator => $translation
		$operators = array(
			'AND' =>
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_AND')),
			'OR'  =>
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_OR')),
			'NOT' =>
StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_NOT')),
		);

		// If language debugging is enabled you need to ignore the debug strings
in matching.
		if (JDEBUG)
		{
			$debugStrings = array('**', '??');
			$operators    = str_replace($debugStrings, '', $operators);
		}

		/*
		 * Iterate through the terms and perform any sorting that needs to be
		 * done based on boolean search operators. Terms that are before an
		 * and/or/not modifier have to be handled in relation to their operator.
		 */
		for ($i = 0, $c = count($terms); $i < $c; $i++)
		{
			// Check if the term is followed by an operator that we understand.
			if (isset($terms[$i + 1]) && in_array($terms[$i + 1],
$operators, true))
			{
				// Get the operator mode.
				$op = array_search($terms[$i + 1], $operators, true);

				// Handle the AND operator.
				if ($op === 'AND' && isset($terms[$i + 2]))
				{
					// Tokenize the current term.
					$token = FinderIndexerHelper::tokenize($terms[$i], $lang, true);

					// Todo: The previous function call may return an array, which seems
not to be handled by the next one, which expects an object
					$token = $this->getTokenData($token);

					// Set the required flag.
					$token->required = true;

					// Add the current token to the stack.
					$this->included[] = $token;
					$this->highlight  = array_merge($this->highlight,
array_keys($token->matches));

					// Skip the next token (the mode operator).
					$this->operators[] = $terms[$i + 1];

					// Tokenize the term after the next term (current plus two).
					$other = FinderIndexerHelper::tokenize($terms[$i + 2], $lang, true);
					$other = $this->getTokenData($other);

					// Set the required flag.
					$other->required = true;

					// Add the token after the next token to the stack.
					$this->included[] = $other;
					$this->highlight  = array_merge($this->highlight,
array_keys($other->matches));

					// Remove the processed phrases if possible.
					if (($pk = array_search($terms[$i], $phrases, true)) !== false)
					{
						unset($phrases[$pk]);
					}

					if (($pk = array_search($terms[$i + 2], $phrases, true)) !== false)
					{
						unset($phrases[$pk]);
					}

					// Remove the processed terms.
					unset($terms[$i], $terms[$i + 1], $terms[$i + 2]);

					// Adjust the loop.
					$i += 2;
					continue;
				}
				// Handle the OR operator.
				elseif ($op === 'OR' && isset($terms[$i + 2]))
				{
					// Tokenize the current term.
					$token = FinderIndexerHelper::tokenize($terms[$i], $lang, true);
					$token = $this->getTokenData($token);

					// Set the required flag.
					$token->required = false;

					// Add the current token to the stack.
					if ((bool) $token->matches)
					{
						$this->included[] = $token;
						$this->highlight  = array_merge($this->highlight,
array_keys($token->matches));
					}
					else
					{
						$this->ignored[] = $token;
					}

					// Skip the next token (the mode operator).
					$this->operators[] = $terms[$i + 1];

					// Tokenize the term after the next term (current plus two).
					$other = FinderIndexerHelper::tokenize($terms[$i + 2], $lang, true);
					$other = $this->getTokenData($other);

					// Set the required flag.
					$other->required = false;

					// Add the token after the next token to the stack.
					if ((bool) $other->matches)
					{
						$this->included[] = $other;
						$this->highlight  = array_merge($this->highlight,
array_keys($other->matches));
					}
					else
					{
						$this->ignored[] = $other;
					}

					// Remove the processed phrases if possible.
					if (($pk = array_search($terms[$i], $phrases, true)) !== false)
					{
						unset($phrases[$pk]);
					}

					if (($pk = array_search($terms[$i + 2], $phrases, true)) !== false)
					{
						unset($phrases[$pk]);
					}

					// Remove the processed terms.
					unset($terms[$i], $terms[$i + 1], $terms[$i + 2]);

					// Adjust the loop.
					$i += 2;
					continue;
				}
			}
			// Handle an orphaned OR operator.
			elseif (isset($terms[$i + 1]) && array_search($terms[$i],
$operators, true) === 'OR')
			{
				// Skip the next token (the mode operator).
				$this->operators[] = $terms[$i];

				// Tokenize the next term (current plus one).
				$other = FinderIndexerHelper::tokenize($terms[$i + 1], $lang, true);
				$other = $this->getTokenData($other);

				// Set the required flag.
				$other->required = false;

				// Add the token after the next token to the stack.
				if ((bool) $other->matches)
				{
					$this->included[] = $other;
					$this->highlight  = array_merge($this->highlight,
array_keys($other->matches));
				}
				else
				{
					$this->ignored[] = $other;
				}

				// Remove the processed phrase if possible.
				if (($pk = array_search($terms[$i + 1], $phrases, true)) !== false)
				{
					unset($phrases[$pk]);
				}

				// Remove the processed terms.
				unset($terms[$i], $terms[$i + 1]);

				// Adjust the loop.
				$i++;
				continue;
			}
			// Handle the NOT operator.
			elseif (isset($terms[$i + 1]) && array_search($terms[$i],
$operators, true) === 'NOT')
			{
				// Skip the next token (the mode operator).
				$this->operators[] = $terms[$i];

				// Tokenize the next term (current plus one).
				$other = FinderIndexerHelper::tokenize($terms[$i + 1], $lang, true);
				$other = $this->getTokenData($other);

				// Set the required flag.
				$other->required = false;

				// Add the next token to the stack.
				if ((bool) $other->matches)
				{
					$this->excluded[] = $other;
				}
				else
				{
					$this->ignored[] = $other;
				}

				// Remove the processed phrase if possible.
				if (($pk = array_search($terms[$i + 1], $phrases, true)) !== false)
				{
					unset($phrases[$pk]);
				}

				// Remove the processed terms.
				unset($terms[$i], $terms[$i + 1]);

				// Adjust the loop.
				$i++;
				continue;
			}
		}

		/*
		 * Iterate through any search phrases and tokenize them. We handle
		 * phrases as autonomous units and do not break them down into two and
		 * three word combinations.
		 */
		for ($i = 0, $c = count($phrases); $i < $c; $i++)
		{
			// Tokenize the phrase.
			$token = FinderIndexerHelper::tokenize($phrases[$i], $lang, true);
			$token = $this->getTokenData($token);

			// Set the required flag.
			$token->required = true;

			// Add the current token to the stack.
			$this->included[] = $token;
			$this->highlight  = array_merge($this->highlight,
array_keys($token->matches));

			// Remove the processed term if possible.
			if (($pk = array_search($phrases[$i], $terms, true)) !== false)
			{
				unset($terms[$pk]);
			}

			// Remove the processed phrase.
			unset($phrases[$i]);
		}

		/*
		 * Handle any remaining tokens using the standard processing mechanism.
		 */
		if ((bool) $terms)
		{
			// Tokenize the terms.
			$terms  = implode(' ', $terms);
			$tokens = FinderIndexerHelper::tokenize($terms, $lang, false);

			// Make sure we are working with an array.
			$tokens = is_array($tokens) ? $tokens : array($tokens);

			// Get the token data and required state for all the tokens.
			foreach ($tokens as $token)
			{
				// Get the token data.
				$token = $this->getTokenData($token);

				// Set the required flag for the token.
				$token->required = $mode === 'AND' ? (!$token->phrase)
: false;

				// Add the token to the appropriate stack.
				if ($token->required || (bool) $token->matches)
				{
					$this->included[] = $token;
					$this->highlight  = array_merge($this->highlight,
array_keys($token->matches));
				}
				else
				{
					$this->ignored[] = $token;
				}
			}
		}

		return true;
	}

	/**
	 * Method to get the base and similar term ids and, if necessary,
suggested
	 * term data from the database. The terms ids are identified based on a
	 * 'like' match in MySQL and/or a common stem. If no term ids
could be
	 * found, then we know that we will not be able to return any results for
	 * that term and we should try to find a similar term to use that we can
	 * match so that we can suggest the alternative search query to the user.
	 *
	 * @param   FinderIndexerToken  $token  A FinderIndexerToken object.
	 *
	 * @return  FinderIndexerToken  A FinderIndexerToken object.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected function getTokenData($token)
	{
		// Get the database object.
		$db = JFactory::getDbo();

		// Create a database query to build match the token.
		$query = $db->getQuery(true)
			->select('t.term, t.term_id')
			->from('#__finder_terms AS t');

		/*
		 * If the token is a phrase, the lookup process is fairly simple. If
		 * the token is a word, it is a little more complicated. We have to
		 * create two queries to lookup the term and the stem respectively,
		 * then union the result sets together. This is MUCH faster than using
		 * an or condition in the database query.
		 */
		if ($token->phrase)
		{
			// Add the phrase to the query.
			$query->where('t.term = ' . $db->quote($token->term))
				->where('t.phrase = 1');
		}
		else
		{
			// Add the term to the query.
			$query->where('t.term = ' . $db->quote($token->term))
				->where('t.phrase = 0');

			// Clone the query, replace the WHERE clause.
			$sub = clone $query;
			$sub->clear('where');
			$sub->where('t.stem = ' . $db->quote($token->stem));
			$sub->where('t.phrase = 0');

			// Union the two queries.
			$query->union($sub);
		}

		// Get the terms.
		$db->setQuery($query);
		$matches = $db->loadObjectList();

		// Check the matching terms.
		if ((bool) $matches)
		{
			// Add the matches to the token.
			for ($i = 0, $c = count($matches); $i < $c; $i++)
			{
				$token->matches[$matches[$i]->term] = (int)
$matches[$i]->term_id;
			}
		}

		// If no matches were found, try to find a similar but better token.
		if (empty($token->matches))
		{
			// Create a database query to get the similar terms.
			// TODO: PostgreSQL doesn't support SOUNDEX out of the box
			$query->clear()
				->select('DISTINCT t.term_id AS id, t.term AS term')
				->from('#__finder_terms AS t')
				// ->where('t.soundex = ' .
soundex($db->quote($token->term)))
				->where('t.soundex = SOUNDEX(' .
$db->quote($token->term) . ')')
				->where('t.phrase = ' . (int) $token->phrase);

			// Get the terms.
			$db->setQuery($query);
			$results = $db->loadObjectList();

			// Check if any similar terms were found.
			if (empty($results))
			{
				return $token;
			}

			// Stack for sorting the similar terms.
			$suggestions = array();

			// Get the levnshtein distance for all suggested terms.
			foreach ($results as $sk => $st)
			{
				// Get the levenshtein distance between terms.
				$distance = levenshtein($st->term, $token->term);

				// Make sure the levenshtein distance isn't over 50.
				if ($distance < 50)
				{
					$suggestions[$sk] = $distance;
				}
			}

			// Sort the suggestions.
			asort($suggestions, SORT_NUMERIC);

			// Get the closest match.
			$keys = array_keys($suggestions);
			$key  = $keys[0];

			// Add the suggested term.
			$token->suggestion = $results[$key]->term;
		}

		return $token;
	}
}
PK[��FL""helpers/indexer/result.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexer', __DIR__ .
'/indexer.php');

/**
 * Result class for the Finder indexer package.
 *
 * This class uses magic __get() and __set() methods to prevent properties
 * being added that might confuse the system. All properties not explicitly
 * declared will be pushed into the elements array and can be accessed
 * explicitly using the getElement() method.
 *
 * @since  2.5
 */
class FinderIndexerResult
{
	/**
	 * An array of extra result properties.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $elements = array();

	/**
	 * This array tells the indexer which properties should be indexed and
what
	 * weights to use for those properties.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $instructions = array(
		FinderIndexer::TITLE_CONTEXT => array('title',
'subtitle', 'id'),
		FinderIndexer::TEXT_CONTEXT  => array('summary',
'body'),
		FinderIndexer::META_CONTEXT  => array('meta',
'list_price', 'sale_price'),
		FinderIndexer::PATH_CONTEXT  => array('path',
'alias'),
		FinderIndexer::MISC_CONTEXT  => array('comments'),
	);

	/**
	 * The indexer will use this data to create taxonomy mapping entries for
	 * the item so that it can be filtered by type, label, category,
	 * or whatever.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $taxonomy = array();

	/**
	 * The content URL.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $url;

	/**
	 * The content route.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $route;

	/**
	 * The content title.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $title;

	/**
	 * The content description.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $description;

	/**
	 * The published state of the result.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $published;

	/**
	 * The content published state.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $state;

	/**
	 * The content access level.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $access;

	/**
	 * The content language.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $language = '*';

	/**
	 * The publishing start date.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $publish_start_date;

	/**
	 * The publishing end date.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $publish_end_date;

	/**
	 * The generic start date.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $start_date;

	/**
	 * The generic end date.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $end_date;

	/**
	 * The item list price.
	 *
	 * @var    mixed
	 * @since  2.5
	 */
	public $list_price;

	/**
	 * The item sale price.
	 *
	 * @var    mixed
	 * @since  2.5
	 */
	public $sale_price;

	/**
	 * The content type id. This is set by the adapter.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $type_id;

	/**
	 * The default language for content.
	 *
	 * @var    string
	 * @since  3.0.2
	 */
	public $defaultLanguage;

	/**
	 * Constructor
	 *
	 * @since   3.0.3
	 */
	public function __construct()
	{
		$this->defaultLanguage =
JComponentHelper::getParams('com_languages')->get('site',
'en-GB');
	}

	/**
	 * The magic set method is used to push additional values into the
elements
	 * array in order to preserve the cleanliness of the object.
	 *
	 * @param   string  $name   The name of the element.
	 * @param   mixed   $value  The value of the element.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function __set($name, $value)
	{
		$this->setElement($name, $value);
	}

	/**
	 * The magic get method is used to retrieve additional element values from
the elements array.
	 *
	 * @param   string  $name  The name of the element.
	 *
	 * @return  mixed  The value of the element if set, null otherwise.
	 *
	 * @since   2.5
	 */
	public function __get($name)
	{
		return $this->getElement($name);
	}

	/**
	 * The magic isset method is used to check the state of additional element
values in the elements array.
	 *
	 * @param   string  $name  The name of the element.
	 *
	 * @return  boolean  True if set, false otherwise.
	 *
	 * @since   2.5
	 */
	public function __isset($name)
	{
		return isset($this->elements[$name]);
	}

	/**
	 * The magic unset method is used to unset additional element values in
the elements array.
	 *
	 * @param   string  $name  The name of the element.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function __unset($name)
	{
		unset($this->elements[$name]);
	}

	/**
	 * Method to retrieve additional element values from the elements array.
	 *
	 * @param   string  $name  The name of the element.
	 *
	 * @return  mixed  The value of the element if set, null otherwise.
	 *
	 * @since   2.5
	 */
	public function getElement($name)
	{
		// Get the element value if set.
		if (array_key_exists($name, $this->elements))
		{
			return $this->elements[$name];
		}

		return null;
	}

	/**
	 * Method to retrieve all elements.
	 *
	 * @return  array  The elements
	 *
	 * @since   3.8.3
	 */
	public function getElements()
	{
		return $this->elements;
	}

	/**
	 * Method to set additional element values in the elements array.
	 *
	 * @param   string  $name   The name of the element.
	 * @param   mixed   $value  The value of the element.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function setElement($name, $value)
	{
		$this->elements[$name] = $value;
	}

	/**
	 * Method to get all processing instructions.
	 *
	 * @return  array  An array of processing instructions.
	 *
	 * @since   2.5
	 */
	public function getInstructions()
	{
		return $this->instructions;
	}

	/**
	 * Method to add a processing instruction for an item property.
	 *
	 * @param   string  $group     The group to associate the property with.
	 * @param   string  $property  The property to process.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function addInstruction($group, $property)
	{
		// Check if the group exists. We can't add instructions for unknown
groups.
		// Check if the property exists in the group.
		if (array_key_exists($group, $this->instructions) &&
!in_array($property, $this->instructions[$group], true))
		{
			// Add the property to the group.
			$this->instructions[$group][] = $property;
		}
	}

	/**
	 * Method to remove a processing instruction for an item property.
	 *
	 * @param   string  $group     The group to associate the property with.
	 * @param   string  $property  The property to process.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function removeInstruction($group, $property)
	{
		// Check if the group exists. We can't remove instructions for
unknown groups.
		if (array_key_exists($group, $this->instructions))
		{
			// Search for the property in the group.
			$key = array_search($property, $this->instructions[$group]);

			// If the property was found, remove it.
			if ($key !== false)
			{
				unset($this->instructions[$group][$key]);
			}
		}
	}

	/**
	 * Method to get the taxonomy maps for an item.
	 *
	 * @param   string  $branch  The taxonomy branch to get. [optional]
	 *
	 * @return  array  An array of taxonomy maps.
	 *
	 * @since   2.5
	 */
	public function getTaxonomy($branch = null)
	{
		// Get the taxonomy branch if available.
		if ($branch !== null && isset($this->taxonomy[$branch]))
		{
			// Filter the input.
			$branch =
preg_replace('#[^\pL\pM\pN\p{Pi}\p{Pf}\'+-.,_]+#mui', '
', $branch);

			return $this->taxonomy[$branch];
		}

		return $this->taxonomy;
	}

	/**
	 * Method to add a taxonomy map for an item.
	 *
	 * @param   string   $branch  The title of the taxonomy branch to add the
node to.
	 * @param   string   $title   The title of the taxonomy node.
	 * @param   integer  $state   The published state of the taxonomy node.
[optional]
	 * @param   integer  $access  The access level of the taxonomy node.
[optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function addTaxonomy($branch, $title, $state = 1, $access = 1)
	{
		// Filter the input.
		$branch =
preg_replace('#[^\pL\pM\pN\p{Pi}\p{Pf}\'+-.,_]+#mui', '
', $branch);

		// Create the taxonomy node.
		$node = new JObject;
		$node->title = $title;
		$node->state = (int) $state;
		$node->access = (int) $access;

		// Add the node to the taxonomy branch.
		$this->taxonomy[$branch][$node->title] = $node;
	}

	/**
	 * Method to set the item language
	 *
	 * @return  void
	 *
	 * @since   3.0
	 */
	public function setLanguage()
	{
		if ($this->language == '')
		{
			$this->language = $this->defaultLanguage;
		}
	}
}
PK[��h(h(helpers/indexer/stemmer/fr.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerStemmer', dirname(__DIR__) .
'/stemmer.php');

/**
 * French stemmer class for Smart Search indexer.
 *
 * First contributed by Eric Sanou (bobotche@hotmail.fr)
 * This class is inspired in  Alexis Ulrich's French stemmer code
(http://alx2002.free.fr)
 *
 * @since  3.0
 */
class FinderIndexerStemmerFr extends FinderIndexerStemmer
{
	/**
	 * Stemming rules.
	 *
	 * @var    array
	 * @since  3.0
	 */
	private static $stemRules;

	/**
	 * Method to stem a token and return the root.
	 *
	 * @param   string  $token  The token to stem.
	 * @param   string  $lang   The language of the token.
	 *
	 * @return  string  The root token.
	 *
	 * @since   3.0
	 */
	public function stem($token, $lang)
	{
		// Check if the token is long enough to merit stemming.
		if (strlen($token) <= 2)
		{
			return $token;
		}

		// Check if the language is French or All.
		if ($lang !== 'fr' && $lang !== '*')
		{
			return $token;
		}

		// Stem the token if it is not in the cache.
		if (!isset($this->cache[$lang][$token]))
		{
			// Stem the token.
			$result = self::getStem($token);

			// Add the token to the cache.
			$this->cache[$lang][$token] = $result;
		}

		return $this->cache[$lang][$token];
	}

	/**
	 * French stemmer rules variables.
	 *
	 * @return  array  The rules
	 *
	 * @since   3.0
	 */
	protected static function getStemRules()
	{
		if (self::$stemRules)
		{
			return self::$stemRules;
		}

		$vars = array();

		// French accented letters in ISO-8859-1 encoding
		$vars['accents'] = chr(224) . chr(226) . chr(232) . chr(233) .
chr(234) . chr(235) . chr(238) . chr(239)
			. chr(244) . chr(251) . chr(249) . chr(231);

		// The rule patterns include all accented words for french language
		$vars['rule_pattern'] = '/^([a-z' .
$vars['accents'] . ']*)(\*){0,1}(\d)([a-z' .
$vars['accents'] . ']*)([.|>])/';

		// French vowels (including y) in ISO-8859-1 encoding
		$vars['vowels'] = chr(97) . chr(224) . chr(226) . chr(101) .
chr(232) . chr(233) . chr(234) . chr(235)
			. chr(105) . chr(238) . chr(239) . chr(111) . chr(244) . chr(117) .
chr(251) . chr(249) . chr(121);

		// The French rules in ISO-8859-1 encoding
		$vars['rules'] = array(
			'esre1>', 'esio1>', 'siol1.',
'siof0.', 'sioe0.', 'sio3>',
'st1>', 'sf1>', 'sle1>',
'slo1>', 's' . chr(233) . '1>',
chr(233) . 'tuae5.',
			chr(233) . 'tuae2.', 'tnia0.', 'tniv1.',
'tni3>', 'suor1.', 'suo0.',
'sdrail5.', 'sdrai4.', 'er' . chr(232) .
'i1>', 'sesue3x>',
			'esuey5i.', 'esue2x>', 'se1>',
'er' . chr(232) . 'g3.', 'eca1>',
'esiah0.', 'esi1>', 'siss2.',
'sir2>', 'sit2>', 'egan' . chr(233) .
'1.',
			'egalli6>', 'egass1.', 'egas0.',
'egat3.', 'ega3>', 'ette4>',
'ett2>', 'etio1.', 'tio' . chr(231) .
'4c.', 'tio0.', 'et1>',
'eb1>',
			'snia1>', 'eniatnau8>', 'eniatn4.',
'enia1>', 'niatnio3.', 'niatg3.',
'e' . chr(233) . '1>', chr(233) .
'hcat1.', chr(233) . 'hca4.',
			chr(233) . 'tila5>', chr(233) . 'tici5.',
chr(233) . 'tir1.', chr(233) . 'ti3>', chr(233) .
'gan1.', chr(233) . 'ga3>',
			chr(233) . 'tehc1.', chr(233) . 'te3>', chr(233)
. 'it0.', chr(233) . '1>', 'eire4.',
'eirue5.', 'eio1.', 'eia1.',
'ei1>', 'eng1.',
			'xuaessi7.', 'xuae1>', 'uaes0.',
'uae3.', 'xuave2l.', 'xuav2li>',
'xua3la>', 'ela1>', 'lart2.',
'lani2>', 'la' . chr(233) . '2>',
			'siay4i.', 'siassia7.', 'siarv1*.',
'sia1>', 'tneiayo6i.', 'tneiay6i.',
'tneiassia9.', 'tneiareio7.', 'tneia5>',
'tneia4>', 'tiario4.',
			'tiarim3.', 'tiaria3.', 'tiaris3.',
'tiari5.', 'tiarve6>', 'tiare5>',
'iare4>', 'are3>', 'tiay4i.',
'tia3>', 'tnay4i.',
			'em' . chr(232) . 'iu5>', 'em' .
chr(232) . 'i4>', 'tnaun3.', 'tnauqo3.',
'tnau4>', 'tnaf0.', 'tnat' . chr(233) .
'2>', 'tna3>', 'tno3>',
			'zeiy4i.', 'zey3i.', 'zeire5>',
'zeird4.', 'zeirio4.', 'ze2>',
'ssiab0.', 'ssia4.', 'ssi3.',
'tnemma6>', 'tnemesuey9i.',
'tnemesue8>',
			'tnemevi7.', 'tnemessia5.', 'tnemessi8.',
'tneme5>', 'tnemia4.', 'tnem' . chr(233) .
'5>', 'el2l>', 'lle3le>',
'let' . chr(244) . '0.',
			'lepp0.', 'le2>', 'srei1>',
'reit3.', 'reila2.', 'rei3>',
'ert' . chr(226) . 'e5.', 'ert' . chr(226) .
chr(233) . '1.',
			'ert' . chr(226) . '4.', 'drai4.',
'erdro0.', 'erute5.', 'ruta0.',
'eruta1.', 'erutiov1.', 'erub3.',
'eruh3.', 'erul3.', 'er2r>',
'nn1>',
			'r' . chr(232) . 'i3.', 'srev0.',
'sr1>', 'rid2>', 're2>',
'xuei4.', 'esuei5.', 'lbati3.',
'lba3>', 'rueis0.', 'ruehcn4.',
'ecirta6.',
			'ruetai6.', 'rueta5.', 'rueir0.',
'rue3>', 'esseti6.', 'essere6>',
'esserd1.', 'esse4>', 'essiab1.',
'essia5.', 'essio1.', 'essi4.',
			'essal4.', 'essa1>', 'ssab1.',
'essurp1.', 'essu4.', 'essi1.',
'ssor1.', 'essor2.', 'esso1>',
'ess2>', 'tio3.', 'r' . chr(232) .
's2re.',
			'r' . chr(232) . '0e.', 'esn1.',
'eu1>', 'sua0.', 'su1>',
'utt1>', 'tu' . chr(231) . '3c.',
'u' . chr(231) . '2c.', 'ur1.',
'ehcn2>',
			'ehcu1>', 'snorr3.', 'snoru3.',
'snorua3.', 'snorv3.', 'snorio4.',
'snori5.', 'snore5>', 'snortt4>',
'snort' . chr(238) . 'a7.', 'snort3.',
			'snor4.', 'snossi6.', 'snoire6.',
'snoird5.', 'snoitai7.', 'snoita6.',
'snoits1>', 'noits0.', 'snoi4>',
'noitaci7>', 'noitai6.', 'noita5.',
			'noitu4.', 'noi3>', 'snoya0.',
'snoy4i.', 'sno' . chr(231) . 'a1.',
'sno' . chr(231) . 'r1.', 'snoe4.',
'snosiar1>', 'snola1.', 'sno3>',
			'sno1>', 'noll2.', 'tnennei4.',
'ennei2>', 'snei1>', 'sne' . chr(233) .
'1>', 'enne' . chr(233) . '5e.',
'ne' . chr(233) . '3e.', 'neic0.',
			'neiv0.', 'nei3.', 'sc1.',
'sd1.', 'sg1.', 'sni1.', 'tiu0.',
'ti2.', 'sp1>', 'sna1>',
'sue1.', 'enn2>', 'nong2.',
'noss2.', 'rioe4.',
			'riot0.', 'riorc1.', 'riovec5.',
'rio3.', 'ric2.', 'ril2.',
'tnerim3.', 'tneris3>', 'tneri5.',
't' . chr(238) . 'a3.', 'riss2.',
			't' . chr(238) . '2.', 't' . chr(226) .
'2>', 'ario2.', 'arim1.',
'ara1.', 'aris1.', 'ari3.',
'art1>', 'ardn2.', 'arr1.',
'arua1.',
			'aro1.', 'arv1.', 'aru1.',
'ar2.', 'rd1.', 'ud1.', 'ul1.',
'ini1.', 'rin2.', 'tnessiab3.',
'tnessia7.', 'tnessi6.', 'tnessni4.',
'sini2.',
			'sl1.', 'iard3.', 'iario3.',
'ia2>', 'io0.', 'iule2.',
'i1>', 'sid2.', 'sic2.',
'esoi4.', 'ed1.', 'ai2>',
'a1>', 'adr1.',
			'tner' . chr(232) . '5>', 'evir1.',
'evio4>', 'evi3.', 'fita4.',
'fi2>', 'enie1.', 'sare4>',
'sari4>', 'sard3.', 'sart2>',
'sa2.',
			'tnessa6>', 'tnessu6>', 'tnegna3.',
'tnegi3.', 'tneg0.', 'tneru5>',
'tnemg0.', 'tnerni4.', 'tneiv1.',
'tne3>', 'une1.', 'en1>',
'nitn2.',
			'ecnay5i.', 'ecnal1.', 'ecna4.',
'ec1>', 'nn1.', 'rit2>',
'rut2>', 'rud2.', 'ugn1>',
'eg1>', 'tuo0.', 'tul2>', 't'
. chr(251) . '2>',
			'ev1>', 'v' . chr(232) . '2ve>',
'rtt1>', 'emissi6.', 'em1.',
'ehc1.', 'c' . chr(233) . 'i2c' . chr(232) .
'.', 'libi2l.', 'llie1.',
			'liei4i.', 'xuev1.', 'xuey4i.',
'xueni5>', 'xuell4.', 'xuere5.',
'xue3>', 'rb' . chr(233) . '3rb' .
chr(232) . '.', 'tur2.',
			'rir' . chr(233) . '4re.', 'rir2.',
'c' . chr(226) . '2ca.', 'snu1.',
'rt' . chr(238) . 'a4.', 'long2.',
'vec2.', chr(231) . '1c>',
			'ssilp3.', 'silp2.', 't' . chr(232) .
'hc2te.', 'n' . chr(232) . 'm2ne.',
'llepp1.', 'tan2.', 'rv' . chr(232) .
'3rve.',
			'rv' . chr(233) . '3rve.', 'r' . chr(232)
. '2re.', 'r' . chr(233) . '2re.',
't' . chr(232) . '2te.', 't' . chr(233) .
'2te.', 'epp1.',
			'eya2i.', 'ya1i.', 'yo1i.',
'esu1.', 'ugi1.', 'tt1.', 'end0.'
		);

		self::$stemRules = $vars;

		return self::$stemRules;
	}

	/**
	 * Returns the number of the first rule from the rule number
	 * that can be applied to the given reversed input.
	 * returns -1 if no rule can be applied, ie the stem has been found
	 *
	 * @param   string   $reversedInput  The input to check in reversed order
	 * @param   integer  $ruleNumber     The rule number to check
	 *
	 * @return  integer  Number of the first rule
	 *
	 * @since   3.0
	 */
	private static function getFirstRule($reversedInput, $ruleNumber)
	{
		$vars = static::getStemRules();

		$nb_rules = count($vars['rules']);

		for ($i = $ruleNumber; $i < $nb_rules; $i++)
		{
			// Gets the letters from the current rule
			$rule = $vars['rules'][$i];
			$rule = preg_replace($vars['rule_pattern'], "\\1",
$rule);

			if (strncasecmp(utf8_decode($rule), $reversedInput,
strlen(utf8_decode($rule))) == 0)
			{
				return $i;
			}
		}

		return -1;
	}

	/**
	 * Check the acceptability of a stem for French language
	 *
	 * @param   string  $reversedStem  The stem to check in reverse form
	 *
	 * @return  boolean  True if stem is acceptable
	 *
	 * @since   3.0
	 */
	private static function check($reversedStem)
	{
		$vars = static::getStemRules();

		if (preg_match('/[' . $vars['vowels'] .
']$/', utf8_encode($reversedStem)))
		{
			// If the form starts with a vowel then at least two letters must remain
after stemming (e.g.: "etaient" --> "et")
			return (strlen($reversedStem) > 2);
		}
		else
		{
			// If the reversed stem starts with a consonant then at least two
letters must remain after stemming
			if (strlen($reversedStem) <= 2)
			{
				return false;
			}

			// And at least one of these must be a vowel or "y"
			return preg_match('/[' . $vars['vowels'] .
']/', utf8_encode($reversedStem));
		}
	}

	/**
	 * Paice/Husk stemmer which returns a stem for the given $input
	 *
	 * @param   string  $input  The word for which we want the stem in UTF-8
	 *
	 * @return  string  The stem
	 *
	 * @since   3.0
	 */
	private static function getStem($input)
	{
		$vars = static::getStemRules();

		$reversed_input = strrev(utf8_decode($input));
		$rule_number = 0;

		// This loop goes through the rules' array until it finds an ending
one (ending by '.') or the last one ('end0.')
		while (true)
		{
			$rule_number = self::getFirstRule($reversed_input, $rule_number);

			if ($rule_number === -1)
			{
				// No other rule can be applied => the stem has been found
				break;
			}

			$rule = $vars['rules'][$rule_number];
			preg_match($vars['rule_pattern'], $rule, $matches);

			$reversed_stem = utf8_decode($matches[4]) . substr($reversed_input,
$matches[3]);

			if (self::check($reversed_stem))
			{
				$reversed_input = $reversed_stem;

				if ($matches[5] === '.')
				{
					break;
				}
			}
			else
			{
				// Go to another rule
				$rule_number++;
			}
		}

		return utf8_encode(strrev($reversed_input));
	}
}
PK[�(''%helpers/indexer/stemmer/porter_en.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerStemmer', dirname(__DIR__) .
'/stemmer.php');

/**
 * Porter English stemmer class for the Finder indexer package.
 *
 * This class was adapted from one written by Richard Heyes.
 * See copyright and link information above.
 *
 * @since  2.5
 */
class FinderIndexerStemmerPorter_En extends FinderIndexerStemmer
{
	/**
	 * Regex for matching a consonant.
	 *
	 * @var    string
	 * @since  2.5
	 */
	private static $regex_consonant =
'(?:[bcdfghjklmnpqrstvwxz]|(?<=[aeiou])y|^y)';

	/**
	 * Regex for matching a vowel
	 *
	 * @var    string
	 * @since  2.5
	 */
	private static $regex_vowel = '(?:[aeiou]|(?<![aeiou])y)';

	/**
	 * Method to stem a token and return the root.
	 *
	 * @param   string  $token  The token to stem.
	 * @param   string  $lang   The language of the token.
	 *
	 * @return  string  The root token.
	 *
	 * @since   2.5
	 */
	public function stem($token, $lang)
	{
		// Check if the token is long enough to merit stemming.
		if (strlen($token) <= 2)
		{
			return $token;
		}

		// Check if the language is English or All.
		if ($lang !== 'en' && $lang !== '*')
		{
			return $token;
		}

		// Stem the token if it is not in the cache.
		if (!isset($this->cache[$lang][$token]))
		{
			// Stem the token.
			$result = $token;
			$result = self::step1ab($result);
			$result = self::step1c($result);
			$result = self::step2($result);
			$result = self::step3($result);
			$result = self::step4($result);
			$result = self::step5($result);

			// Add the token to the cache.
			$this->cache[$lang][$token] = $result;
		}

		return $this->cache[$lang][$token];
	}

	/**
	 * Step 1
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step1ab($word)
	{
		// Part a
		if (substr($word, -1) === 's')
		{
			self::replace($word, 'sses', 'ss')
			|| self::replace($word, 'ies', 'i')
			|| self::replace($word, 'ss', 'ss')
			|| self::replace($word, 's', '');
		}

		// Part b
		if (substr($word, -2, 1) !== 'e' || !self::replace($word,
'eed', 'ee', 0))
		{
			// First rule
			$v = self::$regex_vowel;

			// Words ending with ing and ed
			// Note use of && and OR, for precedence reasons
			if (preg_match("#$v+#", substr($word, 0, -3)) &&
self::replace($word, 'ing', '')
				|| preg_match("#$v+#", substr($word, 0, -2)) &&
self::replace($word, 'ed', ''))
			{
				// If one of above two test successful
				if (!self::replace($word, 'at', 'ate') &&
!self::replace($word, 'bl', 'ble') &&
!self::replace($word, 'iz', 'ize'))
				{
					// Double consonant ending
					$wordSubStr = substr($word, -2);

					if ($wordSubStr !== 'll' && $wordSubStr !==
'ss' && $wordSubStr !== 'zz' &&
self::doubleConsonant($word))
					{
						$word = substr($word, 0, -1);
					}
					elseif (self::m($word) === 1 && self::cvc($word))
					{
						$word .= 'e';
					}
				}
			}
		}

		return $word;
	}

	/**
	 * Step 1c
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step1c($word)
	{
		$v = self::$regex_vowel;

		if (substr($word, -1) === 'y' &&
preg_match("#$v+#", substr($word, 0, -1)))
		{
			self::replace($word, 'y', 'i');
		}

		return $word;
	}

	/**
	 * Step 2
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step2($word)
	{
		switch (substr($word, -2, 1))
		{
			case 'a':
				self::replace($word, 'ational', 'ate', 0)
				|| self::replace($word, 'tional', 'tion', 0);
				break;
			case 'c':
				self::replace($word, 'enci', 'ence', 0)
				|| self::replace($word, 'anci', 'ance', 0);
				break;
			case 'e':
				self::replace($word, 'izer', 'ize', 0);
				break;
			case 'g':
				self::replace($word, 'logi', 'log', 0);
				break;
			case 'l':
				self::replace($word, 'entli', 'ent', 0)
				|| self::replace($word, 'ousli', 'ous', 0)
				|| self::replace($word, 'alli', 'al', 0)
				|| self::replace($word, 'bli', 'ble', 0)
				|| self::replace($word, 'eli', 'e', 0);
				break;
			case 'o':
				self::replace($word, 'ization', 'ize', 0)
				|| self::replace($word, 'ation', 'ate', 0)
				|| self::replace($word, 'ator', 'ate', 0);
				break;
			case 's':
				self::replace($word, 'iveness', 'ive', 0)
				|| self::replace($word, 'fulness', 'ful', 0)
				|| self::replace($word, 'ousness', 'ous', 0)
				|| self::replace($word, 'alism', 'al', 0);
				break;
			case 't':
				self::replace($word, 'biliti', 'ble', 0)
				|| self::replace($word, 'aliti', 'al', 0)
				|| self::replace($word, 'iviti', 'ive', 0);
				break;
		}

		return $word;
	}

	/**
	 * Step 3
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step3($word)
	{
		switch (substr($word, -2, 1))
		{
			case 'a':
				self::replace($word, 'ical', 'ic', 0);
				break;
			case 's':
				self::replace($word, 'ness', '', 0);
				break;
			case 't':
				self::replace($word, 'icate', 'ic', 0)
				|| self::replace($word, 'iciti', 'ic', 0);
				break;
			case 'u':
				self::replace($word, 'ful', '', 0);
				break;
			case 'v':
				self::replace($word, 'ative', '', 0);
				break;
			case 'z':
				self::replace($word, 'alize', 'al', 0);
				break;
		}

		return $word;
	}

	/**
	 * Step 4
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step4($word)
	{
		switch (substr($word, -2, 1))
		{
			case 'a':
				self::replace($word, 'al', '', 1);
				break;
			case 'c':
				self::replace($word, 'ance', '', 1)
				|| self::replace($word, 'ence', '', 1);
				break;
			case 'e':
				self::replace($word, 'er', '', 1);
				break;
			case 'i':
				self::replace($word, 'ic', '', 1);
				break;
			case 'l':
				self::replace($word, 'able', '', 1)
				|| self::replace($word, 'ible', '', 1);
				break;
			case 'n':
				self::replace($word, 'ant', '', 1)
				|| self::replace($word, 'ement', '', 1)
				|| self::replace($word, 'ment', '', 1)
				|| self::replace($word, 'ent', '', 1);
				break;
			case 'o':
				$wordSubStr = substr($word, -4);

				if ($wordSubStr === 'tion' || $wordSubStr ===
'sion')
				{
					self::replace($word, 'ion', '', 1);
				}
				else
				{
					self::replace($word, 'ou', '', 1);
				}
				break;
			case 's':
				self::replace($word, 'ism', '', 1);
				break;
			case 't':
				self::replace($word, 'ate', '', 1)
				|| self::replace($word, 'iti', '', 1);
				break;
			case 'u':
				self::replace($word, 'ous', '', 1);
				break;
			case 'v':
				self::replace($word, 'ive', '', 1);
				break;
			case 'z':
				self::replace($word, 'ize', '', 1);
				break;
		}

		return $word;
	}

	/**
	 * Step 5
	 *
	 * @param   string  $word  The token to stem.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	private static function step5($word)
	{
		// Part a
		if (substr($word, -1) === 'e')
		{
			if (self::m(substr($word, 0, -1)) > 1)
			{
				self::replace($word, 'e', '');
			}
			elseif (self::m(substr($word, 0, -1)) === 1)
			{
				if (!self::cvc(substr($word, 0, -1)))
				{
					self::replace($word, 'e', '');
				}
			}
		}

		// Part b
		if (self::m($word) > 1 && self::doubleConsonant($word)
&& substr($word, -1) === 'l')
		{
			$word = substr($word, 0, -1);
		}

		return $word;
	}

	/**
	 * Replaces the first string with the second, at the end of the string. If
third
	 * arg is given, then the preceding string must match that m count at
least.
	 *
	 * @param   string   $str    String to check
	 * @param   string   $check  Ending to check for
	 * @param   string   $repl   Replacement string
	 * @param   integer  $m      Optional minimum number of m() to meet
	 *
	 * @return  boolean  Whether the $check string was at the end
	 *                   of the $str string. True does not necessarily mean
	 *                   that it was replaced.
	 *
	 * @since   2.5
	 */
	private static function replace(&$str, $check, $repl, $m = null)
	{
		$len = 0 - strlen($check);

		if (substr($str, $len) === $check)
		{
			$substr = substr($str, 0, $len);

			if ($m === null || self::m($substr) > $m)
			{
				$str = $substr . $repl;
			}

			return true;
		}

		return false;
	}

	/**
	 * m() measures the number of consonant sequences in $str. if c is
	 * a consonant sequence and v a vowel sequence, and <..> indicates
arbitrary
	 * presence,
	 *
	 * <c><v>       gives 0
	 * <c>vc<v>     gives 1
	 * <c>vcvc<v>   gives 2
	 * <c>vcvcvc<v> gives 3
	 *
	 * @param   string  $str  The string to return the m count for
	 *
	 * @return  integer  The m count
	 *
	 * @since   2.5
	 */
	private static function m($str)
	{
		$c = self::$regex_consonant;
		$v = self::$regex_vowel;

		$str = preg_replace("#^$c+#", '', $str);
		$str = preg_replace("#$v+$#", '', $str);

		preg_match_all("#($v+$c+)#", $str, $matches);

		return count($matches[1]);
	}

	/**
	 * Returns true/false as to whether the given string contains two
	 * of the same consonant next to each other at the end of the string.
	 *
	 * @param   string  $str  String to check
	 *
	 * @return  boolean  Result
	 *
	 * @since   2.5
	 */
	private static function doubleConsonant($str)
	{
		$c = self::$regex_consonant;

		return preg_match("#$c{2}$#", $str, $matches) &&
$matches[0][0] === $matches[0][1];
	}

	/**
	 * Checks for ending CVC sequence where second C is not W, X or Y
	 *
	 * @param   string  $str  String to check
	 *
	 * @return  boolean  Result
	 *
	 * @since   2.5
	 */
	private static function cvc($str)
	{
		$c = self::$regex_consonant;
		$v = self::$regex_vowel;

		return preg_match("#($c$v$c)$#", $str, $matches) &&
strlen($matches[1]) === 3 && $matches[1][2] !== 'w'
&& $matches[1][2] !== 'x'
			&& $matches[1][2] !== 'y';
	}
}
PK[�Ӷ��
�
$helpers/indexer/stemmer/snowball.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderIndexerStemmer', dirname(__DIR__) .
'/stemmer.php');

/**
 * Snowball stemmer class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerStemmerSnowball extends FinderIndexerStemmer
{
	/**
	 * Method to stem a token and return the root.
	 *
	 * @param   string  $token  The token to stem.
	 * @param   string  $lang   The language of the token.
	 *
	 * @return  string  The root token.
	 *
	 * @since   2.5
	 */
	public function stem($token, $lang)
	{
		// Language to use if All is specified.
		static $defaultLang = '';

		// If language is All then try to get site default language.
		if ($lang === '*' && $defaultLang === '')
		{
			$languages = JLanguageHelper::getLanguages();
			$defaultLang = isset($languages[0]->sef) ? $languages[0]->sef :
'*';
			$lang = $defaultLang;
		}

		// Stem the token if it is not in the cache.
		if (!isset($this->cache[$lang][$token]))
		{
			// Get the stem function from the language string.
			switch ($lang)
			{
				// Danish stemmer.
				case 'da':
					$function = 'stem_danish';
					break;

				// German stemmer.
				case 'de':
					$function = 'stem_german';
					break;

				// English stemmer.
				default:
				case 'en':
					$function = 'stem_english';
					break;

				// Spanish stemmer.
				case 'es':
					$function = 'stem_spanish';
					break;

				// Finnish stemmer.
				case 'fi':
					$function = 'stem_finnish';
					break;

				// French stemmer.
				case 'fr':
					$function = 'stem_french';
					break;

				// Hungarian stemmer.
				case 'hu':
					$function = 'stem_hungarian';
					break;

				// Italian stemmer.
				case 'it':
					$function = 'stem_italian';
					break;

				// Norwegian stemmer.
				case 'nb':
					$function = 'stem_norwegian';
					break;

				// Dutch stemmer.
				case 'nl':
					$function = 'stem_dutch';
					break;

				// Portuguese stemmer.
				case 'pt':
					$function = 'stem_portuguese';
					break;

				// Romanian stemmer.
				case 'ro':
					$function = 'stem_romanian';
					break;

				// Russian stemmer.
				case 'ru':
					$function = 'stem_russian_unicode';
					break;

				// Swedish stemmer.
				case 'sv':
					$function = 'stem_swedish';
					break;

				// Turkish stemmer.
				case 'tr':
					$function = 'stem_turkish_unicode';
					break;
			}

			// Stem the word if the stemmer method exists.
			$this->cache[$lang][$token] = function_exists($function) ?
$function($token) : $token;
		}

		return $this->cache[$lang][$token];
	}
}
PK[x���helpers/indexer/stemmer.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Stemmer base class for the Finder indexer package.
 *
 * @since  2.5
 */
abstract class FinderIndexerStemmer
{
	/**
	 * An internal cache of stemmed tokens.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $cache = array();

	/**
	 * Method to get a stemmer, creating it if necessary.
	 *
	 * @param   string  $adapter  The type of stemmer to load.
	 *
	 * @return  FinderIndexerStemmer  A FinderIndexerStemmer instance.
	 *
	 * @since   2.5
	 * @throws  Exception on invalid stemmer.
	 */
	public static function getInstance($adapter)
	{
		static $instances;

		// Only create one stemmer for each adapter.
		if (isset($instances[$adapter]))
		{
			return $instances[$adapter];
		}

		// Create an array of instances if necessary.
		if (!is_array($instances))
		{
			$instances = array();
		}

		// Setup the adapter for the stemmer.
		$adapter = JFilterInput::getInstance()->clean($adapter,
'cmd');
		$path = __DIR__ . '/stemmer/' . $adapter . '.php';
		$class = 'FinderIndexerStemmer' . ucfirst($adapter);

		// Check if a stemmer exists for the adapter.
		if (!file_exists($path))
		{
			// Throw invalid adapter exception.
			throw new
Exception(JText::sprintf('COM_FINDER_INDEXER_INVALID_STEMMER',
$adapter));
		}

		// Instantiate the stemmer.
		JLoader::register($class, $path);
		$instances[$adapter] = new $class;

		return $instances[$adapter];
	}

	/**
	 * Method to stem a token and return the root.
	 *
	 * @param   string  $token  The token to stem.
	 * @param   string  $lang   The language of the token.
	 *
	 * @return  string  The root token.
	 *
	 * @since   2.5
	 */
	abstract public function stem($token, $lang);
}
PK[̝Vqj'j'helpers/indexer/taxonomy.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Stemmer base class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerTaxonomy
{
	/**
	 * An internal cache of taxonomy branch data.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public static $branches = array();

	/**
	 * An internal cache of taxonomy node data.
	 *
	 * @var    array
	 * @since  2.5
	 */
	public static $nodes = array();

	/**
	 * Method to add a branch to the taxonomy tree.
	 *
	 * @param   string   $title   The title of the branch.
	 * @param   integer  $state   The published state of the branch.
[optional]
	 * @param   integer  $access  The access state of the branch. [optional]
	 *
	 * @return  integer  The id of the branch.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function addBranch($title, $state = 1, $access = 1)
	{
		// Check to see if the branch is in the cache.
		if (isset(static::$branches[$title]))
		{
			return static::$branches[$title]->id;
		}

		// Check to see if the branch is in the table.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select('*')
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = 1')
			->where($db->quoteName('title') . ' = ' .
$db->quote($title));
		$db->setQuery($query);

		// Get the result.
		$result = $db->loadObject();

		// Check if the database matches the input data.
		if ((bool) $result && $result->state == $state &&
$result->access == $access)
		{
			// The data matches, add the item to the cache.
			static::$branches[$title] = $result;

			return static::$branches[$title]->id;
		}

		/*
		 * The database did not match the input. This could be because the
		 * state has changed or because the branch does not exist. Let's
figure
		 * out which case is true and deal with it.
		 */
		$branch = new JObject;

		if (empty($result))
		{
			// Prepare the branch object.
			$branch->parent_id = 1;
			$branch->title = $title;
			$branch->state = (int) $state;
			$branch->access = (int) $access;
		}
		else
		{
			// Prepare the branch object.
			$branch->id = (int) $result->id;
			$branch->parent_id = (int) $result->parent_id;
			$branch->title = $result->title;
			$branch->state = (int) $result->title;
			$branch->access = (int) $result->access;
			$branch->ordering = (int) $result->ordering;
		}

		// Store the branch.
		static::storeNode($branch);

		// Add the branch to the cache.
		static::$branches[$title] = $branch;

		return static::$branches[$title]->id;
	}

	/**
	 * Method to add a node to the taxonomy tree.
	 *
	 * @param   string   $branch  The title of the branch to store the node
in.
	 * @param   string   $title   The title of the node.
	 * @param   integer  $state   The published state of the node. [optional]
	 * @param   integer  $access  The access state of the node. [optional]
	 *
	 * @return  integer  The id of the node.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function addNode($branch, $title, $state = 1, $access = 1)
	{
		// Check to see if the node is in the cache.
		if (isset(static::$nodes[$branch][$title]))
		{
			return static::$nodes[$branch][$title]->id;
		}

		// Get the branch id, insert it if it does not exist.
		$branchId = static::addBranch($branch);

		// Check to see if the node is in the table.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select('*')
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = ' .
$db->quote($branchId))
			->where($db->quoteName('title') . ' = ' .
$db->quote($title));
		$db->setQuery($query);

		// Get the result.
		$result = $db->loadObject();

		// Check if the database matches the input data.
		if ((bool) $result && $result->state == $state &&
$result->access == $access)
		{
			// The data matches, add the item to the cache.
			static::$nodes[$branch][$title] = $result;

			return static::$nodes[$branch][$title]->id;
		}

		/*
		 * The database did not match the input. This could be because the
		 * state has changed or because the node does not exist. Let's
figure
		 * out which case is true and deal with it.
		 */
		$node = new JObject;

		if (empty($result))
		{
			// Prepare the node object.
			$node->parent_id = (int) $branchId;
			$node->title = $title;
			$node->state = (int) $state;
			$node->access = (int) $access;
		}
		else
		{
			// Prepare the node object.
			$node->id = (int) $result->id;
			$node->parent_id = (int) $result->parent_id;
			$node->title = $result->title;
			$node->state = (int) $result->title;
			$node->access = (int) $result->access;
			$node->ordering = (int) $result->ordering;
		}

		// Store the node.
		static::storeNode($node);

		// Add the node to the cache.
		static::$nodes[$branch][$title] = $node;

		return static::$nodes[$branch][$title]->id;
	}

	/**
	 * Method to add a map entry between a link and a taxonomy node.
	 *
	 * @param   integer  $linkId  The link to map to.
	 * @param   integer  $nodeId  The node to map to.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function addMap($linkId, $nodeId)
	{
		// Insert the map.
		$db = JFactory::getDbo();

		$query = $db->getQuery(true)
			->select($db->quoteName('link_id'))
			->from($db->quoteName('#__finder_taxonomy_map'))
			->where($db->quoteName('link_id') . ' = ' .
(int) $linkId)
			->where($db->quoteName('node_id') . ' = ' .
(int) $nodeId);
		$db->setQuery($query);
		$db->execute();
		$id = (int) $db->loadResult();

		$map = new JObject;
		$map->link_id = (int) $linkId;
		$map->node_id = (int) $nodeId;

		if ($id)
		{
			$db->updateObject('#__finder_taxonomy_map', $map,
array('link_id', 'node_id'));
		}
		else
		{
			$db->insertObject('#__finder_taxonomy_map', $map);
		}

		return true;
	}

	/**
	 * Method to get the title of all taxonomy branches.
	 *
	 * @return  array  An array of branch titles.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function getBranchTitles()
	{
		$db = JFactory::getDbo();

		// Set user variables
		$groups = implode(',',
JFactory::getUser()->getAuthorisedViewLevels());

		// Create a query to get the taxonomy branch titles.
		$query = $db->getQuery(true)
			->select($db->quoteName('title'))
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = 1')
			->where($db->quoteName('state') . ' = 1')
			->where($db->quoteName('access') . ' IN (' .
$groups . ')');

		// Get the branch titles.
		$db->setQuery($query);

		return $db->loadColumn();
	}

	/**
	 * Method to find a taxonomy node in a branch.
	 *
	 * @param   string  $branch  The branch to search.
	 * @param   string  $title   The title of the node.
	 *
	 * @return  mixed  Integer id on success, null on no match.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function getNodeByTitle($branch, $title)
	{
		$db = JFactory::getDbo();

		// Set user variables
		$groups = implode(',',
JFactory::getUser()->getAuthorisedViewLevels());

		// Create a query to get the node.
		$query = $db->getQuery(true)
			->select('t1.*')
			->from($db->quoteName('#__finder_taxonomy') . ' AS
t1')
			->join('INNER',
$db->quoteName('#__finder_taxonomy') . ' AS t2 ON t2.id =
t1.parent_id')
			->where('t1.access IN (' . $groups . ')')
			->where('t1.state = 1')
			->where('t1.title LIKE ' .
$db->quote($db->escape($title) . '%'))
			->where('t2.access IN (' . $groups . ')')
			->where('t2.state = 1')
			->where('t2.title = ' . $db->quote($branch));

		// Get the node.
		$db->setQuery($query, 0, 1);

		return $db->loadObject();
	}

	/**
	 * Method to remove map entries for a link.
	 *
	 * @param   integer  $linkId  The link to remove.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function removeMaps($linkId)
	{
		// Delete the maps.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->delete($db->quoteName('#__finder_taxonomy_map'))
			->where($db->quoteName('link_id') . ' = ' .
(int) $linkId);
		$db->setQuery($query);
		$db->execute();

		return true;
	}

	/**
	 * Method to remove orphaned taxonomy nodes and branches.
	 *
	 * @return  integer  The number of deleted rows.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	public static function removeOrphanNodes()
	{
		// Delete all orphaned nodes.
		$db = JFactory::getDbo();
		$query     = $db->getQuery(true);
		$subquery  = $db->getQuery(true);
		$subquery1 = $db->getQuery(true);

		$subquery1->select($db->quoteName('t.id'))
			->from($db->quoteName('#__finder_taxonomy',
't'))
			->join('LEFT',
$db->quoteName('#__finder_taxonomy_map', 'm') .
' ON ' . $db->quoteName('m.node_id') . '='
. $db->quoteName('t.id'))
			->where($db->quoteName('t.parent_id') . ' > 1
')
			->where($db->quoteName('m.link_id') . ' IS
NULL');

		$subquery->select($db->quoteName('id'))
			->from('(' . $subquery1 . ') temp');

		$query->delete($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('id') . ' IN (' .
$subquery . ')');

		$db->setQuery($query);
		$db->execute();

		return $db->getAffectedRows();
	}

	/**
	 * Method to store a node to the database.  This method will accept either
a branch or a node.
	 *
	 * @param   object  $item  The item to store.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 * @throws  Exception on database error.
	 */
	protected static function storeNode($item)
	{
		$db = JFactory::getDbo();

		// Check if we are updating or inserting the item.
		if (empty($item->id))
		{
			// Insert the item.
			$db->insertObject('#__finder_taxonomy', $item,
'id');
		}
		else
		{
			// Update the item.
			$db->updateObject('#__finder_taxonomy', $item,
'id');
		}

		return true;
	}
}
PK[�j���helpers/indexer/token.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;

/**
 * Token class for the Finder indexer package.
 *
 * @since  2.5
 */
class FinderIndexerToken
{
	/**
	 * This is the term that will be referenced in the terms table and the
	 * mapping tables.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $term;

	/**
	 * The stem is used to match the root term and produce more potential
	 * matches when searching the index.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $stem;

	/**
	 * If the token is numeric, it is likely to be short and uncommon so the
	 * weight is adjusted to compensate for that situation.
	 *
	 * @var    boolean
	 * @since  2.5
	 */
	public $numeric;

	/**
	 * If the token is a common term, the weight is adjusted to compensate for
	 * the higher frequency of the term in relation to other terms.
	 *
	 * @var    boolean
	 * @since  2.5
	 */
	public $common;

	/**
	 * Flag for phrase tokens.
	 *
	 * @var    boolean
	 * @since  2.5
	 */
	public $phrase;

	/**
	 * The length is used to calculate the weight of the token.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $length;

	/**
	 * The weight is calculated based on token size and whether the token is
	 * considered a common term.
	 *
	 * @var    integer
	 * @since  2.5
	 */
	public $weight;

	/**
	 * The simple language identifier for the token.
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $language;

	/**
	 * The container for matches.
	 *
	 * @var    array
	 * @since  3.8.12
	 */
	public $matches = array();

	/**
	 * Is derived token (from individual words)
	 *
	 * @var    boolean
	 * @since  3.8.12
	 */
	public $derived;

	/**
	 * The suggested term
	 *
	 * @var    string
	 * @since  3.8.12
	 */
	public $suggestion;

	/**
	 * Method to construct the token object.
	 *
	 * @param   mixed   $term    The term as a string for words or an array
for phrases.
	 * @param   string  $lang    The simple language identifier.
	 * @param   string  $spacer  The space separator for phrases. [optional]
	 *
	 * @since   2.5
	 */
	public function __construct($term, $lang, $spacer = ' ')
	{
		$this->language = $lang;

		// Tokens can be a single word or an array of words representing a
phrase.
		if (is_array($term))
		{
			// Populate the token instance.
			$this->term = implode($spacer, $term);
			$this->stem = implode($spacer,
array_map(array('FinderIndexerHelper', 'stem'), $term,
array($lang)));
			$this->numeric = false;
			$this->common = false;
			$this->phrase = true;
			$this->length = StringHelper::strlen($this->term);

			/*
			 * Calculate the weight of the token.
			 *
			 * 1. Length of the token up to 30 and divide by 30, add 1.
			 * 2. Round weight to 4 decimal points.
			 */
			$this->weight = (($this->length >= 30 ? 30 : $this->length)
/ 30) + 1;
			$this->weight = round($this->weight, 4);
		}
		else
		{
			// Populate the token instance.
			$this->term = $term;
			$this->stem = FinderIndexerHelper::stem($this->term, $lang);
			$this->numeric = (is_numeric($this->term) || (bool)
preg_match('#^[0-9,.\-\+]+$#', $this->term));
			$this->common = $this->numeric ? false :
FinderIndexerHelper::isCommon($this->term, $lang);
			$this->phrase = false;
			$this->length = StringHelper::strlen($this->term);

			/*
			 * Calculate the weight of the token.
			 *
			 * 1. Length of the token up to 15 and divide by 15.
			 * 2. If common term, divide weight by 8.
			 * 3. If numeric, multiply weight by 1.5.
			 * 4. Round weight to 4 decimal points.
			 */
			$this->weight = ($this->length >= 15 ? 15 : $this->length) /
15;
			$this->weight = $this->common === true ? $this->weight / 8 :
$this->weight;
			$this->weight = $this->numeric === true ? $this->weight * 1.5 :
$this->weight;
			$this->weight = round($this->weight, 4);
		}
	}
}
PK[(��~##helpers/language.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Finder language helper class.
 *
 * @since  2.5
 */
class FinderHelperLanguage
{
	/**
	 * Method to return a plural language code for a taxonomy branch.
	 *
	 * @param   string  $branchName  Branch title.
	 *
	 * @return  string  Language key code.
	 *
	 * @since   2.5
	 */
	public static function branchPlural($branchName)
	{
		$return = preg_replace('/[^a-zA-Z0-9]+/', '_',
strtoupper($branchName));

		if ($return !== '_')
		{
			return 'PLG_FINDER_QUERY_FILTER_BRANCH_P_' . $return;
		}

		return $branchName;
	}

	/**
	 * Method to return a singular language code for a taxonomy branch.
	 *
	 * @param   string  $branchName  Branch name.
	 *
	 * @return  string  Language key code.
	 *
	 * @since   2.5
	 */
	public static function branchSingular($branchName)
	{
		$return = preg_replace('/[^a-zA-Z0-9]+/', '_',
strtoupper($branchName));

		return 'PLG_FINDER_QUERY_FILTER_BRANCH_S_' . $return;
	}

	/**
	 * Method to return the language name for a language taxonomy branch.
	 *
	 * @param   string  $branchName  Language branch name.
	 *
	 * @return  string  The language title.
	 *
	 * @since   3.6.0
	 */
	public static function branchLanguageTitle($branchName)
	{
		$title = $branchName;

		if ($branchName === '*')
		{
			$title = JText::_('JALL_LANGUAGE');
		}
		else
		{
			$languages = JLanguageHelper::getLanguages('lang_code');

			if (isset($languages[$branchName]))
			{
				$title = $languages[$branchName]->title;
			}
		}

		return $title;
	}

	/**
	 * Method to load Smart Search component language file.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public static function loadComponentLanguage()
	{
		JFactory::getLanguage()->load('com_finder', JPATH_SITE);
	}

	/**
	 * Method to load Smart Search plugin language files.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public static function loadPluginLanguage()
	{
		static $loaded = false;

		// If already loaded, don't load again.
		if ($loaded)
		{
			return;
		}

		$loaded = true;

		// Get array of all the enabled Smart Search plugin names.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select(array($db->qn('name'),
$db->qn('element')))
			->from($db->quoteName('#__extensions'))
			->where($db->quoteName('type') . ' = ' .
$db->quote('plugin'))
			->where($db->quoteName('folder') . ' = ' .
$db->quote('finder'))
			->where($db->quoteName('enabled') . ' = 1');
		$db->setQuery($query);
		$plugins = $db->loadObjectList();

		if (empty($plugins))
		{
			return;
		}

		// Load generic language strings.
		$lang = JFactory::getLanguage();
		$lang->load('plg_content_finder', JPATH_ADMINISTRATOR);

		// Load language file for each plugin.
		foreach ($plugins as $plugin)
		{
			$lang->load($plugin->name, JPATH_ADMINISTRATOR)
				|| $lang->load($plugin->name, JPATH_PLUGINS .
'/finder/' . $plugin->element);
		}
	}
}
PK[�x���models/fields/branches.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_BASE') or die();

JFormHelper::loadFieldClass('list');

/**
 * Search Branches field for the Finder package.
 *
 * @since  3.5
 */
class JFormFieldBranches extends JFormFieldList
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $type = 'Branches';

	/**
	 * Method to get the field options.
	 *
	 * @return  array  The field option objects.
	 *
	 * @since   3.5
	 */
	public function getOptions()
	{
		return JHtml::_('finder.mapslist');
	}
}
PK[F�=�zzmodels/fields/contentmap.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JFormHelper::loadFieldClass('groupedlist');

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

/**
 * Supports a select grouped list of finder content map.
 *
 * @since  3.6.0
 */
class JFormFieldContentMap extends JFormFieldGroupedList
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.6.0
	 */
	public $type = 'ContentMap';

	/**
	 * Method to get the list of content map options grouped by first level.
	 *
	 * @return  array  The field option objects as a nested array in groups.
	 *
	 * @since   3.6.0
	 */
	protected function getGroups()
	{
		$groups = array();

		// Get the database object and a new query object.
		$db = JFactory::getDbo();

		// Levels subquery.
		$levelQuery = $db->getQuery(true);
		$levelQuery->select('title AS branch_title, 1 as level')
			->select($db->quoteName('id'))
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = 1');
		$levelQuery2 = $db->getQuery(true);
		$levelQuery2->select('b.title AS branch_title, 2 as level')
			->select($db->quoteName('a.id'))
			->from($db->quoteName('#__finder_taxonomy',
'a'))
			->join('LEFT',
$db->quoteName('#__finder_taxonomy', 'b') . '
ON ' . $db->qn('a.parent_id') . ' = ' .
$db->qn('b.id'))
			->where($db->quoteName('a.parent_id') . ' NOT IN
(0, 1)');

		$levelQuery->union($levelQuery2);

		// Main query.
		$query = $db->getQuery(true)
			->select($db->quoteName('a.title', 'text'))
			->select($db->quoteName('a.id', 'value'))
			->select($db->quoteName('d.level'))
			->from($db->quoteName('#__finder_taxonomy',
'a'))
			->join('LEFT', '(' . $levelQuery . ') AS d
ON ' . $db->qn('d.id') . ' = ' .
$db->qn('a.id'))
			->where($db->quoteName('a.parent_id') . ' <>
0')
			->order('d.branch_title ASC, d.level ASC, a.title ASC');

		$db->setQuery($query);

		try
		{
			$contentMap = $db->loadObjectList();
		}
		catch (RuntimeException $e)
		{
			return;
		}

		// Build the grouped list array.
		if ($contentMap)
		{
			$lang = JFactory::getLanguage();

			foreach ($contentMap as $branch)
			{
				if ((int) $branch->level === 1)
				{
					$name = $branch->text;
				}
				else
				{
					$levelPrefix = str_repeat('- ', max(0, $branch->level -
1));

					if (trim($name, '**') === 'Language')
					{
						$text = FinderHelperLanguage::branchLanguageTitle($branch->text);
					}
					else
					{
						$key = FinderHelperLanguage::branchSingular($branch->text);
						$text = $lang->hasKey($key) ? JText::_($key) : $branch->text;
					}

					// Initialize the group if necessary.
					if (!isset($groups[$name]))
					{
						$groups[$name] = array();
					}

					$groups[$name][] = JHtml::_('select.option',
$branch->value, $levelPrefix . $text);
				}
			}
		}

		// Merge any additional groups in the XML definition.
		$groups = array_merge(parent::getGroups(), $groups);

		return $groups;
	}
}
PK[KY!��models/fields/contenttypes.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_BASE') or die();

use Joomla\Utilities\ArrayHelper;

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

JFormHelper::loadFieldClass('list');

/**
 * Content Types Filter field for the Finder package.
 *
 * @since  3.6.0
 */
class JFormFieldContentTypes extends JFormFieldList
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.6.0
	 */
	protected $type = 'ContentTypes';

	/**
	 * Method to get the field options.
	 *
	 * @return  array  The field option objects.
	 *
	 * @since   3.6.0
	 */
	public function getOptions()
	{
		$lang    = JFactory::getLanguage();
		$options = array();

		$db    = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select($db->quoteName('id', 'value'))
			->select($db->quoteName('title', 'text'))
			->from($db->quoteName('#__finder_types'));

		// Get the options.
		$db->setQuery($query);

		try
		{
			$contentTypes = $db->loadObjectList();
		}
		catch (RuntimeException $e)
		{
			JError::raiseWarning(500, $db->getMessage());
		}

		// Translate.
		foreach ($contentTypes as $contentType)
		{
			$key = FinderHelperLanguage::branchSingular($contentType->text);
			$contentType->translatedText = $lang->hasKey($key) ?
JText::_($key) : $contentType->text;
		}

		// Order by title.
		$contentTypes = ArrayHelper::sortObjects($contentTypes,
'translatedText', 1, true, true);

		// Convert the values to options.
		foreach ($contentTypes as $contentType)
		{
			$options[] = JHtml::_('select.option', $contentType->value,
$contentType->translatedText);
		}

		// Merge any additional options in the XML definition.
		$options = array_merge(parent::getOptions(), $options);

		return $options;
	}
}
PK[�����models/fields/directories.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

// Load the base adapter.
JLoader::register('FinderIndexerAdapter', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/indexer/adapter.php');

JFormHelper::loadFieldClass('list');

/**
 * Renders a list of directories.
 *
 * @since       2.5
 * @deprecated  4.0  Use JFormFieldFolderlist
 */
class JFormFieldDirectories extends JFormFieldList
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $type = 'Directories';

	/**
	 * Method to get the field options.
	 *
	 * @return  array  The field option objects.
	 *
	 * @since   2.5
	 */
	public function getOptions()
	{
		$values  = array();
		$options = array();
		$exclude = array(
			JPATH_ADMINISTRATOR,
			JPATH_INSTALLATION,
			JPATH_LIBRARIES,
			JPATH_PLUGINS,
			JPATH_SITE . '/cache',
			JPATH_SITE . '/components',
			JPATH_SITE . '/includes',
			JPATH_SITE . '/language',
			JPATH_SITE . '/modules',
			JPATH_THEMES,
			JFactory::getApplication()->get('log_path'),
			JFactory::getApplication()->get('tmp_path')
		);

		// Get the base directories.
		jimport('joomla.filesystem.folder');
		$dirs = JFolder::folders(JPATH_SITE, '.', false, true);

		// Iterate through the base directories and find the subdirectories.
		foreach ($dirs as $dir)
		{
			// Check if the directory should be excluded.
			if (in_array($dir, $exclude))
			{
				continue;
			}

			// Get the child directories.
			$return = JFolder::folders($dir, '.', true, true);

			// Merge the directories.
			if (is_array($return))
			{
				$values[] = $dir;
				$values = array_merge($values, $return);
			}
		}

		// Convert the values to options.
		foreach ($values as $value)
		{
			$options[] = JHtml::_('select.option', str_replace(JPATH_SITE
. '/', '', $value), str_replace(JPATH_SITE .
'/', '', $values));
		}

		// Add a null option.
		array_unshift($options, JHtml::_('select.option', '',
'- ' . JText::_('JNONE') . ' -'));

		return $options;
	}
}
PK[�K
��models/fields/searchfilter.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_BASE') or die();

JFormHelper::loadFieldClass('list');

/**
 * Search Filter field for the Finder package.
 *
 * @since  2.5
 */
class JFormFieldSearchFilter extends JFormFieldList
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $type = 'SearchFilter';

	/**
	 * Method to get the field options.
	 *
	 * @return  array  The field option objects.
	 *
	 * @since   2.5
	 */
	public function getOptions()
	{
		// Build the query.
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select('f.title AS text, f.filter_id AS value')
			->from($db->quoteName('#__finder_filters') . ' AS
f')
			->where('f.state = 1')
			->order('f.title ASC');
		$db->setQuery($query);
		$options = $db->loadObjectList();

		array_unshift($options, JHtml::_('select.option', '',
JText::_('COM_FINDER_SELECT_SEARCH_FILTER'), 'value',
'text'));

		return $options;
	}
}
PK[^o	D}}models/filter.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Filter model class for Finder.
 *
 * @since  2.5
 */
class FinderModelFilter extends JModelAdmin
{
	/**
	 * The prefix to use with controller messages.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $text_prefix = 'COM_FINDER';

	/**
	 * Model context string.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $context = 'com_finder.filter';

	/**
	 * Custom clean cache method.
	 *
	 * @param   string   $group     The component name. [optional]
	 * @param   integer  $clientId  The client ID. [optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function cleanCache($group = 'com_finder', $clientId =
1)
	{
		parent::cleanCache($group, $clientId);
	}

	/**
	 * Method to get the filter data.
	 *
	 * @return  FinderTableFilter|boolean  The filter data or false on a
failure.
	 *
	 * @since   2.5
	 */
	public function getFilter()
	{
		$filter_id = (int) $this->getState('filter.id');

		// Get a FinderTableFilter instance.
		$filter = $this->getTable();

		// Attempt to load the row.
		$return = $filter->load($filter_id);

		// Check for a database error.
		if ($return === false && $filter->getError())
		{
			$this->setError($filter->getError());

			return false;
		}

		// Process the filter data.
		if (!empty($filter->data))
		{
			$filter->data = explode(',', $filter->data);
		}
		elseif (empty($filter->data))
		{
			$filter->data = array();
		}

		// Check for a database error.
		if ($this->_db->getErrorNum())
		{
			$this->setError($this->_db->getErrorMsg());

			return false;
		}

		return $filter;
	}

	/**
	 * Method to get the record form.
	 *
	 * @param   array    $data      Data for the form. [optional]
	 * @param   boolean  $loadData  True if the form is to load its own data
(default case), false if not. [optional]
	 *
	 * @return  JForm|boolean  A JForm object on success, false on failure
	 *
	 * @since   2.5
	 */
	public function getForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm('com_finder.filter',
'filter', array('control' => 'jform',
'load_data' => $loadData));

		if (empty($form))
		{
			return false;
		}

		return $form;
	}

	/**
	 * Returns a JTable object, always creating it.
	 *
	 * @param   string  $type    The table type to instantiate. [optional]
	 * @param   string  $prefix  A prefix for the table class name. [optional]
	 * @param   array   $config  Configuration array for model. [optional]
	 *
	 * @return  JTable  A database object
	 *
	 * @since   2.5
	 */
	public function getTable($type = 'Filter', $prefix =
'FinderTable', $config = array())
	{
		return JTable::getInstance($type, $prefix, $config);
	}

	/**
	 * Method to get the data that should be injected in the form.
	 *
	 * @return  mixed  The data for the form.
	 *
	 * @since   2.5
	 */
	protected function loadFormData()
	{
		// Check the session for previously entered form data.
		$data =
JFactory::getApplication()->getUserState('com_finder.edit.filter.data',
array());

		if (empty($data))
		{
			$data = $this->getItem();
		}

		$this->preprocessData('com_finder.filter', $data);

		return $data;
	}

	/**
	 * Method to get the total indexed items
	 *
	 * @return  number the number of indexed items
	 *
	 * @since  3.5
	 */
	public function getTotal()
	{
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true)
			->select('MAX(link_id)')
			->from('#__finder_links');

		return $db->setQuery($query)->loadResult();
	}
}
PK[[�l.models/filters.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Filters model class for Finder.
 *
 * @since  2.5
 */
class FinderModelFilters extends JModelList
{
	/**
	 * Constructor.
	 *
	 * @param   array  $config  An associative array of configuration
settings. [optional]
	 *
	 * @since   2.5
	 * @see     JControllerLegacy
	 */
	public function __construct($config = array())
	{
		if (empty($config['filter_fields']))
		{
			$config['filter_fields'] = array(
				'filter_id', 'a.filter_id',
				'title', 'a.title',
				'state', 'a.state',
				'created_by_alias', 'a.created_by_alias',
				'created', 'a.created',
				'map_count', 'a.map_count'
			);
		}

		parent::__construct($config);
	}

	/**
	 * Build an SQL query to load the list data.
	 *
	 * @return  JDatabaseQuery  A JDatabaseQuery object
	 *
	 * @since   2.5
	 */
	protected function getListQuery()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true);

		// Select all fields from the table.
		$query->select('a.*')
			->from($db->quoteName('#__finder_filters',
'a'));

		// Join over the users for the checked out user.
		$query->select($db->quoteName('uc.name',
'editor'))
			->join('LEFT', $db->quoteName('#__users',
'uc') . ' ON ' . $db->quoteName('uc.id') .
' = ' . $db->quoteName('a.checked_out'));

		// Join over the users for the author.
		$query->select($db->quoteName('ua.name',
'user_name'))
			->join('LEFT', $db->quoteName('#__users',
'ua') . ' ON ' . $db->quoteName('ua.id') .
' = ' . $db->quoteName('a.created_by'));

		// Check for a search filter.
		if ($search = $this->getState('filter.search'))
		{
			$search = $db->quote('%' . str_replace(' ',
'%', $db->escape(trim($search), true) . '%'));
			$query->where($db->quoteName('a.title') . ' LIKE
' . $search);
		}

		// If the model is set to check item state, add to the query.
		$state = $this->getState('filter.state');

		if (is_numeric($state))
		{
			$query->where($db->quoteName('a.state') . ' =
' . (int) $state);
		}

		// Add the list ordering clause.
		$query->order($db->escape($this->getState('list.ordering',
'a.title') . ' ' .
$db->escape($this->getState('list.direction',
'ASC'))));

		return $query;
	}

	/**
	 * Method to get a store id based on model configuration state.
	 *
	 * This is necessary because the model is used by the component and
	 * different modules that might need different sets of data or different
	 * ordering requirements.
	 *
	 * @param   string  $id  A prefix for the store id. [optional]
	 *
	 * @return  string  A store id.
	 *
	 * @since   2.5
	 */
	protected function getStoreId($id = '')
	{
		// Compile the store id.
		$id .= ':' . $this->getState('filter.search');
		$id .= ':' . $this->getState('filter.state');

		return parent::getStoreId($id);
	}

	/**
	 * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
	 *
	 * @param   string  $ordering   An optional ordering field. [optional]
	 * @param   string  $direction  An optional direction. [optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function populateState($ordering = 'a.title',
$direction = 'asc')
	{
		// Load the filter state.
		$this->setState('filter.search',
$this->getUserStateFromRequest($this->context .
'.filter.search', 'filter_search', '',
'string'));
		$this->setState('filter.state',
$this->getUserStateFromRequest($this->context .
'.filter.state', 'filter_state', '',
'cmd'));

		// Load the parameters.
		$params = JComponentHelper::getParams('com_finder');
		$this->setState('params', $params);

		// List state information.
		parent::populateState($ordering, $direction);
	}
}
PK[?�=�jjmodels/forms/filter.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fieldset>
		<field 
			name="filter_id"  
			type="text" 
			label="JGLOBAL_FIELD_ID_LABEL"
			description="JGLOBAL_FIELD_ID_DESC" 
			class="readonly" 
			size="10" 
			default="0"
			readonly="true"  
		/>

		<field 
			name="title" 
			type="text" 
			label="JGLOBAL_TITLE"
			description="COM_FINDER_FILTER_TITLE_DESCRIPTION"
			class="input-xxlarge input-large-text"
			size="40"
			id="title"
			required="true" 
		/>

		<field 
			name="alias" 
			type="text" 
			label="JFIELD_ALIAS_LABEL"
			description="JFIELD_ALIAS_DESC"
			hint="JFIELD_ALIAS_PLACEHOLDER" 
			size="45" 
		/>

		<field
			name="created"
			type="calendar"
			label="JGLOBAL_FIELD_CREATED_LABEL"
			description="JGLOBAL_FIELD_CREATED_DESC"
			translateformat="true"
			showtime="true"
			size="22"
			filter="user_utc"
		/>

		<field
			name="modified"
			type="calendar"
			label="JGLOBAL_FIELD_MODIFIED_LABEL"
			description="COM_FINDER_FIELD_MODIFIED_DESCRIPTION"
			class="readonly"
			translateformat="true"
			showtime="true"
			size="22"
			readonly="true"
			filter="user_utc"
		/>

		<field 
			name="created_by" 
			type="user"
			label="COM_FINDER_FIELD_CREATED_BY_LABEL" 
			description="COM_FINDER_FIELD_CREATED_BY_DESC" 
		/>

		<field 
			name="created_by_alias" 
			type="text"
			label="COM_FINDER_FIELD_CREATED_BY_ALIAS_LABEL" 
			description="COM_FINDER_FIELD_CREATED_BY_ALIAS_DESC"
			size="20" 
		/>
		
		<field 
			name="modified_by" 
			type="user"
			label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
			class="readonly"
			readonly="true"
			filter="unset"
		 />

		<field 
			name="checked_out" 
			type="hidden" 
			filter="unset" 
		/>

		<field 
			name="checked_out_time" 
			type="hidden" 
			filter="unset" 
		/>

		<field 
			name="state" 
			type="list" 
			label="JSTATUS"
			description="JFIELD_PUBLISHED_DESC"
			class="chzn-color-state"
			filter="intval"
			size="1"
			default="1" 
			>
			<option value="1">JPUBLISHED</option>
			<option value="0">JUNPUBLISHED</option>
		</field>

		<field
			name="map_count" 
			type="text" 
			label="COM_FINDER_FILTER_MAP_COUNT" 
			description="COM_FINDER_FILTER_MAP_COUNT_DESCRIPTION"
			class="readonly"
			size="10" 
			default="0" 
			readonly="true" 
		/>
	</fieldset>

	<fields name="params">
		<fieldset name="jbasic"
label="COM_FINDER_FILTER_FIELDSET_PARAMS">
			<field
				name="w1"
				type="list"
				label="COM_FINDER_FILTER_WHEN_START_DATE_LABEL"
				description="COM_FINDER_FILTER_WHEN_START_DATE_DESCRIPTION"
				default=""
				filter="string"
				>
				<option value="">JNONE</option>
				<option
value="-1">COM_FINDER_FILTER_WHEN_BEFORE</option>
				<option
value="0">COM_FINDER_FILTER_WHEN_EXACTLY</option>
				<option
value="1">COM_FINDER_FILTER_WHEN_AFTER</option>
			</field>

			<field 
				name="d1"
				type="calendar"
				label="COM_FINDER_FILTER_START_DATE_LABEL"
				description="COM_FINDER_FILTER_START_DATE_DESCRIPTION"
				translateformat="true"
				size="22"
				filter="user_utc"
			/>

			<field
				name="w2"
				type="list"
				label="COM_FINDER_FILTER_WHEN_END_DATE_LABEL"
				description="COM_FINDER_FILTER_WHEN_END_DATE_DESCRIPTION"
				default=""
				filter="string"
				>
				<option value="">JNONE</option>
				<option
value="-1">COM_FINDER_FILTER_WHEN_BEFORE</option>
				<option
value="0">COM_FINDER_FILTER_WHEN_EXACTLY</option>
				<option
value="1">COM_FINDER_FILTER_WHEN_AFTER</option>
			</field>

			<field
				name="d2"
				type="calendar"
				label="COM_FINDER_FILTER_END_DATE_LABEL"
				description="COM_FINDER_FILTER_END_DATE_DESCRIPTION"
				translateformat="true"
				size="22"
				filter="user_utc"
			/>
		</fieldset>

	</fields>
</form>
PK[�@*$��models/forms/filter_filters.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fields name="filter">
		<field
			name="search"
			type="text"
			inputmode="search"
			label="COM_FINDER_SEARCH_FILTER_SEARCH_LABEL"
			description="COM_FINDER_SEARCH_FILTER_SEARCH_DESC"
			hint="JSEARCH_FILTER"
		/>

		<field
			name="state"
			type="status"
			label="COM_FINDER_FILTER_PUBLISHED"
			description="COM_FINDER_FILTER_PUBLISHED_DESC"
			filter="0,1"
			onchange="this.form.submit();"
			>
			<option value="">JOPTION_SELECT_PUBLISHED</option>
		</field>
	</fields>

	<fields name="list">
		<field
			name="fullordering"
			type="list"
			onchange="this.form.submit();"
			default="a.title ASC"
			validate="options"
			>
			<option value="">JGLOBAL_SORT_BY</option>
			<option value="a.state ASC">JSTATUS_ASC</option>
			<option value="a.state DESC">JSTATUS_DESC</option>
			<option value="a.title
ASC">JGLOBAL_TITLE_ASC</option>
			<option value="a.title
DESC">JGLOBAL_TITLE_DESC</option>
			<option value="a.created_by_alias
ASC">COM_FINDER_HEADING_CREATED_BY_ASC</option>
			<option value="a.created_by_alias
DESC">COM_FINDER_HEADING_CREATED_BY_DESC</option>
			<option value="a.created
ASC">COM_FINDER_HEADING_CREATED_ON_ASC</option>
			<option value="a.created
DESC">COM_FINDER_HEADING_CREATED_ON_DESC</option>
			<option value="a.map_count
ASC">COM_FINDER_HEADING_MAP_COUNT_ASC</option>
			<option value="a.map_count
DESC">COM_FINDER_HEADING_MAP_COUNT_DESC</option>
			<option value="a.filter_id
ASC">JGRID_HEADING_ID_ASC</option>
			<option value="a.filter_id
DESC">JGRID_HEADING_ID_DESC</option>
		</field>

		<field
			name="limit"
			type="limitbox"
			class="input-mini"
			default="25"
			onchange="this.form.submit();"
		/>
	</fields>
</form>
PK[}SB((models/forms/filter_index.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fields name="filter">
		<field
			name="search"
			type="text"
			inputmode="search"
			label="COM_FINDER_INDEX_SEARCH_LABEL"
			description="COM_FINDER_INDEX_SEARCH_DESC"
			hint="JSEARCH_FILTER"
		/>

		<field
			name="state"
			type="status"
			label="COM_FINDER_FILTER_PUBLISHED"
			description="COM_FINDER_FILTER_PUBLISHED_DESC"
			filter="0,1"
			onchange="this.form.submit();"
			>
			<option value="">JOPTION_SELECT_PUBLISHED</option>
		</field>

		<field
			name="type"
			type="ContentTypes"
			label="JOPTION_FILTER_CATEGORY"
			description="JOPTION_FILTER_CATEGORY_DESC"
			onchange="this.form.submit();"
			>
			<option
value="">COM_FINDER_MAPS_SELECT_TYPE</option>
		</field>

		<field
			name="content_map"
			type="ContentMap"
			label="COM_FINDER_FILTER_CONTENT_MAP_LABEL"
			description="COM_FINDER_FILTER_CONTENT_MAP_DESC"
			onchange="this.form.submit();"
			>
			<option
value="">COM_FINDER_FILTER_SELECT_CONTENT_MAP</option>
		</field>
	</fields>

	<fields name="list">
		<field
			name="fullordering"
			type="list"
			onchange="this.form.submit();"
			default="l.title ASC"
			validate="options"
			>
			<option value="">JGLOBAL_SORT_BY</option>
			<option value="l.published
ASC">JSTATUS_ASC</option>
			<option value="l.published
DESC">JSTATUS_DESC</option>
			<option value="l.title
ASC">JGLOBAL_TITLE_ASC</option>
			<option value="l.title
DESC">JGLOBAL_TITLE_DESC</option>
			<option value="t.title
ASC">COM_FINDER_INDEX_HEADING_INDEX_TYPE_ASC</option>
			<option value="t.title
DESC">COM_FINDER_INDEX_HEADING_INDEX_TYPE_DESC</option>
			<option value="l.indexdate
ASC">COM_FINDER_INDEX_HEADING_INDEX_DATE_ASC</option>
			<option value="l.indexdate
DESC">COM_FINDER_INDEX_HEADING_INDEX_DATE_DESC</option>
			<option value="l.url
ASC">COM_FINDER_INDEX_HEADING_LINK_URL_ASC</option>
			<option value="l.url
DESC">COM_FINDER_INDEX_HEADING_LINK_URL_DESC</option>
		</field>

		<field
			name="limit"
			type="limitbox"
			class="input-mini"
			default="25"
			onchange="this.form.submit();"
		/>
	</fields>
</form>
PK[��o���models/forms/filter_maps.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fields name="filter">
		<field
			name="search"
			type="text"
			inputmode="search"
			label="COM_FINDER_SEARCH_SEARCH_QUERY_LABEL"
			description="COM_FINDER_SEARCH_SEARCH_QUERY_DESC"
			hint="JSEARCH_FILTER"
		/>

		<field
			name="state"
			type="status"
			label="COM_FINDER_FILTER_PUBLISHED"
			description="COM_FINDER_FILTER_PUBLISHED_DESC"
			filter="0,1"
			onchange="this.form.submit();"
			>
			<option value="">JOPTION_SELECT_PUBLISHED</option>
		</field>

		<field
			name="branch"
			type="branches"
			default="0"
			onchange="this.form.submit();"
		/>

		<field
			name="level"
			type="integer"
			label="JOPTION_FILTER_LEVEL"
			description="JOPTION_FILTER_LEVEL_DESC"
			first="1"
			last="2"
			step="1"
			languages="*"
			onchange="this.form.submit();"
			>
			<option
value="">JOPTION_SELECT_MAX_LEVELS</option>
		</field>
	</fields>

	<fields name="list">
		<field
			name="fullordering"
			type="list"
			onchange="this.form.submit();"
			default="d.branch_title ASC"
			validate="options"
			>
			<option value="">JGLOBAL_SORT_BY</option>
			<option value="d.branch_title
ASC">JGLOBAL_TITLE_ASC</option>
			<option value="d.branch_title
DESC">JGLOBAL_TITLE_DESC</option>
			<option value="a.state ASC">JSTATUS_ASC</option>
			<option value="a.state DESC">JSTATUS_DESC</option>
		</field>

		<field
			name="limit"
			type="limitbox"
			class="input-mini"
			default="25"
			onchange="this.form.submit();"
		/>
	</fields>
</form>
PK[��-�-models/index.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Index model class for Finder.
 *
 * @since  2.5
 */
class FinderModelIndex extends JModelList
{
	/**
	 * The event to trigger after deleting the data.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $event_after_delete = 'onContentAfterDelete';

	/**
	 * The event to trigger before deleting the data.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $event_before_delete = 'onContentBeforeDelete';

	/**
	 * Constructor.
	 *
	 * @param   array  $config  An associative array of configuration
settings. [optional]
	 *
	 * @since   2.5
	 * @see     JControllerLegacy
	 */
	public function __construct($config = array())
	{
		if (empty($config['filter_fields']))
		{
			$config['filter_fields'] = array(
				'state', 'published', 'l.published',
				'title', 'l.title',
				'type', 'type_id', 'l.type_id',
				't.title', 't_title',
				'url', 'l.url',
				'indexdate', 'l.indexdate',
				'content_map',
			);
		}

		parent::__construct($config);
	}

	/**
	 * Method to test whether a record can be deleted.
	 *
	 * @param   object  $record  A record object.
	 *
	 * @return  boolean  True if allowed to delete the record. Defaults to the
permission for the component.
	 *
	 * @since   2.5
	 */
	protected function canDelete($record)
	{
		return JFactory::getUser()->authorise('core.delete',
$this->option);
	}

	/**
	 * Method to test whether a record can have its state changed.
	 *
	 * @param   object  $record  A record object.
	 *
	 * @return  boolean  True if allowed to change the state of the record.
Defaults to the permission for the component.
	 *
	 * @since   2.5
	 */
	protected function canEditState($record)
	{
		return JFactory::getUser()->authorise('core.edit.state',
$this->option);
	}

	/**
	 * Method to delete one or more records.
	 *
	 * @param   array  $pks  An array of record primary keys.
	 *
	 * @return  boolean  True if successful, false if an error occurs.
	 *
	 * @since   2.5
	 */
	public function delete(&$pks)
	{
		$dispatcher = JEventDispatcher::getInstance();
		$pks = (array) $pks;
		$table = $this->getTable();

		// Include the content plugins for the on delete events.
		JPluginHelper::importPlugin('content');

		// Iterate the items to delete each one.
		foreach ($pks as $i => $pk)
		{
			if ($table->load($pk))
			{
				if ($this->canDelete($table))
				{
					$context = $this->option . '.' . $this->name;

					// Trigger the onContentBeforeDelete event.
					$result = $dispatcher->trigger($this->event_before_delete,
array($context, $table));

					if (in_array(false, $result, true))
					{
						$this->setError($table->getError());

						return false;
					}

					if (!$table->delete($pk))
					{
						$this->setError($table->getError());

						return false;
					}

					// Trigger the onContentAfterDelete event.
					$dispatcher->trigger($this->event_after_delete, array($context,
$table));
				}
				else
				{
					// Prune items that you can't change.
					unset($pks[$i]);
					$error = $this->getError();

					if ($error)
					{
						$this->setError($error);
					}
					else
					{
						$this->setError(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'));
					}
				}
			}
			else
			{
				$this->setError($table->getError());

				return false;
			}
		}

		// Clear the component's cache
		$this->cleanCache();

		return true;
	}

	/**
	 * Build an SQL query to load the list data.
	 *
	 * @return  JDatabaseQuery  A JDatabaseQuery object
	 *
	 * @since   2.5
	 */
	protected function getListQuery()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->select('l.*')
			->select($db->quoteName('t.title', 't_title'))
			->from($db->quoteName('#__finder_links', 'l'))
			->join('INNER',
$db->quoteName('#__finder_types', 't') . ' ON
' . $db->quoteName('t.id') . ' = ' .
$db->quoteName('l.type_id'));

		// Check the type filter.
		$type = $this->getState('filter.type');

		if (is_numeric($type))
		{
			$query->where($db->quoteName('l.type_id') . ' =
' . (int) $type);
		}

		// Check the map filter.
		$contentMapId = $this->getState('filter.content_map');

		if (is_numeric($contentMapId))
		{
			$query->join('INNER',
$db->quoteName('#__finder_taxonomy_map', 'm') .
' ON ' . $db->quoteName('m.link_id') . ' =
' . $db->quoteName('l.link_id'))
				->where($db->quoteName('m.node_id') . ' = ' .
(int) $contentMapId);
		}

		// Check for state filter.
		$state = $this->getState('filter.state');

		if (is_numeric($state))
		{
			$query->where($db->quoteName('l.published') . ' =
' . (int) $state);
		}

		// Check the search phrase.
		$search = $this->getState('filter.search');

		if (!empty($search))
		{
			$search      = $db->quote('%' . str_replace(' ',
'%', $db->escape(trim($search), true) . '%'));
			$orSearchSql = $db->quoteName('l.title') . ' LIKE
' . $search . ' OR ' . $db->quoteName('l.url')
. ' LIKE ' . $search;

			// Filter by indexdate only if $search doesn't contains non-ascii
characters
			if (!preg_match('/[^\x00-\x7F]/', $search))
			{
				$orSearchSql .= ' OR ' .
$query->castAsChar($db->quoteName('l.indexdate')) . '
LIKE ' . $search;
			}

			$query->where('(' . $orSearchSql . ')');
		}

		// Handle the list ordering.
		$listOrder = $this->getState('list.ordering',
'l.title');
		$listDir   = $this->getState('list.direction',
'ASC');

		if ($listOrder === 't.title')
		{
			$ordering = $db->quoteName('t.title') . ' ' .
$db->escape($listDir) . ', ' .
$db->quoteName('l.title') . ' ' .
$db->escape($listDir);
		}
		else
		{
			$ordering = $db->escape($listOrder) . ' ' .
$db->escape($listDir);
		}

		$query->order($ordering);

		return $query;
	}

	/**
	 * Method to get the state of the Smart Search Plugins.
	 *
	 * @return  array  Array of relevant plugins and whether they are enabled
or not.
	 *
	 * @since   2.5
	 */
	public function getPluginState()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->select('name, enabled')
			->from($db->quoteName('#__extensions'))
			->where($db->quoteName('type') . ' = ' .
$db->quote('plugin'))
			->where($db->quoteName('folder') . ' IN (' .
$db->quote('system') . ',' .
$db->quote('content') . ')')
			->where($db->quoteName('element') . ' = ' .
$db->quote('finder'));
		$db->setQuery($query);

		return $db->loadObjectList('name');
	}

	/**
	 * Method to get a store id based on model configuration state.
	 *
	 * This is necessary because the model is used by the component and
	 * different modules that might need different sets of data or different
	 * ordering requirements.
	 *
	 * @param   string  $id  A prefix for the store id. [optional]
	 *
	 * @return  string  A store id.
	 *
	 * @since   2.5
	 */
	protected function getStoreId($id = '')
	{
		// Compile the store id.
		$id .= ':' . $this->getState('filter.search');
		$id .= ':' . $this->getState('filter.state');
		$id .= ':' . $this->getState('filter.type');
		$id .= ':' .
$this->getState('filter.content_map');

		return parent::getStoreId($id);
	}

	/**
	 * Gets the total of indexed items.
	 *
	 * @return  integer  The total of indexed items.
	 *
	 * @since   3.6.0
	 */
	public function getTotalIndexed()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->select('COUNT(link_id)')
			->from($db->quoteName('#__finder_links'));
		$db->setQuery($query);

		$db->execute();

		return (int) $db->loadResult();
	}

	/**
	 * Returns a JTable object, always creating it.
	 *
	 * @param   string  $type    The table type to instantiate. [optional]
	 * @param   string  $prefix  A prefix for the table class name. [optional]
	 * @param   array   $config  Configuration array for model. [optional]
	 *
	 * @return  JTable  A database object
	 *
	 * @since   2.5
	 */
	public function getTable($type = 'Link', $prefix =
'FinderTable', $config = array())
	{
		return JTable::getInstance($type, $prefix, $config);
	}

	/**
	 * Method to purge the index, deleting all links.
	 *
	 * @return  boolean  True on success, false on failure.
	 *
	 * @since   2.5
	 * @throws  Exception on database error
	 */
	public function purge()
	{
		$db = $this->getDbo();

		// Truncate the links table.
		$db->truncateTable('#__finder_links');

		// Truncate the links terms tables.
		for ($i = 0; $i <= 15; $i++)
		{
			// Get the mapping table suffix.
			$suffix = dechex($i);

			$db->truncateTable('#__finder_links_terms' . $suffix);
		}

		// Truncate the terms table.
		$db->truncateTable('#__finder_terms');

		// Truncate the taxonomy map table.
		$db->truncateTable('#__finder_taxonomy_map');

		// Delete all the taxonomy nodes except the root.
		$query = $db->getQuery(true)
			->delete($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('id') . ' > 1');
		$db->setQuery($query);
		$db->execute();

		// Truncate the tokens tables.
		$db->truncateTable('#__finder_tokens');

		// Truncate the tokens aggregate table.
		$db->truncateTable('#__finder_tokens_aggregate');

		return true;
	}

	/**
	 * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
	 *
	 * @param   string  $ordering   An optional ordering field. [optional]
	 * @param   string  $direction  An optional direction. [optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function populateState($ordering = 'l.title',
$direction = 'asc')
	{
		// Load the filter state.
		$this->setState('filter.search',
$this->getUserStateFromRequest($this->context .
'.filter.search', 'filter_search', '',
'string'));
		$this->setState('filter.state',
$this->getUserStateFromRequest($this->context .
'.filter.state', 'filter_state', '',
'cmd'));
		$this->setState('filter.type',
$this->getUserStateFromRequest($this->context .
'.filter.type', 'filter_type', '',
'cmd'));
		$this->setState('filter.content_map',
$this->getUserStateFromRequest($this->context .
'.filter.content_map', 'filter_content_map',
'', 'cmd'));

		// Load the parameters.
		$params = JComponentHelper::getParams('com_finder');
		$this->setState('params', $params);

		// List state information.
		parent::populateState($ordering, $direction);
	}

	/**
	 * Method to change the published state of one or more records.
	 *
	 * @param   array    $pks    A list of the primary keys to change.
	 * @param   integer  $value  The value of the published state. [optional]
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function publish(&$pks, $value = 1)
	{
		$dispatcher = JEventDispatcher::getInstance();
		$user = JFactory::getUser();
		$table = $this->getTable();
		$pks = (array) $pks;

		// Include the content plugins for the change of state event.
		JPluginHelper::importPlugin('content');

		// Access checks.
		foreach ($pks as $i => $pk)
		{
			$table->reset();

			if ($table->load($pk) && !$this->canEditState($table))
			{
				// Prune items that you can't change.
				unset($pks[$i]);
				$this->setError(JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));

				return false;
			}
		}

		// Attempt to change the state of the records.
		if (!$table->publish($pks, $value, $user->get('id')))
		{
			$this->setError($table->getError());

			return false;
		}

		$context = $this->option . '.' . $this->name;

		// Trigger the onContentChangeState event.
		$result = $dispatcher->trigger('onContentChangeState',
array($context, $pks, $value));

		if (in_array(false, $result, true))
		{
			$this->setError($table->getError());

			return false;
		}

		// Clear the component's cache
		$this->cleanCache();

		return true;
	}
}
PK[�r���models/indexer.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Indexer model class for Finder.
 *
 * @since  2.5
 */
class FinderModelIndexer extends JModelLegacy
{
}
PK[ᅑ�**models/maps.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die();

/**
 * Maps model for the Finder package.
 *
 * @since  2.5
 */
class FinderModelMaps extends JModelList
{
	/**
	 * Constructor.
	 *
	 * @param   array  $config  An associative array of configuration
settings. [optional]
	 *
	 * @since   2.5
	 * @see     JControllerLegacy
	 */
	public function __construct($config = array())
	{
		if (empty($config['filter_fields']))
		{
			$config['filter_fields'] = array(
				'state', 'a.state',
				'title', 'a.title',
				'branch',
				'branch_title', 'd.branch_title',
				'level', 'd.level',
			);
		}

		parent::__construct($config);
	}

	/**
	 * Method to test whether a record can be deleted.
	 *
	 * @param   object  $record  A record object.
	 *
	 * @return  boolean  True if allowed to delete the record. Defaults to the
permission for the component.
	 *
	 * @since   2.5
	 */
	protected function canDelete($record)
	{
		return JFactory::getUser()->authorise('core.delete',
$this->option);
	}

	/**
	 * Method to test whether a record can have its state changed.
	 *
	 * @param   object  $record  A record object.
	 *
	 * @return  boolean  True if allowed to change the state of the record.
Defaults to the permission for the component.
	 *
	 * @since   2.5
	 */
	protected function canEditState($record)
	{
		return JFactory::getUser()->authorise('core.edit.state',
$this->option);
	}

	/**
	 * Method to delete one or more records.
	 *
	 * @param   array  $pks  An array of record primary keys.
	 *
	 * @return  boolean  True if successful, false if an error occurs.
	 *
	 * @since   2.5
	 */
	public function delete(&$pks)
	{
		$dispatcher = JEventDispatcher::getInstance();
		$pks = (array) $pks;
		$table = $this->getTable();

		// Include the content plugins for the on delete events.
		JPluginHelper::importPlugin('content');

		// Iterate the items to delete each one.
		foreach ($pks as $i => $pk)
		{
			if ($table->load($pk))
			{
				if ($this->canDelete($table))
				{
					$context = $this->option . '.' . $this->name;

					// Trigger the onContentBeforeDelete event.
					$result = $dispatcher->trigger('onContentBeforeDelete',
array($context, $table));

					if (in_array(false, $result, true))
					{
						$this->setError($table->getError());

						return false;
					}

					if (!$table->delete($pk))
					{
						$this->setError($table->getError());

						return false;
					}

					// Trigger the onContentAfterDelete event.
					$dispatcher->trigger('onContentAfterDelete',
array($context, $table));
				}
				else
				{
					// Prune items that you can't change.
					unset($pks[$i]);
					$error = $this->getError();

					if ($error)
					{
						$this->setError($error);
					}
					else
					{
						$this->setError(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'));
					}
				}
			}
			else
			{
				$this->setError($table->getError());

				return false;
			}
		}

		// Clear the component's cache
		$this->cleanCache();

		return true;
	}

	/**
	 * Build an SQL query to load the list data.
	 *
	 * @return  JDatabaseQuery  A JDatabaseQuery object
	 *
	 * @since   2.5
	 */
	protected function getListQuery()
	{
		$db = $this->getDbo();

		// Select all fields from the table.
		$query = $db->getQuery(true)
			->select('a.id, a.parent_id, a.title, a.state, a.access,
a.ordering')
			->select('CASE WHEN a.parent_id = 1 THEN 1 ELSE 2 END AS
level')
			->select('p.title AS parent_title')
			->from($db->quoteName('#__finder_taxonomy',
'a'))
			->leftJoin($db->quoteName('#__finder_taxonomy',
'p') . ' ON p.id = a.parent_id')
			->where('a.parent_id != 0');

		$childQuery = $db->getQuery(true)
			->select('parent_id')
			->select('COUNT(*) AS num_children')
			->from($db->quoteName('#__finder_taxonomy'))
			->where('parent_id != 0')
			->group('parent_id');

		// Join to get children.
		$query->select('b.num_children');
		$query->select('CASE WHEN a.parent_id = 1 THEN a.title ELSE
p.title END AS branch_title');
		$query->leftJoin('(' . $childQuery . ') AS b ON
b.parent_id = a.id');

		// Join to get the map links.
		$stateQuery = $db->getQuery(true)
			->select('m.node_id')
			->select('COUNT(NULLIF(l.published, 0)) AS
count_published')
			->select('COUNT(NULLIF(l.published, 1)) AS
count_unpublished')
			->from($db->quoteName('#__finder_taxonomy_map',
'm'))
			->leftJoin($db->quoteName('#__finder_links',
'l') . ' ON l.link_id = m.link_id')
			->group('m.node_id');

		$query->select('COALESCE(s.count_published, 0) AS
count_published');
		$query->select('COALESCE(s.count_unpublished, 0) AS
count_unpublished');
		$query->leftJoin('(' . $stateQuery . ') AS s ON
s.node_id = a.id');

		// If the model is set to check item state, add to the query.
		$state = $this->getState('filter.state');

		if (is_numeric($state))
		{
			$query->where('a.state = ' . (int) $state);
		}

		// Filter over level.
		$level = $this->getState('filter.level');

		if (is_numeric($level) && (int) $level === 1)
		{
			$query->where('a.parent_id = 1');
		}

		// Filter the maps over the branch if set.
		$branchId = $this->getState('filter.branch');

		if (is_numeric($branchId))
		{
			$query->where('a.parent_id = ' . (int) $branchId);
		}

		// Filter the maps over the search string if set.
		if ($search = $this->getState('filter.search'))
		{
			$search = $db->quote('%' . str_replace(' ',
'%', $db->escape(trim($search), true) . '%'));
			$query->where('a.title LIKE ' . $search);
		}

		// Handle the list ordering.
		$listOrdering = $this->getState('list.ordering',
'd.branch_title');
		$listDirn     = $this->getState('list.direction',
'ASC');

		if ($listOrdering === 'd.branch_title')
		{
			$query->order("branch_title $listDirn, level ASC, a.title
$listDirn");
		}
		elseif ($listOrdering === 'a.state')
		{
			$query->order("a.state $listDirn, branch_title $listDirn, level
ASC");
		}

		return $query;
	}

	/**
	 * Returns a record count for the query.
	 *
	 * @param   JDatabaseQuery|string  $query  The query.
	 *
	 * @return  integer  Number of rows for query.
	 *
	 * @since   3.0
	 */
	protected function _getListCount($query)
	{
		$query = clone $query;
		$query->clear('select')->clear('join')->clear('order')->clear('limit')->clear('offset')->select('COUNT(*)');

		return (int) $this->getDbo()->setQuery($query)->loadResult();
	}

	/**
	 * Method to get a store id based on model configuration state.
	 *
	 * This is necessary because the model is used by the component and
	 * different modules that might need different sets of data or different
	 * ordering requirements.
	 *
	 * @param   string  $id  A prefix for the store id. [optional]
	 *
	 * @return  string  A store id.
	 *
	 * @since   2.5
	 */
	protected function getStoreId($id = '')
	{
		// Compile the store id.
		$id .= ':' . $this->getState('filter.search');
		$id .= ':' . $this->getState('filter.state');
		$id .= ':' . $this->getState('filter.branch');
		$id .= ':' . $this->getState('filter.level');

		return parent::getStoreId($id);
	}

	/**
	 * Returns a JTable object, always creating it.
	 *
	 * @param   string  $type    The table type to instantiate. [optional]
	 * @param   string  $prefix  A prefix for the table class name. [optional]
	 * @param   array   $config  Configuration array for model. [optional]
	 *
	 * @return  JTable  A database object
	 *
	 * @since   2.5
	 */
	public function getTable($type = 'Map', $prefix =
'FinderTable', $config = array())
	{
		return JTable::getInstance($type, $prefix, $config);
	}

	/**
	 * Method to auto-populate the model state.  Calling getState in this
method will result in recursion.
	 *
	 * @param   string  $ordering   An optional ordering field. [optional]
	 * @param   string  $direction  An optional direction. [optional]
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function populateState($ordering = 'd.branch_title',
$direction = 'ASC')
	{
		// Load the filter state.
		$this->setState('filter.search',
$this->getUserStateFromRequest($this->context .
'.filter.search', 'filter_search', '',
'string'));
		$this->setState('filter.state',
$this->getUserStateFromRequest($this->context .
'.filter.state', 'filter_state', '',
'cmd'));
		$this->setState('filter.branch',
$this->getUserStateFromRequest($this->context .
'.filter.branch', 'filter_branch', '',
'cmd'));
		$this->setState('filter.level',
$this->getUserStateFromRequest($this->context .
'.filter.level', 'filter_level', '',
'cmd'));

		// Load the parameters.
		$params = JComponentHelper::getParams('com_finder');
		$this->setState('params', $params);

		// List state information.
		parent::populateState($ordering, $direction);
	}

	/**
	 * Method to change the published state of one or more records.
	 *
	 * @param   array    $pks    A list of the primary keys to change.
	 * @param   integer  $value  The value of the published state. [optional]
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function publish(&$pks, $value = 1)
	{
		$dispatcher = JEventDispatcher::getInstance();
		$user = JFactory::getUser();
		$table = $this->getTable();
		$pks = (array) $pks;

		// Include the content plugins for the change of state event.
		JPluginHelper::importPlugin('content');

		// Access checks.
		foreach ($pks as $i => $pk)
		{
			$table->reset();

			if ($table->load($pk) && !$this->canEditState($table))
			{
				// Prune items that you can't change.
				unset($pks[$i]);
				$this->setError(JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));

				return false;
			}
		}

		// Attempt to change the state of the records.
		if (!$table->publish($pks, $value, $user->get('id')))
		{
			$this->setError($table->getError());

			return false;
		}

		$context = $this->option . '.' . $this->name;

		// Trigger the onContentChangeState event.
		$result = $dispatcher->trigger('onContentChangeState',
array($context, $pks, $value));

		if (in_array(false, $result, true))
		{
			$this->setError($table->getError());

			return false;
		}

		// Clear the component's cache
		$this->cleanCache();

		return true;
	}

	/**
	 * Method to purge all maps from the taxonomy.
	 *
	 * @return  boolean  Returns true on success, false on failure.
	 *
	 * @since   2.5
	 */
	public function purge()
	{
		$db = $this->getDbo();
		$query = $db->getQuery(true)
			->delete($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' >
1');
		$db->setQuery($query);
		$db->execute();

		$query->clear()
			->delete($db->quoteName('#__finder_taxonomy_map'));
		$db->setQuery($query);
		$db->execute();

		return true;
	}
}
PK[���VVmodels/statistics.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Statistics model class for Finder.
 *
 * @since  2.5
 */
class FinderModelStatistics extends JModelLegacy
{
	/**
	 * Method to get the component statistics
	 *
	 * @return  JObject  The component statistics
	 *
	 * @since   2.5
	 */
	public function getData()
	{
		// Initialise
		$db = $this->getDbo();
		$query = $db->getQuery(true);
		$data = new JObject;

		$query->select('COUNT(term_id)')
			->from($db->quoteName('#__finder_terms'));
		$db->setQuery($query);
		$data->term_count = $db->loadResult();

		$query->clear()
			->select('COUNT(link_id)')
			->from($db->quoteName('#__finder_links'));
		$db->setQuery($query);
		$data->link_count = $db->loadResult();

		$query->clear()
			->select('COUNT(id)')
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' = 1');
		$db->setQuery($query);
		$data->taxonomy_branch_count = $db->loadResult();

		$query->clear()
			->select('COUNT(id)')
			->from($db->quoteName('#__finder_taxonomy'))
			->where($db->quoteName('parent_id') . ' >
1');
		$db->setQuery($query);
		$data->taxonomy_node_count = $db->loadResult();

		$query->clear()
			->select('t.title AS type_title, COUNT(a.link_id) AS
link_count')
			->from($db->quoteName('#__finder_links') . ' AS
a')
			->join('INNER',
$db->quoteName('#__finder_types') . ' AS t ON t.id =
a.type_id')
			->group('a.type_id, t.title')
			->order($db->quoteName('type_title') . '
ASC');
		$db->setQuery($query);
		$data->type_list = $db->loadObjectList();

		$lang  = JFactory::getLanguage();
		$plugins = JPluginHelper::getPlugin('finder');

		foreach ($plugins as $plugin)
		{
			$lang->load('plg_finder_' . $plugin->name .
'.sys', JPATH_ADMINISTRATOR, null, false, true)
			|| $lang->load('plg_finder_' . $plugin->name .
'.sys', JPATH_PLUGINS . '/finder/' . $plugin->name,
null, false, true);
		}

		return $data;
	}
}
PK�[��aa;a;sql/install.mysql.sqlnu�[���--
-- Table structure for table `#__finder_filters`
--

CREATE TABLE IF NOT EXISTS `#__finder_filters` (
  `filter_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `alias` varchar(255) NOT NULL,
  `state` tinyint(1) NOT NULL DEFAULT 1,
  `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `created_by` int(10) unsigned NOT NULL,
  `created_by_alias` varchar(255) NOT NULL,
  `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `modified_by` int(10) unsigned NOT NULL DEFAULT 0,
  `checked_out` int(10) unsigned NOT NULL DEFAULT 0,
  `checked_out_time` datetime NOT NULL DEFAULT '0000-00-00
00:00:00',
  `map_count` int(10) unsigned NOT NULL DEFAULT 0,
  `data` text NOT NULL,
  `params` mediumtext,
  PRIMARY KEY (`filter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links`
--

CREATE TABLE IF NOT EXISTS `#__finder_links` (
  `link_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(255) NOT NULL,
  `route` varchar(255) NOT NULL,
  `title` varchar(400) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `indexdate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `md5sum` varchar(32) DEFAULT NULL,
  `published` tinyint(1) NOT NULL DEFAULT 1,
  `state` int(5) DEFAULT 1,
  `access` int(5) DEFAULT 0,
  `language` varchar(8) NOT NULL,
  `publish_start_date` datetime NOT NULL DEFAULT '0000-00-00
00:00:00',
  `publish_end_date` datetime NOT NULL DEFAULT '0000-00-00
00:00:00',
  `start_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `end_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `list_price` double unsigned NOT NULL DEFAULT 0,
  `sale_price` double unsigned NOT NULL DEFAULT 0,
  `type_id` int(11) NOT NULL,
  `object` mediumblob NOT NULL,
  PRIMARY KEY (`link_id`),
  KEY `idx_type` (`type_id`),
  KEY `idx_title` (`title`(100)),
  KEY `idx_md5` (`md5sum`),
  KEY `idx_url` (`url`(75)),
  KEY `idx_published_list`
(`published`,`state`,`access`,`publish_start_date`,`publish_end_date`,`list_price`),
  KEY `idx_published_sale`
(`published`,`state`,`access`,`publish_start_date`,`publish_end_date`,`sale_price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms0`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms0` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms1`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms1` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms2`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms2` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms3`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms3` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms4`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms4` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms5`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms5` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms6`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms6` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms7`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms7` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms8`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms8` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_terms9`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_terms9` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termsa`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termsa` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termsb`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termsb` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termsc`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termsc` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termsd`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termsd` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termse`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termse` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_links_termsf`
--

CREATE TABLE IF NOT EXISTS `#__finder_links_termsf` (
  `link_id` int(10) unsigned NOT NULL,
  `term_id` int(10) unsigned NOT NULL,
  `weight` float unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`term_id`),
  KEY `idx_term_weight` (`term_id`,`weight`),
  KEY `idx_link_term_weight` (`link_id`,`term_id`,`weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_taxonomy`
--

CREATE TABLE IF NOT EXISTS `#__finder_taxonomy` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `parent_id` int(10) unsigned NOT NULL DEFAULT 0,
  `title` varchar(255) NOT NULL,
  `state` tinyint(1) unsigned NOT NULL DEFAULT 1,
  `access` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `ordering` tinyint(1) unsigned NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`),
  KEY `state` (`state`),
  KEY `ordering` (`ordering`),
  KEY `access` (`access`),
  KEY `idx_parent_published` (`parent_id`,`state`,`access`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Dumping data for table `#__finder_taxonomy`
--

REPLACE INTO `#__finder_taxonomy` (`id`, `parent_id`, `title`, `state`,
`access`, `ordering`) VALUES
(1, 0, 'ROOT', 0, 0, 0);

--
-- Table structure for table `#__finder_taxonomy_map`
--

CREATE TABLE IF NOT EXISTS `#__finder_taxonomy_map` (
  `link_id` int(10) unsigned NOT NULL,
  `node_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`link_id`,`node_id`),
  KEY `link_id` (`link_id`),
  KEY `node_id` (`node_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_terms`
--

CREATE TABLE IF NOT EXISTS `#__finder_terms` (
  `term_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `term` varchar(75) NOT NULL,
  `stem` varchar(75) NOT NULL,
  `common` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `phrase` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `weight` float unsigned NOT NULL DEFAULT 0,
  `soundex` varchar(75) NOT NULL,
  `links` int(10) NOT NULL DEFAULT 0,
  `language` char(3) NOT NULL DEFAULT '',
  PRIMARY KEY (`term_id`),
  UNIQUE KEY `idx_term` (`term`),
  KEY `idx_term_phrase` (`term`,`phrase`),
  KEY `idx_stem_phrase` (`stem`,`phrase`),
  KEY `idx_soundex_phrase` (`soundex`,`phrase`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_terms_common`
--

CREATE TABLE IF NOT EXISTS `#__finder_terms_common` (
  `term` varchar(75) NOT NULL,
  `language` varchar(3) NOT NULL,
  KEY `idx_word_lang` (`term`,`language`),
  KEY `idx_lang` (`language`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Dumping data for table `#__finder_terms_common`
--

REPLACE INTO `#__finder_terms_common` (`term`, `language`) VALUES
('a', 'en'),
('about', 'en'),
('after', 'en'),
('ago', 'en'),
('all', 'en'),
('am', 'en'),
('an', 'en'),
('and', 'en'),
('any', 'en'),
('are', 'en'),
('aren''t', 'en'),
('as', 'en'),
('at', 'en'),
('be', 'en'),
('but', 'en'),
('by', 'en'),
('for', 'en'),
('from', 'en'),
('get', 'en'),
('go', 'en'),
('how', 'en'),
('if', 'en'),
('in', 'en'),
('into', 'en'),
('is', 'en'),
('isn''t', 'en'),
('it', 'en'),
('its', 'en'),
('me', 'en'),
('more', 'en'),
('most', 'en'),
('must', 'en'),
('my', 'en'),
('new', 'en'),
('no', 'en'),
('none', 'en'),
('not', 'en'),
('nothing', 'en'),
('of', 'en'),
('off', 'en'),
('often', 'en'),
('old', 'en'),
('on', 'en'),
('onc', 'en'),
('once', 'en'),
('only', 'en'),
('or', 'en'),
('other', 'en'),
('our', 'en'),
('ours', 'en'),
('out', 'en'),
('over', 'en'),
('page', 'en'),
('she', 'en'),
('should', 'en'),
('small', 'en'),
('so', 'en'),
('some', 'en'),
('than', 'en'),
('thank', 'en'),
('that', 'en'),
('the', 'en'),
('their', 'en'),
('theirs', 'en'),
('them', 'en'),
('then', 'en'),
('there', 'en'),
('these', 'en'),
('they', 'en'),
('this', 'en'),
('those', 'en'),
('thus', 'en'),
('time', 'en'),
('times', 'en'),
('to', 'en'),
('too', 'en'),
('true', 'en'),
('under', 'en'),
('until', 'en'),
('up', 'en'),
('upon', 'en'),
('use', 'en'),
('user', 'en'),
('users', 'en'),
('version', 'en'),
('very', 'en'),
('via', 'en'),
('want', 'en'),
('was', 'en'),
('way', 'en'),
('were', 'en'),
('what', 'en'),
('when', 'en'),
('where', 'en'),
('which', 'en'),
('who', 'en'),
('whom', 'en'),
('whose', 'en'),
('why', 'en'),
('wide', 'en'),
('will', 'en'),
('with', 'en'),
('within', 'en'),
('without', 'en'),
('would', 'en'),
('yes', 'en'),
('yet', 'en'),
('you', 'en'),
('your', 'en'),
('yours', 'en');

--
-- Table structure for table `#__finder_tokens`
--

CREATE TABLE IF NOT EXISTS `#__finder_tokens` (
  `term` varchar(75) NOT NULL,
  `stem` varchar(75) NOT NULL,
  `common` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `phrase` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `weight` float unsigned NOT NULL DEFAULT 1,
  `context` tinyint(1) unsigned NOT NULL DEFAULT 2,
  `language` char(3) NOT NULL DEFAULT '',
  KEY `idx_word` (`term`),
  KEY `idx_context` (`context`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_tokens_aggregate`
--

CREATE TABLE IF NOT EXISTS `#__finder_tokens_aggregate` (
  `term_id` int(10) unsigned NOT NULL,
  `map_suffix` char(1) NOT NULL,
  `term` varchar(75) NOT NULL,
  `stem` varchar(75) NOT NULL,
  `common` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `phrase` tinyint(1) unsigned NOT NULL DEFAULT 0,
  `term_weight` float unsigned NOT NULL,
  `context` tinyint(1) unsigned NOT NULL DEFAULT 2,
  `context_weight` float unsigned NOT NULL,
  `total_weight` float unsigned NOT NULL,
  `language` char(3) NOT NULL DEFAULT '',
  KEY `token` (`term`),
  KEY `keyword_id` (`term_id`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;

--
-- Table structure for table `#__finder_types`
--

CREATE TABLE IF NOT EXISTS `#__finder_types` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) NOT NULL,
  `mime` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci;
PK�[�; ��sql/install.postgresql.sqlnu�[���--
-- Table: #__finder_filters
--
CREATE TABLE "#__finder_filters" (
  "filter_id" serial NOT NULL,
  "title" character varying(255) NOT NULL,
  "alias" character varying(255) NOT NULL,
  "state" smallint DEFAULT 1 NOT NULL,
  "created" timestamp without time zone DEFAULT '1970-01-01
00:00:00' NOT NULL,
  "created_by" integer NOT NULL,
  "created_by_alias" character varying(255) NOT NULL,
  "modified" timestamp without time zone DEFAULT '1970-01-01
00:00:00' NOT NULL,
  "modified_by" integer DEFAULT 0 NOT NULL,
  "checked_out" integer DEFAULT 0 NOT NULL,
  "checked_out_time" timestamp without time zone DEFAULT
'1970-01-01 00:00:00' NOT NULL,
  "map_count" integer DEFAULT 0 NOT NULL,
  "data" text NOT NULL,
  "params" text,
  PRIMARY KEY ("filter_id")
);

--
-- Table: #__finder_links
--
CREATE TABLE "#__finder_links" (
  "link_id" serial NOT NULL,
  "url" character varying(255) NOT NULL,
  "route" character varying(255) NOT NULL,
  "title" character varying(255) DEFAULT NULL,
  "description" character varying(255) DEFAULT NULL,
  "indexdate" timestamp without time zone DEFAULT
'1970-01-01 00:00:00' NOT NULL,
  "md5sum" character varying(32) DEFAULT NULL,
  "published" smallint DEFAULT 1 NOT NULL,
  "state" integer DEFAULT 1,
  "access" integer DEFAULT 0,
  "language" character varying(8) NOT NULL,
  "publish_start_date" timestamp without time zone DEFAULT
'1970-01-01 00:00:00' NOT NULL,
  "publish_end_date" timestamp without time zone DEFAULT
'1970-01-01 00:00:00' NOT NULL,
  "start_date" timestamp without time zone DEFAULT
'1970-01-01 00:00:00' NOT NULL,
  "end_date" timestamp without time zone DEFAULT '1970-01-01
00:00:00' NOT NULL,
  "list_price" numeric(8,2) DEFAULT 0 NOT NULL,
  "sale_price" numeric(8,2) DEFAULT 0 NOT NULL,
  "type_id" bigint NOT NULL,
  "object" bytea NOT NULL,
  PRIMARY KEY ("link_id")
);
CREATE INDEX "#__finder_links_idx_type" on
"#__finder_links" ("type_id");
CREATE INDEX "#__finder_links_idx_title" on
"#__finder_links" ("title");
CREATE INDEX "#__finder_links_idx_md5" on
"#__finder_links" ("md5sum");
CREATE INDEX "#__finder_links_idx_url" on
"#__finder_links" (url(75));
CREATE INDEX "#__finder_links_idx_published_list" on
"#__finder_links" ("published", "state",
"access", "publish_start_date",
"publish_end_date", "list_price");
CREATE INDEX "#__finder_links_idx_published_sale" on
"#__finder_links" ("published", "state",
"access", "publish_start_date",
"publish_end_date", "sale_price");

--
-- Table: #__finder_links_terms0
--
CREATE TABLE "#__finder_links_terms0" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms0_idx_term_weight" on
"#__finder_links_terms0" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms0_idx_link_term_weight" on
"#__finder_links_terms0" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms1
--
CREATE TABLE "#__finder_links_terms1" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms1_idx_term_weight" on
"#__finder_links_terms1" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms1_idx_link_term_weight" on
"#__finder_links_terms1" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms2
--
CREATE TABLE "#__finder_links_terms2" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms2_idx_term_weight" on
"#__finder_links_terms2" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms2_idx_link_term_weight" on
"#__finder_links_terms2" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms3
--
CREATE TABLE "#__finder_links_terms3" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms3_idx_term_weight" on
"#__finder_links_terms3" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms3_idx_link_term_weight" on
"#__finder_links_terms3" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms4
--
CREATE TABLE "#__finder_links_terms4" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms4_idx_term_weight" on
"#__finder_links_terms4" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms4_idx_link_term_weight" on
"#__finder_links_terms4" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms5
--
CREATE TABLE "#__finder_links_terms5" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms5_idx_term_weight" on
"#__finder_links_terms5" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms5_idx_link_term_weight" on
"#__finder_links_terms5" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms6
--
CREATE TABLE "#__finder_links_terms6" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms6_idx_term_weight" on
"#__finder_links_terms6" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms6_idx_link_term_weight" on
"#__finder_links_terms6" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms7
--
CREATE TABLE "#__finder_links_terms7" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms7_idx_term_weight" on
"#__finder_links_terms7" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms7_idx_link_term_weight" on
"#__finder_links_terms7" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms8
--
CREATE TABLE "#__finder_links_terms8" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms8_idx_term_weight" on
"#__finder_links_terms8" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms8_idx_link_term_weight" on
"#__finder_links_terms8" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_terms9
--
CREATE TABLE "#__finder_links_terms9" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms9_idx_term_weight" on
"#__finder_links_terms9" ("term_id",
"weight");
CREATE INDEX "#__finder_links_terms9_idx_link_term_weight" on
"#__finder_links_terms9" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termsa
--
CREATE TABLE "#__finder_links_termsa" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termsa_idx_term_weight" on
"#__finder_links_termsa" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termsa_idx_link_term_weight" on
"#__finder_links_termsa" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termsb
--
CREATE TABLE "#__finder_links_termsb" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termsb_idx_term_weight" on
"#__finder_links_termsb" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termsb_idx_link_term_weight" on
"#__finder_links_termsb" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termsc
--
CREATE TABLE "#__finder_links_termsc" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termsc_idx_term_weight" on
"#__finder_links_termsc" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termsc_idx_link_term_weight" on
"#__finder_links_termsc" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termsd
--
CREATE TABLE "#__finder_links_termsd" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termsd_idx_term_weight" on
"#__finder_links_termsd" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termsd_idx_link_term_weight" on
"#__finder_links_termsd" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termse
--
CREATE TABLE "#__finder_links_termse" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termse_idx_term_weight" on
"#__finder_links_termse" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termse_idx_link_term_weight" on
"#__finder_links_termse" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_links_termsf
--
CREATE TABLE "#__finder_links_termsf" (
  "link_id" integer NOT NULL,
  "term_id" integer NOT NULL,
  "weight" numeric(8,2) NOT NULL,
  PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_termsf_idx_term_weight" on
"#__finder_links_termsf" ("term_id",
"weight");
CREATE INDEX "#__finder_links_termsf_idx_link_term_weight" on
"#__finder_links_termsf" ("link_id",
"term_id", "weight");

--
-- Table: #__finder_taxonomy
--
CREATE TABLE "#__finder_taxonomy" (
  "id" serial NOT NULL,
  "parent_id" integer DEFAULT 0 NOT NULL,
  "title" character varying(255) NOT NULL,
  "state" smallint DEFAULT 1 NOT NULL,
  "access" smallint DEFAULT 0 NOT NULL,
  "ordering" smallint DEFAULT 0 NOT NULL,
  PRIMARY KEY ("id")
);
CREATE INDEX "#__finder_taxonomy_parent_id" on
"#__finder_taxonomy" ("parent_id");
CREATE INDEX "#__finder_taxonomy_state" on
"#__finder_taxonomy" ("state");
CREATE INDEX "#__finder_taxonomy_ordering" on
"#__finder_taxonomy" ("ordering");
CREATE INDEX "#__finder_taxonomy_access" on
"#__finder_taxonomy" ("access");
CREATE INDEX "#__finder_taxonomy_idx_parent_published" on
"#__finder_taxonomy" ("parent_id", "state",
"access");

--
-- Dumping data for table #__finder_taxonomy
--
UPDATE "#__finder_taxonomy" SET ("id",
"parent_id", "title", "state",
"access", "ordering") = (1, 0, 'ROOT', 0, 0,
0) 
WHERE "id"=1;

INSERT INTO "#__finder_taxonomy" ("id",
"parent_id", "title", "state",
"access", "ordering") 
SELECT 1, 0, 'ROOT', 0, 0, 0 WHERE 1 NOT IN 
(SELECT 1 FROM "#__finder_taxonomy" WHERE "id"=1);



--
-- Table: #__finder_taxonomy_map
--
CREATE TABLE "#__finder_taxonomy_map" (
  "link_id" integer NOT NULL,
  "node_id" integer NOT NULL,
  PRIMARY KEY ("link_id", "node_id")
);
CREATE INDEX "#__finder_taxonomy_map_link_id" on
"#__finder_taxonomy_map" ("link_id");
CREATE INDEX "#__finder_taxonomy_map_node_id" on
"#__finder_taxonomy_map" ("node_id");

--
-- Table: #__finder_terms
--
CREATE TABLE "#__finder_terms" (
  "term_id" serial NOT NULL,
  "term" character varying(75) NOT NULL,
  "stem" character varying(75) NOT NULL,
  "common" smallint DEFAULT 0 NOT NULL,
  "phrase" smallint DEFAULT 0 NOT NULL,
  "weight" numeric(8,2) DEFAULT 0 NOT NULL,
  "soundex" character varying(75) NOT NULL,
  "links" integer DEFAULT 0 NOT NULL,
  PRIMARY KEY ("term_id"),
  CONSTRAINT "#__finder_terms_idx_term" UNIQUE ("term")
);
CREATE INDEX "#__finder_terms_idx_term_phrase" on
"#__finder_terms" ("term", "phrase");
CREATE INDEX "#__finder_terms_idx_stem_phrase" on
"#__finder_terms" ("stem", "phrase");
CREATE INDEX "#__finder_terms_idx_soundex_phrase" on
"#__finder_terms" ("soundex", "phrase");

--
-- Table: #__finder_terms_common
--
CREATE TABLE "#__finder_terms_common" (
  "term" character varying(75) NOT NULL,
  "language" character varying(3) NOT NULL
);
CREATE INDEX "#__finder_terms_common_idx_word_lang" on
"#__finder_terms_common" ("term",
"language");
CREATE INDEX "#__finder_terms_common_idx_lang" on
"#__finder_terms_common" ("language");


--
-- Dumping data for table `#__finder_terms_common`
--

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('a', 'en') WHERE
"term"='a';

INSERT INTO "#__finder_terms_common" ("term",
"language") 
SELECT 'a', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='a');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('about', 'en') WHERE
"term"='about';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'about', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='about');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('after', 'en') WHERE
"term"='after';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'after', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='after');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('ago', 'en') WHERE
"term"='ago';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'ago', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='ago');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('all', 'en') WHERE
"term"='all';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'all', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='all');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('am', 'en') WHERE
"term"='am';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'am', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='am');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('an', 'en') WHERE
"term"='an';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'an', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='an');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('and', 'en') WHERE
"term"='and';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'and', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='and');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('any', 'en') WHERE
"term"='any';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'any', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='any');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('are', 'en') WHERE
"term"='are';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'are', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='are');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('aren''t', 'en')
WHERE "term"='aren''t';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'aren''t', 'en' WHERE 1 NOT IN (SELECT
1 FROM "#__finder_terms_common" WHERE
"term"='aren''t');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('as', 'en') WHERE
"term"='as';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'as', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='as');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('at', 'en') WHERE
"term"='at';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'at', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='at');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('be', 'en') WHERE
"term"='be';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'be', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='be');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('but', 'en') WHERE
"term"='but';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'but', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='but');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('by', 'en') WHERE
"term"='by';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'by', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='by');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('for', 'en') WHERE
"term"='for';

INSERT INTO "#__finder_terms_common" ("term",
"language") SELECT 'for', 'en' WHERE 1 NOT IN

(SELECT 1 FROM "#__finder_terms_common" WHERE
"term"='for');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('from', 'en') WHERE
"term"='from';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'from', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='from');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('get', 'en') WHERE
"term"='get';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'get', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='get');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('go', 'en') WHERE
"term"='go';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'go', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='go');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('how', 'en') WHERE
"term"='how';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'how', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='how');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('if', 'en') WHERE
"term"='if';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'if', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='if');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('in', 'en') WHERE
"term"='in';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'in', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='in');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('into', 'en') WHERE
"term"='into';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'into', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='into');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('is', 'en') WHERE
"term"='is';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'is', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='is');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('isn''t', 'en')
WHERE "term"='isn''t';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'isn''t', 'en' WHERE 1 NOT IN (SELECT
1 FROM "#__finder_terms_common" WHERE
"term"='isn''t');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('it', 'en') WHERE
"term"='it';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'it', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='it');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('its', 'en') WHERE
"term"='its';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'its', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='its');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('me', 'en') WHERE
"term"='me';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'me', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='me');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('more', 'en') WHERE
"term"='more';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'more', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='more');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('most', 'en') WHERE
"term"='most';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'most', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='most');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('must', 'en') WHERE
"term"='must';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'must', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='must');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('my', 'en') WHERE
"term"='my';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'my', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='my');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('new', 'en') WHERE
"term"='new';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'new', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='new');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('no', 'en') WHERE
"term"='no';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'no', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='no');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('none', 'en') WHERE
"term"='none';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'none', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='none');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('not', 'en') WHERE
"term"='not';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'not', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='not');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('nothing', 'en') WHERE
"term"='nothing';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'nothing', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='nothing');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('of', 'en') WHERE
"term"='of';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'of', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='of');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('off', 'en') WHERE
"term"='off';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'off', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='off');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('often', 'en') WHERE
"term"='often';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'often', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='often');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('old', 'en') WHERE
"term"='old';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'old', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='old');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('on', 'en') WHERE
"term"='on';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'on', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='on');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('onc', 'en') WHERE
"term"='onc';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'onc', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='onc');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('once', 'en') WHERE
"term"='once';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'once', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='once');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('only', 'en') WHERE
"term"='only';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'only', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='only');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('or', 'en') WHERE
"term"='or';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'or', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='or');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('other', 'en') WHERE
"term"='other';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'other', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='other');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('our', 'en') WHERE
"term"='our';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'our', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='our');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('ours', 'en') WHERE
"term"='ours';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'ours', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='ours');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('out', 'en') WHERE
"term"='out';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'out', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='out');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('over', 'en') WHERE
"term"='over';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'over', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='over');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('page', 'en') WHERE
"term"='page';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'page', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='page');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('she', 'en') WHERE
"term"='she';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'she', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='she');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('should', 'en') WHERE
"term"='should';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'should', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='should');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('small', 'en') WHERE
"term"='small';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'small', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='small');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('so', 'en') WHERE
"term"='so';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'so', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='so');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('some', 'en') WHERE
"term"='some';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'some', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='some');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('than', 'en') WHERE
"term"='than';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'than', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='than');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('thank', 'en') WHERE
"term"='thank';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'thank', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='thank');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('that', 'en') WHERE
"term"='that';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'that', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='that');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('the', 'en') WHERE
"term"='the';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'the', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='the');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('their', 'en') WHERE
"term"='their';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'their', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='their');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('theirs', 'en') WHERE
"term"='theirs';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'theirs', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='theirs');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('them', 'en') WHERE
"term"='them';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'them', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='them');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('then', 'en') WHERE
"term"='then';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'then', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='then');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('there', 'en') WHERE
"term"='there';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'there', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='there');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('these', 'en') WHERE
"term"='these';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'these', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='these');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('they', 'en') WHERE
"term"='they';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'they', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='they');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('this', 'en') WHERE
"term"='this';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'this', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='this');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('those', 'en') WHERE
"term"='those';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'those', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='those');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('thus', 'en') WHERE
"term"='thus';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'thus', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='thus');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('time', 'en') WHERE
"term"='time';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'time', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='time');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('times', 'en') WHERE
"term"='times';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'times', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='times');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('to', 'en') WHERE
"term"='to';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'to', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='to');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('too', 'en') WHERE
"term"='too';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'too', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='too');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('true', 'en') WHERE
"term"='true';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'true', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='true');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('under', 'en')WHERE
"term"='under';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'under', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='under');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('until', 'en') WHERE
"term"='until';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'until', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='until');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('up', 'en') WHERE
"term"='up';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'up', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='up');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('upon', 'en') WHERE
"term"='upon';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'upon', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='upon');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('use', 'en') WHERE
"term"='use';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'use', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='use');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('user', 'en') WHERE
"term"='user';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'user', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='user');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('users', 'en') WHERE
"term"='users';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'users', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='users');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('version', 'en') WHERE
"term"='version';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'version', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='version');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('very', 'en') WHERE
"term"='very';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'very', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='very');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('via', 'en') WHERE
"term"='via';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'via', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='via');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('want', 'en') WHERE
"term"='want';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'want', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='want');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('was', 'en') WHERE
"term"='was';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'was', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='was');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('way', 'en') WHERE
"term"='way';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'way', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='way');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('were', 'en') WHERE
"term"='were';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'were', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='were');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('what', 'en') WHERE
"term"='what';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'what', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='what');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('when', 'en') WHERE
"term"='when';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'when', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='when');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('where', 'en') WHERE
"term"='where';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'where', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='where');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('which', 'en') WHERE
"term"='which';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'which', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='which');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('who', 'en') WHERE
"term"='who';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'who', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='who');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('whom', 'en') WHERE
"term"='whom';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'whom', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='whom');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('whose', 'en') WHERE
"term"='whose';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'whose', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='whose');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('why', 'en') WHERE
"term"='why';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'why', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='why');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('wide', 'en') WHERE
"term"='wide';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'wide', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='wide');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('will', 'en') WHERE
"term"='will';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'will', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='will');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('with', 'en') WHERE
"term"='with';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'with', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='with');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('within', 'en') WHERE
"term"='within';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'within', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='within');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('without', 'en') WHERE
"term"='without';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'without', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='without');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('would', 'en') WHERE
"term"='would';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'would', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='would');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('yes', 'en') WHERE
"term"='yes';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'yes', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='yes');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('yet', 'en') WHERE
"term"='yet';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'yet', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='yet');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('you', 'en') WHERE
"term"='you';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'you', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE "term"='you');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('your', 'en') WHERE
"term"='your';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'your', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='your');

--
UPDATE "#__finder_terms_common" SET ("term",
"language") = ('yours', 'en') WHERE
"term"='yours';

INSERT INTO "#__finder_terms_common" ("term",
"language")
SELECT 'yours', 'en' WHERE 1 NOT IN (SELECT 1 FROM
"#__finder_terms_common" WHERE
"term"='yours');



--
-- Table: #__finder_tokens
--
CREATE TABLE "#__finder_tokens" (
  "term" character varying(75) NOT NULL,
  "stem" character varying(75) NOT NULL,
  "common" smallint DEFAULT 0 NOT NULL,
  "phrase" smallint DEFAULT 0 NOT NULL,
  "weight" numeric(8,2) DEFAULT 1 NOT NULL,
  "context" smallint DEFAULT 2 NOT NULL
);
CREATE INDEX "#__finder_tokens_idx_word" on
"#__finder_tokens" ("term");
CREATE INDEX "#__finder_tokens_idx_context" on
"#__finder_tokens" ("context");

--
-- Table: #__finder_tokens_aggregate
--
CREATE TABLE "#__finder_tokens_aggregate" (
  "term_id" integer NOT NULL,
  "map_suffix" character(1) NOT NULL,
  "term" character varying(75) NOT NULL,
  "stem" character varying(75) NOT NULL,
  "common" smallint DEFAULT 0 NOT NULL,
  "phrase" smallint DEFAULT 0 NOT NULL,
  "term_weight" numeric(8,2) NOT NULL,
  "context" smallint DEFAULT 2 NOT NULL,
  "context_weight" numeric(8,2) NOT NULL,
  "total_weight" numeric(8,2) NOT NULL
);
CREATE INDEX "#__finder_tokens_aggregate_token" on
"#__finder_tokens_aggregate" ("term");
CREATE INDEX "_#__finder_tokens_aggregate_keyword_id" on
"#__finder_tokens_aggregate" ("term_id");

--
-- Table: #__finder_types
--
CREATE TABLE "#__finder_types" (
  "id" serial NOT NULL,
  "title" character varying(100) NOT NULL,
  "mime" character varying(100) NOT NULL,
  PRIMARY KEY ("id"),
  CONSTRAINT "#__finder_types_title" UNIQUE ("title")
);

PK�[�1�iwwsql/uninstall.mysql.sqlnu�[���DROP TABLE
IF EXISTS `#__finder_filters`;
DROP TABLE IF EXISTS `#__finder_links`;
DROP TABLE IF EXISTS `#__finder_links_terms0`;
DROP TABLE IF EXISTS `#__finder_links_terms1`;
DROP TABLE IF EXISTS `#__finder_links_terms2`;
DROP TABLE IF EXISTS `#__finder_links_terms3`;
DROP TABLE IF EXISTS `#__finder_links_terms4`;
DROP TABLE IF EXISTS `#__finder_links_terms5`;
DROP TABLE IF EXISTS `#__finder_links_terms6`;
DROP TABLE IF EXISTS `#__finder_links_terms7`;
DROP TABLE IF EXISTS `#__finder_links_terms8`;
DROP TABLE IF EXISTS `#__finder_links_terms9`;
DROP TABLE IF EXISTS `#__finder_links_termsa`;
DROP TABLE IF EXISTS `#__finder_links_termsb`;
DROP TABLE IF EXISTS `#__finder_links_termsc`;
DROP TABLE IF EXISTS `#__finder_links_termsd`;
DROP TABLE IF EXISTS `#__finder_links_termse`;
DROP TABLE IF EXISTS `#__finder_links_termsf`;
DROP TABLE IF EXISTS `#__finder_taxonomy`;
DROP TABLE IF EXISTS `#__finder_taxonomy_map`;
DROP TABLE IF EXISTS `#__finder_terms`;
DROP TABLE IF EXISTS `#__finder_terms_common`;
DROP TABLE IF EXISTS `#__finder_tokens`;
DROP TABLE IF EXISTS `#__finder_tokens_aggregate`;
DROP TABLE IF EXISTS `#__finder_types`;
PK�[�|��wwsql/uninstall.postgresql.sqlnu�[���DROP
TABLE IF EXISTS "#__finder_filters";
DROP TABLE IF EXISTS "#__finder_links";
DROP TABLE IF EXISTS "#__finder_links_terms0";
DROP TABLE IF EXISTS "#__finder_links_terms1";
DROP TABLE IF EXISTS "#__finder_links_terms2";
DROP TABLE IF EXISTS "#__finder_links_terms3";
DROP TABLE IF EXISTS "#__finder_links_terms4";
DROP TABLE IF EXISTS "#__finder_links_terms5";
DROP TABLE IF EXISTS "#__finder_links_terms6";
DROP TABLE IF EXISTS "#__finder_links_terms7";
DROP TABLE IF EXISTS "#__finder_links_terms8";
DROP TABLE IF EXISTS "#__finder_links_terms9";
DROP TABLE IF EXISTS "#__finder_links_termsa";
DROP TABLE IF EXISTS "#__finder_links_termsb";
DROP TABLE IF EXISTS "#__finder_links_termsc";
DROP TABLE IF EXISTS "#__finder_links_termsd";
DROP TABLE IF EXISTS "#__finder_links_termse";
DROP TABLE IF EXISTS "#__finder_links_termsf";
DROP TABLE IF EXISTS "#__finder_taxonomy";
DROP TABLE IF EXISTS "#__finder_taxonomy_map";
DROP TABLE IF EXISTS "#__finder_terms";
DROP TABLE IF EXISTS "#__finder_terms_common";
DROP TABLE IF EXISTS "#__finder_tokens";
DROP TABLE IF EXISTS "#__finder_tokens_aggregate";
DROP TABLE IF EXISTS "#__finder_types";
PK�[h�܅��tables/filter.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;

/**
 * Filter table class for the Finder package.
 *
 * @since  2.5
 */
class FinderTableFilter extends JTable
{
	/**
	 * Constructor
	 *
	 * @param   JDatabaseDriver  $db  JDatabaseDriver connector object.
	 *
	 * @since   2.5
	 */
	public function __construct(&$db)
	{
		parent::__construct('#__finder_filters', 'filter_id',
$db);

		$this->setColumnAlias('published', 'state');
	}

	/**
	 * Method to bind an associative array or object to the JTable instance. 
This
	 * method only binds properties that are publicly accessible and
optionally
	 * takes an array of properties to ignore when binding.
	 *
	 * @param   array  $array   Named array
	 * @param   mixed  $ignore  An optional array or space separated list of
properties
	 *                          to ignore while binding. [optional]
	 *
	 * @return  mixed  Null if operation was satisfactory, otherwise returns
an error string
	 *
	 * @since   2.5
	 */
	public function bind($array, $ignore = '')
	{
		if (isset($array['params']) &&
is_array($array['params']))
		{
			$registry = new Registry($array['params']);
			$array['params'] = (string) $registry;
		}

		return parent::bind($array, $ignore);
	}

	/**
	 * Method to perform sanity checks on the JTable instance properties to
ensure
	 * they are safe to store in the database.  Child classes should override
this
	 * method to make sure the data they are storing in the database is safe
and
	 * as expected before storage.
	 *
	 * @return  boolean  True if the instance is sane and able to be stored in
the database.
	 *
	 * @since   2.5
	 */
	public function check()
	{
		if (trim($this->alias) === '')
		{
			$this->alias = $this->title;
		}

		$this->alias = JApplicationHelper::stringURLSafe($this->alias);

		if (trim(str_replace('-', '', $this->alias)) ===
'')
		{
			$this->alias =
JFactory::getDate()->format('Y-m-d-H-i-s');
		}

		$params = new Registry($this->params);

		$nullDate = $this->_db->getNullDate();
		$d1 = $params->get('d1', $nullDate);
		$d2 = $params->get('d2', $nullDate);

		// Check the end date is not earlier than the start date.
		if ($d2 > $nullDate && $d2 < $d1)
		{
			// Swap the dates.
			$params->set('d1', $d2);
			$params->set('d2', $d1);
			$this->params = (string) $params;
		}

		return true;
	}

	/**
	 * Method to set the publishing state for a row or list of rows in the
database
	 * table. The method respects checked out rows by other users and will
attempt
	 * to checkin rows that it can after adjustments are made.
	 *
	 * @param   mixed    $pks     An array of primary key values to update. 
If not
	 *                            set the instance property value is used.
[optional]
	 * @param   integer  $state   The publishing state. eg. [0 = unpublished,
1 = published] [optional]
	 * @param   integer  $userId  The user id of the user performing the
operation. [optional]
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function publish($pks = null, $state = 1, $userId = 0)
	{
		$k = $this->_tbl_key;

		// Sanitize input.
		$pks = ArrayHelper::toInteger($pks);
		$userId = (int) $userId;
		$state = (int) $state;

		// If there are no primary keys set check to see if the instance key is
set.
		if (empty($pks))
		{
			if ($this->$k)
			{
				$pks = array($this->$k);
			}
			// Nothing to set publishing state on, return false.
			else
			{
				$this->setError(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));

				return false;
			}
		}

		// Build the WHERE clause for the primary keys.
		$where = $k . '=' . implode(' OR ' . $k .
'=', $pks);

		// Determine if there is checkin support for the table.
		if (!property_exists($this, 'checked_out') ||
!property_exists($this, 'checked_out_time'))
		{
			$checkin = '';
		}
		else
		{
			$checkin = ' AND (checked_out = 0 OR checked_out = ' . (int)
$userId . ')';
		}

		// Update the publishing state for rows with the given primary keys.
		$query = $this->_db->getQuery(true)
			->update($this->_db->quoteName($this->_tbl))
			->set($this->_db->quoteName('state') . ' =
' . (int) $state)
			->where($where);
		$this->_db->setQuery($query . $checkin);

		try
		{
			$this->_db->execute();
		}
		catch (RuntimeException $e)
		{
			$this->setError($e->getMessage());

			return false;
		}

		// If checkin is supported and all rows were adjusted, check them in.
		if ($checkin && count($pks) ===
$this->_db->getAffectedRows())
		{
			// Checkin the rows.
			foreach ($pks as $pk)
			{
				$this->checkIn($pk);
			}
		}

		// If the JTable instance value is in the list of primary keys that were
set, set the instance.
		if (in_array($this->$k, $pks))
		{
			$this->state = $state;
		}

		$this->setError('');

		return true;
	}

	/**
	 * Method to store a row in the database from the JTable instance
properties.
	 * If a primary key value is set the row with that primary key value will
be
	 * updated with the instance property values.  If no primary key value is
set
	 * a new row will be inserted into the database with the properties from
the
	 * JTable instance.
	 *
	 * @param   boolean  $updateNulls  True to update fields even if they are
null. [optional]
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function store($updateNulls = false)
	{
		$date = JFactory::getDate()->toSql();
		$userId = JFactory::getUser()->id;

		$this->modified = $date;

		if ($this->filter_id)
		{
			// Existing item
			$this->modified_by = $userId;
		}
		else
		{
			// New item. A filter's created field can be set by the user,
			// so we don't touch it if it is set.
			if (!(int) $this->created)
			{
				$this->created = $date;
			}

			if (empty($this->created_by))
			{
				$this->created_by = $userId;
			}
		}

		if (is_array($this->data))
		{
			$this->map_count = count($this->data);
			$this->data = implode(',', $this->data);
		}
		else
		{
			$this->map_count = 0;
			$this->data = implode(',', array());
		}

		// Verify that the alias is unique
		$table = JTable::getInstance('Filter', 'FinderTable',
array('dbo' => $this->_db));

		if ($table->load(array('alias' => $this->alias))
&& ($table->filter_id != $this->filter_id ||
$this->filter_id == 0))
		{
			$this->setError(JText::_('JLIB_DATABASE_ERROR_ARTICLE_UNIQUE_ALIAS'));

			return false;
		}

		return parent::store($updateNulls);
	}
}
PK�[>��u__tables/link.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Link table class for the Finder package.
 *
 * @since  2.5
 */
class FinderTableLink extends JTable
{
	/**
	 * Constructor
	 *
	 * @param   JDatabaseDriver  $db  JDatabaseDriver connector object.
	 *
	 * @since   2.5
	 */
	public function __construct(&$db)
	{
		parent::__construct('#__finder_links', 'link_id',
$db);
	}
}
PK�[�&��	�	tables/map.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Utilities\ArrayHelper;

/**
 * Map table class for the Finder package.
 *
 * @since  2.5
 */
class FinderTableMap extends JTable
{
	/**
	 * Constructor
	 *
	 * @param   JDatabaseDriver  $db  JDatabaseDriver connector object.
	 *
	 * @since   2.5
	 */
	public function __construct(&$db)
	{
		parent::__construct('#__finder_taxonomy', 'id', $db);
	}

	/**
	 * Method to set the publishing state for a row or list of rows in the
database
	 * table. The method respects checked out rows by other users and will
attempt
	 * to checkin rows that it can after adjustments are made.
	 *
	 * @param   mixed    $pks     An array of primary key values to update. 
If not
	 *                            set the instance property value is used.
[optional]
	 * @param   integer  $state   The publishing state. eg. [0 = unpublished,
1 = published] [optional]
	 * @param   integer  $userId  The user id of the user performing the
operation. [optional]
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   2.5
	 */
	public function publish($pks = null, $state = 1, $userId = 0)
	{
		$k = $this->_tbl_key;

		// Sanitize input.
		$pks = ArrayHelper::toInteger($pks);
		$state = (int) $state;

		// If there are no primary keys set check to see if the instance key is
set.
		if (empty($pks))
		{
			if ($this->$k)
			{
				$pks = array($this->$k);
			}
			// Nothing to set publishing state on, return false.
			else
			{
				$this->setError(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));

				return false;
			}
		}

		// Build the WHERE clause for the primary keys.
		$where = $k . '=' . implode(' OR ' . $k .
'=', $pks);

		// Update the publishing state for rows with the given primary keys.
		$query = $this->_db->getQuery(true)
			->update($this->_db->quoteName($this->_tbl))
			->set($this->_db->quoteName('state') . ' =
' . (int) $state)
			->where($where);
		$this->_db->setQuery($query);

		try
		{
			$this->_db->execute();
		}
		catch (RuntimeException $e)
		{
			$this->setError($e->getMessage());

			return false;
		}

		// If the JTable instance value is in the list of primary keys that were
set, set the instance.
		if (in_array($this->$k, $pks))
		{
			$this->state = $state;
		}

		$this->setError('');

		return true;
	}
}
PK�[^�M�
�
views/filter/tmpl/edit.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('behavior.formvalidator');
JHtml::_('behavior.keepalive');
JHtml::_('formbehavior.chosen', 'select');
JHtml::_('behavior.tabstate');

JFactory::getDocument()->addScriptDeclaration('
	Joomla.submitbutton = function(task)
	{
		if (task == "filter.cancel" ||
document.formvalidator.isValid(document.getElementById("adminForm")))
		{
			Joomla.submitform(task, document.getElementById("adminForm"));
		}
	};

	jQuery(document).ready(function($) {
		$("#rightbtn").on("click", function() {
			if($(this).text() == "' .
JText::_('COM_FINDER_FILTER_SHOW_ALL') . '") {
				$(".collapse:not(.in)").each(function (index) {
					$(this).collapse("toggle");
				});
				$(this).text("' .
JText::_('COM_FINDER_FILTER_HIDE_ALL') . '");
			} else {
				$(this).text("' .
JText::_('COM_FINDER_FILTER_SHOW_ALL') . '");
				$(".collapse.in").each(function (index) {
				$(this).collapse("toggle");
			});
		}
		return false;
		});

		$(".filter-node").change(function() {
			$(\'input[id="jform_map_count"]\').val(document.querySelectorAll(\'input[type="checkbox"]:checked\').length);
		});


	});
');

JFactory::getDocument()->addStyleDeclaration('
	.accordion-inner .control-group .controls {
		margin-left: 10px;
	}
	.accordion-inner > .control-group {
		margin-bottom: 0;
	}
	');
?>

<form action="<?php echo
JRoute::_('index.php?option=com_finder&view=filter&layout=edit&filter_id='
. (int) $this->item->filter_id); ?>" method="post"
name="adminForm" id="adminForm"
class="form-validate">

	<?php echo JLayoutHelper::render('joomla.edit.title_alias',
$this); ?>

	<div class="form-horizontal">
		<?php echo JHtml::_('bootstrap.startTabSet',
'myTab', array('active' => 'details'));
?>

		<?php echo JHtml::_('bootstrap.addTab', 'myTab',
'details', JText::_('COM_FINDER_EDIT_FILTER')); ?>
		<div class="row-fluid">
			<div class="span9">
				<?php if ($this->total > 0) : ?>
					<div class="well">
						<?php echo $this->form->renderField('map_count');
?>
					</div>
					<button class="btn jform-rightbtn"
type="button"
onclick="jQuery('.filter-node').each(function () {
this.click(); });">
						<span class="icon-checkbox-partial"
aria-hidden="true"></span> <?php echo
JText::_('JGLOBAL_SELECTION_INVERT'); ?></button>

					<button class="btn pull-right" type="button"
id="rightbtn" ><?php echo
JText::_('COM_FINDER_FILTER_SHOW_ALL'); ?></button>
					<hr>
				<?php endif; ?>

				<?php echo JHtml::_('filter.slider',
array('selected_nodes' => $this->filter->data)); ?>
			</div>
			<div class="span3">
				<?php echo JLayoutHelper::render('joomla.edit.global',
$this); ?>
			</div>
		</div>
		<?php echo JHtml::_('bootstrap.endTab'); ?>

		<?php echo JHtml::_('bootstrap.addTab', 'myTab',
'publishing', JText::_('JGLOBAL_FIELDSET_PUBLISHING'));
?>
		<div class="row-fluid form-horizontal-desktop">
			<?php echo
JLayoutHelper::render('joomla.edit.publishingdata', $this); ?>
		</div>
		<?php echo JHtml::_('bootstrap.endTab'); ?>

		<?php echo JLayoutHelper::render('joomla.edit.params',
$this); ?>

		<?php echo JHtml::_('bootstrap.endTabSet'); ?>
	</div>

	<input type="hidden" name="task" value=""
/>
	<input type="hidden" name="return"
value="<?php echo
JFactory::getApplication()->input->get('return',
'', 'BASE64'); ?>" />
	<?php echo JHtml::_('form.token'); ?>
</form>
PK�[h��h
h
views/filter/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Filter view class for Finder.
 *
 * @since  2.5
 */
class FinderViewFilter extends JViewLegacy
{
	/**
	 * The filter object
	 *
	 * @var  FinderTableFilter
	 *
	 * @since  3.6.2
	 */
	protected $filter;

	/**
	 * The JForm object
	 *
	 * @var  JForm
	 *
	 * @since  3.6.2
	 */
	protected $form;

	/**
	 * The active item
	 *
	 * @var  JObject|boolean
	 *
	 * @since  3.6.2
	 */
	protected $item;

	/**
	 * The model state
	 *
	 * @var  mixed
	 *
	 * @since  3.6.2
	 */
	protected $state;

	/**
	 * The total indexed items
	 *
	 * @var  integer
	 *
	 * @since  3.8.0
	 */
	protected $total;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  A string if successful, otherwise a JError object.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Load the view data.
		$this->filter = $this->get('Filter');
		$this->item = $this->get('Item');
		$this->form = $this->get('Form');
		$this->state = $this->get('State');
		$this->total = $this->get('Total');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			throw new Exception(implode("\n", $errors), 500);
		}

		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
		JHtml::addIncludePath(JPATH_SITE .
'/components/com_finder/helpers/html');

		// Configure the toolbar.
		$this->addToolbar();

		return parent::display($tpl);
	}

	/**
	 * Method to configure the toolbar for this view.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function addToolbar()
	{
		JFactory::getApplication()->input->set('hidemainmenu',
true);

		$isNew = ($this->item->filter_id == 0);
		$checkedOut = !($this->item->checked_out == 0 ||
$this->item->checked_out == JFactory::getUser()->id);
		$canDo = JHelperContent::getActions('com_finder');

		// Configure the toolbar.
		JToolbarHelper::title(
			$isNew ? JText::_('COM_FINDER_FILTER_NEW_TOOLBAR_TITLE') :
JText::_('COM_FINDER_FILTER_EDIT_TOOLBAR_TITLE'),
			'zoom-in finder'
		);

		// Set the actions for new and existing records.
		if ($isNew)
		{
			// For new records, check the create permission.
			if ($canDo->get('core.create'))
			{
				JToolbarHelper::apply('filter.apply');
				JToolbarHelper::save('filter.save');
				JToolbarHelper::save2new('filter.save2new');
			}

			JToolbarHelper::cancel('filter.cancel');
		}
		else
		{
			// Can't save the record if it's checked out.
			// Since it's an existing record, check the edit permission.
			if (!$checkedOut && $canDo->get('core.edit'))
			{
				JToolbarHelper::apply('filter.apply');
				JToolbarHelper::save('filter.save');

				// We can save this record, but check the create permission to see if
we can return to make a new one.
				if ($canDo->get('core.create'))
				{
					JToolbarHelper::save2new('filter.save2new');
				}
			}

			// If an existing item, can save as a copy
			if ($canDo->get('core.create'))
			{
				JToolbarHelper::save2copy('filter.save2copy');
			}

			JToolbarHelper::cancel('filter.cancel',
'JTOOLBAR_CLOSE');
		}

		JToolbarHelper::divider();
		JToolbarHelper::help('JHELP_COMPONENTS_FINDER_MANAGE_SEARCH_FILTERS_EDIT');
	}
}
PK�[aۇD��views/filters/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('formbehavior.chosen', 'select');
JHtml::_('bootstrap.tooltip');

$user      = JFactory::getUser();
$userId    = $user->get('id');
$listOrder =
$this->escape($this->state->get('list.ordering'));
$listDirn  =
$this->escape($this->state->get('list.direction'));

JText::script('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT');

JFactory::getDocument()->addScriptDeclaration('
	Joomla.submitbutton = function(pressbutton)
	{
		if (pressbutton == "filters.delete")
		{
			if
(confirm(Joomla.JText._("COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT")))
			{
				Joomla.submitform(pressbutton);
			}
			else
			{
				return false;
			}
		}
		Joomla.submitform(pressbutton);
	};
');
?>
<form action="<?php echo
JRoute::_('index.php?option=com_finder&view=filters');
?>" method="post" name="adminForm"
id="adminForm">
	<?php if (!empty( $this->sidebar)) : ?>
	<div id="j-sidebar-container" class="span2">
		<?php echo $this->sidebar; ?>
	</div>
	<div id="j-main-container" class="span10">
	<?php else : ?>
	<div id="j-main-container">
	<?php endif; ?>
		<?php echo
JLayoutHelper::render('joomla.searchtools.default',
array('view' => $this)); ?>
		<div class="clearfix"> </div>
		<?php if (empty($this->items)) : ?>
		<div class="alert alert-no-items">
			<?php echo JText::_('COM_FINDER_NO_RESULTS_OR_FILTERS');
?>
		</div>
		<?php else : ?>
		<table class="table table-striped">
			<thead>
				<tr>
					<th width="1%" class="nowrap center">
						<?php echo JHtml::_('grid.checkall'); ?>
					</th>
					<th width="1%" class="nowrap">
						<?php echo JHtml::_('searchtools.sort',
'JSTATUS', 'a.state', $listDirn, $listOrder); ?>
					</th>
					<th class="nowrap">
						<?php echo JHtml::_('searchtools.sort',
'JGLOBAL_TITLE', 'a.title', $listDirn, $listOrder);
?>
					</th>
					<th width="10%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_HEADING_CREATED_BY', 'a.created_by_alias',
$listDirn, $listOrder); ?>
					</th>
					<th width="10%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_HEADING_CREATED_ON', 'a.created',
$listDirn, $listOrder); ?>
					</th>
					<th width="5%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_HEADING_MAP_COUNT', 'a.map_count',
$listDirn, $listOrder); ?>
					</th>
					<th width="1%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'JGRID_HEADING_ID', 'a.filter_id', $listDirn,
$listOrder); ?>
					</th>
				</tr>
			</thead>
			<tfoot>
				<tr>
					<td colspan="7">
						<?php echo $this->pagination->getListFooter(); ?>
					</td>
				</tr>
			</tfoot>
			<tbody>
				<?php
				$canCreate                  =
$user->authorise('core.create', 'com_finder');
				$canEdit                    =
$user->authorise('core.edit', 'com_finder');
				$userAuthoriseCoreManage    =
$user->authorise('core.manage', 'com_checkin');
				$userAuthoriseCoreEditState =
$user->authorise('core.edit.state', 'com_finder');
				$userId                     = $user->get('id');
				foreach ($this->items as $i => $item) :
					$canCheckIn   = $userAuthoriseCoreManage || $item->checked_out ==
$userId || $item->checked_out == 0;
					$canChange    = $userAuthoriseCoreEditState && $canCheckIn;
					$escapedTitle = $this->escape($item->title);
					?>
					<tr class="row<?php echo $i % 2; ?>">
						<td class="center">
							<?php echo JHtml::_('grid.id', $i,
$item->filter_id); ?>
						</td>
						<td class="center nowrap">
							<?php echo JHtml::_('jgrid.published', $item->state,
$i, 'filters.', $canChange); ?>
						</td>
						<td>
							<?php if ($item->checked_out) : ?>
								<?php echo JHtml::_('jgrid.checkedout', $i,
$item->editor, $item->checked_out_time, 'filters.',
$canCheckIn); ?>
							<?php endif; ?>
							<?php if ($canEdit) : ?>
								<a href="<?php echo
JRoute::_('index.php?option=com_finder&task=filter.edit&filter_id='
. (int) $item->filter_id); ?>">
									<?php echo $escapedTitle; ?></a>
							<?php else : ?>
								<?php echo $escapedTitle; ?>
							<?php endif; ?>
						</td>
						<td class="nowrap hidden-phone">
							<?php echo $item->created_by_alias ?: $item->user_name;
?>
						</td>
						<td class="nowrap hidden-phone">
							<?php echo JHtml::_('date', $item->created,
JText::_('DATE_FORMAT_LC4')); ?>
						</td>
						<td class="nowrap hidden-phone">
							<?php echo $item->map_count; ?>
						</td>
						<td class="hidden-phone">
							<?php echo (int) $item->filter_id; ?>
						</td>
					</tr>
				<?php endforeach; ?>
			</tbody>
		</table>
		<?php endif; ?>
		<input type="hidden" name="task"
value="" />
		<input type="hidden" name="boxchecked"
value="0" />
		<?php echo JHtml::_('form.token'); ?>
	</div>
</form>
PK�[����views/filters/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Filters view class for Finder.
 *
 * @since  2.5
 */
class FinderViewFilters extends JViewLegacy
{
	/**
	 * An array of items
	 *
	 * @var  array
	 *
	 * @since  3.6.1
	 */
	protected $items;

	/**
	 * The pagination object
	 *
	 * @var  JPagination
	 *
	 * @since  3.6.1
	 */
	protected $pagination;

	/**
	 * The HTML markup for the sidebar
	 *
	 * @var  string
	 *
	 * @since  3.6.1
	 */
	protected $sidebar;

	/**
	 * The model state
	 *
	 * @var  mixed
	 *
	 * @since  3.6.1
	 */
	protected $state;

	/**
	 * The total number of items
	 *
	 * @var  integer
	 *
	 * @since  3.6.1
	 */
	protected $total;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  A string if successful, otherwise a JError object.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Load the view data.
		$this->items         = $this->get('Items');
		$this->pagination    = $this->get('Pagination');
		$this->total         = $this->get('Total');
		$this->state         = $this->get('State');
		$this->filterForm    = $this->get('FilterForm');
		$this->activeFilters = $this->get('ActiveFilters');

		FinderHelper::addSubmenu('filters');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			throw new Exception(implode("\n", $errors), 500);
		}

		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');

		// Configure the toolbar.
		$this->addToolbar();
		$this->sidebar = JHtmlSidebar::render();

		return parent::display($tpl);
	}

	/**
	 * Method to configure the toolbar for this view.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function addToolbar()
	{
		$canDo = JHelperContent::getActions('com_finder');

		JToolbarHelper::title(JText::_('COM_FINDER_FILTERS_TOOLBAR_TITLE'),
'zoom-in finder');
		$toolbar = JToolbar::getInstance('toolbar');

		if ($canDo->get('core.create'))
		{
			JToolbarHelper::addNew('filter.add');
			JToolbarHelper::editList('filter.edit');
			JToolbarHelper::divider();
		}

		if ($canDo->get('core.edit.state'))
		{
			JToolbarHelper::publishList('filters.publish');
			JToolbarHelper::unpublishList('filters.unpublish');
			JToolbarHelper::checkin('filters.checkin');
			JToolbarHelper::divider();
		}

		if ($canDo->get('core.admin') ||
$canDo->get('core.options'))
		{
			JToolbarHelper::preferences('com_finder');
		}

		JToolbarHelper::divider();
		$toolbar->appendButton('Popup', 'bars',
'COM_FINDER_STATISTICS',
'index.php?option=com_finder&view=statistics&tmpl=component',
550, 350);
		JToolbarHelper::divider();
		JToolbarHelper::help('JHELP_COMPONENTS_FINDER_MANAGE_SEARCH_FILTERS');

		if ($canDo->get('core.delete'))
		{
			JToolbarHelper::deleteList('', 'filters.delete');
			JToolbarHelper::divider();
		}
	}
}
PK�['�e���views/index/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('bootstrap.tooltip');
JHtml::_('formbehavior.chosen', 'select');
JHtml::_('bootstrap.popover');

$listOrder =
$this->escape($this->state->get('list.ordering'));
$listDirn  =
$this->escape($this->state->get('list.direction'));
$lang      = JFactory::getLanguage();

JText::script('COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT');
JText::script('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT');

JFactory::getDocument()->addScriptDeclaration('
	Joomla.submitbutton = function(pressbutton)
	{
		if (pressbutton == "index.purge")
		{
			if
(confirm(Joomla.JText._("COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT")))
			{
				Joomla.submitform(pressbutton);
			}
			else
			{
				return false;
			}
		}
		if (pressbutton == "index.delete")
		{
			if
(confirm(Joomla.JText._("COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT")))
			{
				Joomla.submitform(pressbutton);
			}
			else
			{
				return false;
			}
		}

		Joomla.submitform(pressbutton);
	};
');
?>
<form action="<?php echo
JRoute::_('index.php?option=com_finder&view=index');
?>" method="post" name="adminForm"
id="adminForm">
<?php if (!empty( $this->sidebar)) : ?>
	<div id="j-sidebar-container" class="span2">
		<?php echo $this->sidebar; ?>
	</div>
	<div id="j-main-container" class="span10">
<?php else : ?>
	<div id="j-main-container">
<?php endif; ?>
		<?php echo
JLayoutHelper::render('joomla.searchtools.default',
array('view' => $this)); ?>
		<table class="table table-striped">
			<thead>
				<tr>
					<th width="1%" class="nowrap center">
						<?php echo JHtml::_('grid.checkall'); ?>
					</th>
					<th width="1%" class="nowrap center">
						<?php echo JHtml::_('searchtools.sort',
'JSTATUS', 'l.published', $listDirn, $listOrder); ?>
					</th>
					<th class="nowrap">
						<?php echo JHtml::_('searchtools.sort',
'JGLOBAL_TITLE', 'l.title', $listDirn, $listOrder);
?>
					</th>
					<th width="5%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_INDEX_HEADING_INDEX_TYPE', 't.title',
$listDirn, $listOrder); ?>
					</th>
					<th width="1%" class="nowrap hidden-phone">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_INDEX_HEADING_INDEX_DATE', 'l.indexdate',
$listDirn, $listOrder); ?>
					</th>
					<th width="1%" class="nowrap center hidden-phone
hidden-tablet">
						<?php echo JText::_('COM_FINDER_INDEX_HEADING_DETAILS');
?>
					</th>
					<th width="35%" class="nowrap hidden-phone
hidden-tablet">
						<?php echo JHtml::_('searchtools.sort',
'COM_FINDER_INDEX_HEADING_LINK_URL', 'l.url',
$listDirn, $listOrder); ?>
					</th>
				</tr>
			</thead>
			<tfoot>
				<tr>
					<td colspan="7">
						<?php echo $this->pagination->getListFooter(); ?>
					</td>
				</tr>
			</tfoot>
			<tbody>
				<?php $canChange =
JFactory::getUser()->authorise('core.manage',
'com_finder'); ?>
				<?php foreach ($this->items as $i => $item) : ?>
				<tr class="row<?php echo $i % 2; ?>">
					<td class="center">
						<?php echo JHtml::_('grid.id', $i, $item->link_id);
?>
					</td>
					<td class="center">
						<?php echo JHtml::_('jgrid.published',
$item->published, $i, 'index.', $canChange, 'cb');
?>
					</td>
					<td>
						<label for="cb<?php echo $i ?>">
							<?php echo $this->escape($item->title); ?>
						</label>
					</td>
					<td class="small nowrap hidden-phone">
						<?php
						$key = FinderHelperLanguage::branchSingular($item->t_title);
						echo $lang->hasKey($key) ? JText::_($key) : $item->t_title;
						?>
					</td>
					<td class="small nowrap hidden-phone">
						<?php echo JHtml::_('date', $item->indexdate,
JText::_('DATE_FORMAT_LC4')); ?>
					</td>
					<td class="center hidden-phone hidden-tablet">
						<?php if ((int) $item->publish_start_date || (int)
$item->publish_end_date || (int) $item->start_date || (int)
$item->end_date) : ?>
							<span class="icon-calendar pop hasPopover"
aria-hidden="true" data-placement="left"
title="<?php echo
JText::_('COM_FINDER_INDEX_DATE_INFO_TITLE'); ?>"
data-content="<?php echo
JText::sprintf('COM_FINDER_INDEX_DATE_INFO',
$item->publish_start_date, $item->publish_end_date,
$item->start_date, $item->end_date); ?>"></span>
							<span class="element-invisible"><?php echo
JText::sprintf('COM_FINDER_INDEX_DATE_INFO',
$item->publish_start_date, $item->publish_end_date,
$item->start_date, $item->end_date); ?></span>
						<?php endif; ?>
					</td>
					<td class="small break-word hidden-phone
hidden-tablet">
						<?php echo (strlen($item->url) > 80) ? substr($item->url,
0, 70) . '...' : $item->url; ?>
					</td>
				</tr>

				<?php endforeach; ?>
			</tbody>
		</table>
		<input type="hidden" name="task"
value="display" />
		<input type="hidden" name="boxchecked"
value="0" />
		<?php echo JHtml::_('form.token'); ?>
	</div>
</form>
PK�[۾}__views/index/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

/**
 * Index view class for Finder.
 *
 * @since  2.5
 */
class FinderViewIndex extends JViewLegacy
{
	/**
	 * An array of items
	 *
	 * @var  array
	 *
	 * @since  3.6.1
	 */
	protected $items;

	/**
	 * The pagination object
	 *
	 * @var  JPagination
	 *
	 * @since  3.6.1
	 */
	protected $pagination;

	/**
	 * The state of core Smart Search plugins
	 *
	 * @var  array
	 *
	 * @since  3.6.1
	 */
	protected $pluginState;

	/**
	 * The HTML markup for the sidebar
	 *
	 * @var  string
	 *
	 * @since  3.6.1
	 */
	protected $sidebar;

	/**
	 * The model state
	 *
	 * @var  mixed
	 *
	 * @since  3.6.1
	 */
	protected $state;

	/**
	 * The total number of items
	 *
	 * @var  integer
	 *
	 * @since  3.6.1
	 */
	protected $total;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  A string if successful, otherwise a JError object.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Load plugin language files.
		FinderHelperLanguage::loadPluginLanguage();

		$this->items         = $this->get('Items');
		$this->total         = $this->get('Total');
		$this->pagination    = $this->get('Pagination');
		$this->state         = $this->get('State');
		$this->pluginState   = $this->get('pluginState');
		$this->filterForm    = $this->get('FilterForm');
		$this->activeFilters = $this->get('ActiveFilters');

		FinderHelper::addSubmenu('index');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			JError::raiseError(500, implode("\n", $errors));

			return false;
		}

		if (!$this->pluginState['plg_content_finder']->enabled)
		{
			$link =
JRoute::_('index.php?option=com_plugins&task=plugin.edit&extension_id='
. FinderHelper::getFinderPluginId());
			JFactory::getApplication()->enqueueMessage(JText::sprintf('COM_FINDER_INDEX_PLUGIN_CONTENT_NOT_ENABLED',
$link), 'warning');
		}
		elseif ($this->get('TotalIndexed') === 0)
		{
			JFactory::getApplication()->enqueueMessage(JText::_('COM_FINDER_INDEX_NO_DATA')
. '  ' . JText::_('COM_FINDER_INDEX_TIP'),
'notice');
		}

		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');

		// Configure the toolbar.
		$this->addToolbar();
		$this->sidebar = JHtmlSidebar::render();

		return parent::display($tpl);
	}

	/**
	 * Method to configure the toolbar for this view.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function addToolbar()
	{
		$canDo = JHelperContent::getActions('com_finder');

		JToolbarHelper::title(JText::_('COM_FINDER_INDEX_TOOLBAR_TITLE'),
'zoom-in finder');

		$toolbar = JToolbar::getInstance('toolbar');
		$toolbar->appendButton(
			'Popup', 'archive', 'COM_FINDER_INDEX',
'index.php?option=com_finder&view=indexer&tmpl=component',
500, 210, 0, 0,
			'window.parent.location.reload()',
'COM_FINDER_HEADING_INDEXER'
		);

		if ($canDo->get('core.edit.state'))
		{
			JToolbarHelper::publishList('index.publish');
			JToolbarHelper::unpublishList('index.unpublish');
		}

		if ($canDo->get('core.admin') ||
$canDo->get('core.options'))
		{
			JToolbarHelper::preferences('com_finder');
		}

		$toolbar->appendButton('Popup', 'bars',
'COM_FINDER_STATISTICS',
'index.php?option=com_finder&view=statistics&tmpl=component',
550, 350);

		if ($canDo->get('core.delete'))
		{
			JToolbarHelper::deleteList('', 'index.delete');
		}

		if ($canDo->get('core.edit.state'))
		{
			JToolbarHelper::trash('index.purge',
'COM_FINDER_INDEX_TOOLBAR_PURGE', false);
		}

		JToolbarHelper::help('JHELP_COMPONENTS_FINDER_MANAGE_INDEXED_CONTENT');
	}
}
PK�[�
@�rrviews/indexer/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('behavior.keepalive');
JHtml::_('behavior.core');
JHtml::_('jquery.framework');
JHtml::_('script', 'com_finder/indexer.js',
array('version' => 'auto', 'relative'
=> true));
JFactory::getDocument()->addScriptDeclaration('var msg =
"' . JText::_('COM_FINDER_INDEXER_MESSAGE_COMPLETE') .
'";');
?>

<div id="finder-indexer-container">
	<br /><br />
	<h1 id="finder-progress-header"><?php echo
JText::_('COM_FINDER_INDEXER_HEADER_INIT'); ?></h1>

	<p id="finder-progress-message"><?php echo
JText::_('COM_FINDER_INDEXER_MESSAGE_INIT'); ?></p>

	<div id="progress" class="progress progress-striped
active">
		<div id="progress-bar" class="bar bar-success"
aria-valuenow="0" aria-valuemin="0"
aria-valuemax="100"></div>
	</div>

	<input id="finder-indexer-token" type="hidden"
name="<?php echo JFactory::getSession()->getFormToken();
?>" value="1" />
</div>
PK�[�����views/indexer/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Indexer view class for Finder.
 *
 * @since  2.5
 */
class FinderViewIndexer extends JViewLegacy
{

}
PK�[{�s��views/maps/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::_('formbehavior.chosen', 'select');
JHtml::_('bootstrap.tooltip');

$listOrder     =
$this->escape($this->state->get('list.ordering'));
$listDirn      =
$this->escape($this->state->get('list.direction'));
$lang          = JFactory::getLanguage();
$branchFilter  =
$this->escape($this->state->get('filter.branch'));
$colSpan       = $branchFilter ? 5 : 6;
JText::script('COM_FINDER_MAPS_CONFIRM_DELETE_PROMPT');

JFactory::getDocument()->addScriptDeclaration('
	Joomla.submitbutton = function(pressbutton)
	{
		if (pressbutton == "map.delete")
		{
			if
(confirm(Joomla.JText._("COM_FINDER_MAPS_CONFIRM_DELETE_PROMPT")))
			{
				Joomla.submitform(pressbutton);
			}
			else
			{
				return false;
			}
		}
		Joomla.submitform(pressbutton);
	};
');
?>
<form action="<?php echo
JRoute::_('index.php?option=com_finder&view=maps');
?>" method="post" name="adminForm"
id="adminForm">
<?php if (!empty( $this->sidebar)) : ?>
	<div id="j-sidebar-container" class="span2">
		<?php echo $this->sidebar; ?>
	</div>
	<div id="j-main-container" class="span10">
<?php else : ?>
	<div id="j-main-container">
<?php endif; ?>
		<?php echo
JLayoutHelper::render('joomla.searchtools.default',
array('view' => $this)); ?>
		<div class="clearfix"> </div>
		<?php if (empty($this->items)) : ?>
		<div class="alert alert-no-items">
			<?php echo JText::_('COM_FINDER_MAPS_NO_CONTENT'); ?>
		</div>
		<?php else : ?>
		<table class="table table-striped">
			<thead>
				<tr>
					<th width="1%" class="center nowrap">
						<?php echo JHtml::_('grid.checkall'); ?>
					</th>
					<th width="1%" class="center nowrap">
						<?php echo JHtml::_('searchtools.sort',
'JSTATUS', 'a.state', $listDirn, $listOrder); ?>
					</th>
					<th class="nowrap">
						<?php echo JHtml::_('searchtools.sort',
'JGLOBAL_TITLE', 'd.branch_title', $listDirn,
$listOrder); ?>
					</th>
					<?php if (!$branchFilter) : ?>
					<th width="1%" class="nowrap center">
						<?php echo JText::_('COM_FINDER_HEADING_CHILDREN');
?>
					</th>
					<?php endif; ?>
					<th width="1%" class="nowrap center">
						<span class="icon-publish"
aria-hidden="true"></span>
						<span class="hidden-phone"><?php echo
JText::_('COM_FINDER_MAPS_COUNT_PUBLISHED_ITEMS');
?></span>
					</th>
					<th width="1%" class="nowrap center">
						<span class="icon-unpublish"
aria-hidden="true"></span>
						<span class="hidden-phone"><?php echo
JText::_('COM_FINDER_MAPS_COUNT_UNPUBLISHED_ITEMS');
?></span>
					</th>
				</tr>
			</thead>
			<tfoot>
				<tr>
					<td colspan="<?php echo $colSpan; ?>">
						<?php echo $this->pagination->getListFooter(); ?>
					</td>
				</tr>
			</tfoot>
			<tbody>
				<?php $canChange =
JFactory::getUser()->authorise('core.manage',
'com_finder'); ?>
				<?php foreach ($this->items as $i => $item) : ?>
				<tr class="row<?php echo $i % 2; ?>">
					<td class="center">
						<?php echo JHtml::_('grid.id', $i, $item->id); ?>
					</td>
					<td class="center nowrap">
						<?php echo JHtml::_('jgrid.published', $item->state,
$i, 'maps.', $canChange, 'cb'); ?>
					</td>
					<td>
					<?php
					if (trim($item->parent_title, '**') ===
'Language')
					{
						$title = FinderHelperLanguage::branchLanguageTitle($item->title);
					}
					else
					{
						$key = FinderHelperLanguage::branchSingular($item->title);
						$title = $lang->hasKey($key) ? JText::_($key) : $item->title;
					}
					?>
					<?php if ((int) $item->num_children === 0) : ?>
						<span class="gi">&mdash;</span>
					<?php endif; ?>
					<label for="cb<?php echo $i; ?>"
style="display:inline-block;">
						<?php echo $this->escape($title); ?>
					</label>
					<?php if ($this->escape(trim($title, '**')) ===
'Language' && JLanguageMultilang::isEnabled()) : ?>
						<strong><?php echo
JText::_('COM_FINDER_MAPS_MULTILANG'); ?></strong>
					<?php endif; ?>
					</td>
					<?php if (!$branchFilter) : ?>
					<td class="center btns">
					<?php if ((int) $item->num_children !== 0) : ?>
						<a href="<?php echo
JRoute::_('index.php?option=com_finder&view=maps&filter[branch]='
. $item->id); ?>">
							<span class="badge <?php if ($item->num_children >
0) echo 'badge-info'; ?>"><?php echo
$item->num_children; ?></span></a>
					<?php else : ?>
						-
					<?php endif; ?>
					</td>
					<?php endif; ?>
					<td class="center btns">
					<?php if ((int) $item->num_children === 0) : ?>
						<a class="badge <?php if ((int) $item->count_published
> 0) echo 'badge-success'; ?>" title="<?php
echo JText::_('COM_FINDER_MAPS_COUNT_PUBLISHED_ITEMS');
?>" href="<?php echo
JRoute::_('index.php?option=com_finder&view=index&filter[state]=1&filter[content_map]='
. $item->id); ?>">
						<?php echo (int) $item->count_published; ?></a>
					<?php else : ?>
						-
					<?php endif; ?>
					</td>
					<td class="center btns">
					<?php if ((int) $item->num_children === 0) : ?>
						<a class="badge <?php if ((int)
$item->count_unpublished > 0) echo 'badge-important';
?>" title="<?php echo
JText::_('COM_FINDER_MAPS_COUNT_UNPUBLISHED_ITEMS'); ?>"
href="<?php echo
JRoute::_('index.php?option=com_finder&view=index&filter[state]=0&filter[content_map]='
. $item->id); ?>">
						<?php echo (int) $item->count_unpublished; ?></a>
					<?php else : ?>
						-
					<?php endif; ?>
					</td>
				</tr>
				<?php endforeach; ?>
			</tbody>
		</table>
		<?php endif; ?>
	</div>

	<input type="hidden" name="task"
value="display" />
	<input type="hidden" name="boxchecked"
value="0" />
	<?php echo JHtml::_('form.token'); ?>
</form>
PK�[
�u��views/maps/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR .
'/components/com_finder/helpers/language.php');

/**
 * Groups view class for Finder.
 *
 * @since  2.5
 */
class FinderViewMaps extends JViewLegacy
{
	/**
	 * An array of items
	 *
	 * @var  array
	 *
	 * @since  3.6.1
	 */
	protected $items;

	/**
	 * The pagination object
	 *
	 * @var  JPagination
	 *
	 * @since  3.6.1
	 */
	protected $pagination;

	/**
	 * The HTML markup for the sidebar
	 *
	 * @var  string
	 *
	 * @since  3.6.1
	 */
	protected $sidebar;

	/**
	 * The model state
	 *
	 * @var  object
	 *
	 * @since  3.6.1
	 */
	protected $state;

	/**
	 * The total number of items
	 *
	 * @var  object
	 *
	 * @since  3.6.1
	 */
	protected $total;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  A string if successful, otherwise a JError object.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Load plugin language files.
		FinderHelperLanguage::loadPluginLanguage();

		// Load the view data.
		$this->items         = $this->get('Items');
		$this->total         = $this->get('Total');
		$this->pagination    = $this->get('Pagination');
		$this->state         = $this->get('State');
		$this->filterForm    = $this->get('FilterForm');
		$this->activeFilters = $this->get('ActiveFilters');

		FinderHelper::addSubmenu('maps');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			throw new Exception(implode("\n", $errors), 500);
		}

		JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');

		// Prepare the view.
		$this->addToolbar();
		$this->sidebar = JHtmlSidebar::render();

		return parent::display($tpl);
	}

	/**
	 * Method to configure the toolbar for this view.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	protected function addToolbar()
	{
		$canDo = JHelperContent::getActions('com_finder');

		JToolbarHelper::title(JText::_('COM_FINDER_MAPS_TOOLBAR_TITLE'),
'zoom-in finder');

		if ($canDo->get('core.edit.state'))
		{
			JToolbarHelper::publishList('maps.publish');
			JToolbarHelper::unpublishList('maps.unpublish');
			JToolbarHelper::divider();
		}

		if ($canDo->get('core.admin') ||
$canDo->get('core.options'))
		{
			JToolbarHelper::preferences('com_finder');
		}

		JToolbarHelper::divider();
		JToolbar::getInstance('toolbar')->appendButton(
			'Popup',
			'bars',
			'COM_FINDER_STATISTICS',
			'index.php?option=com_finder&view=statistics&tmpl=component',
			550,
			350
		);
		JToolbarHelper::divider();
		JToolbarHelper::help('JHELP_COMPONENTS_FINDER_MANAGE_CONTENT_MAPS');

		if ($canDo->get('core.delete'))
		{
			JToolbarHelper::deleteList('', 'maps.delete');
			JToolbarHelper::divider();
		}
	}
}
PK�[^��;OO!views/statistics/tmpl/default.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;
?>
<h3>
	<?php echo JText::_('COM_FINDER_STATISTICS_TITLE'); ?>
</h3>

<div class="row-fluid">
	<div class="span12">
		<p class="tab-description"><?php echo
JText::sprintf('COM_FINDER_STATISTICS_STATS_DESCRIPTION',
number_format($this->data->term_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')),
number_format($this->data->link_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')),
number_format($this->data->taxonomy_node_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')),
number_format($this->data->taxonomy_branch_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR'))); ?></p>
		<table class="table table-striped table-condensed">
			<thead>
				<tr>
					<th>
						<?php echo
JText::_('COM_FINDER_STATISTICS_LINK_TYPE_HEADING'); ?>
					</th>
					<th>
						<?php echo
JText::_('COM_FINDER_STATISTICS_LINK_TYPE_COUNT'); ?>
					</th>
				</tr>
			</thead>
			<tbody>
				<?php foreach ($this->data->type_list as $type) : ?>
				<tr>
					<td>
						<?php
						$lang_key    = 'PLG_FINDER_STATISTICS_' .
str_replace(' ', '_', $type->type_title);
						$lang_string = JText::_($lang_key);
						echo $lang_string === $lang_key ? $type->type_title :
$lang_string;
						?>
					</td>
					<td>
						<span class="badge badge-info"><?php echo
number_format($type->link_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')); ?></span>
					</td>
				</tr>
				<?php endforeach; ?>
				<tr>
					<td>
						<strong><?php echo
JText::_('COM_FINDER_STATISTICS_LINK_TYPE_TOTAL');
?></strong>
					</td>
					<td>
						<span class="badge badge-info"><?php echo
number_format($this->data->link_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')); ?></span>
					</td>
				</tr>
			</tbody>
		</table>
	</div>
</div>
PK�[�W���views/statistics/view.html.phpnu�[���<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_finder
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Statistics view class for Finder.
 *
 * @since  2.5
 */
class FinderViewStatistics extends JViewLegacy
{
	/**
	 * The index statistics
	 *
	 * @var  JObject
	 *
	 * @since  3.6.1
	 */
	protected $data;

	/**
	 * Method to display the view.
	 *
	 * @param   string  $tpl  A template file to load. [optional]
	 *
	 * @return  mixed  A string if successful, otherwise a JError object.
	 *
	 * @since   2.5
	 */
	public function display($tpl = null)
	{
		// Load the view data.
		$this->data = $this->get('Data');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			throw new Exception(implode("\n", $errors), 500);
		}

		return parent::display($tpl);
	}
}
PK��[�.����controller.phpnu�[���PK��[�d�	�	
�controllers/suggestions.json.phpnu�[���PK��[����AA
�finder.phpnu�[���PK��[��B��7�7Thelpers/html/filter.phpnu�[���PK��[��f��dKhelpers/html/query.phpnu�[���PK��[�(����]helpers/route.phpnu�[���PK��[�Jڄڄ�`models/search.phpnu�[���PK��[0L�`jj��models/suggestions.phpnu�[���PK��[�(�I}}
]�router.phpnu�[���PK��[�u��	views/search/tmpl/default.phpnu�[���PK��[g)4�""Pviews/search/tmpl/default.xmlnu�[���PK��[����
�
"�%views/search/tmpl/default_form.phpnu�[���PK��[��

$�3views/search/tmpl/default_result.phpnu�[���PK��[	���%N>views/search/tmpl/default_results.phpnu�[���PK��[]���
�
cKviews/search/view.feed.phpnu�[���PK��[y�#�??1Vviews/search/view.html.phpnu�[���PK��[{^�ZZ
�tviews/search/view.opensearch.phpnu�[���PK��[��Y��dztemp.phpnu�[���PK��[��LL	P�error_lognu�[���PKo��[Y-�

Վtmpl/search/default.xmlnu�[���PKo��[��=����tmpl/search/default_result.phpnu�[���PKo��[��NL==M�tmpl/search/default_results.phpnu�[���PKo��[_���bb��tmpl/search/default_sorting.phpnu�[���PKo��[�fvv��tmpl/search/default_form.phpnu�[���PKp��[ŃW��L�tmpl/search/default.phpnu�[���PKp��[���]mmL�src/Service/Router.phpnu�[���PKp��[%��I��src/Helper/RouteHelper.phpnu�[���PKp��[m�&@��^src/Helper/FinderHelper.phpnu�[���PKp��[3������src/Model/SuggestionsModel.phpnu�[���PKp��[p	w�RQRQ�/src/Model/SearchModel.phpnu�[���PKp��[:$�~$�src/Controller/DisplayController.phpnu�[���PKp��[]�2�

(�src/Controller/SuggestionsController.phpnu�[���PKp��[��A�1
1
ڒsrc/View/Search/FeedView.phpnu�[���PKp��[f5x(("W�src/View/Search/OpensearchView.phpnu�[���PKp��[�^�_(_(ѩsrc/View/Search/HtmlView.phpnu�[���PK[|I�33
|�access.xmlnu�[���PK[T�:�d"d"
��config.xmlnu�[���PK[Eӓ���controllers/filter.phpnu�[���PK[.M||�controllers/filters.phpnu�[���PK[c�G'GG�controllers/index.phpnu�[���PK[��$��)�)9
controllers/indexer.json.phpnu�[���PK[0ɭ�ttQJcontrollers/maps.phpnu�[���PK[ŜA
	Nfinder.xmlnu�[���PK[f���P	P	QVhelpers/finder.phpnu�[���PK[L&Sߢ��_helpers/html/finder.phpnu�[���PK[X��!�U�U�lhelpers/indexer/adapter.phpnu�[���PK[�o���K�K
��helpers/indexer/driver/mysql.phpnu�[���PK[�RC��?�?%�helpers/indexer/driver/postgresql.phpnu�[���PK[f�X�GGGG!�Nhelpers/indexer/driver/sqlsrv.phpnu�[���PK[:��̴8�8~�helpers/indexer/helper.phpnu�[���PK[.!���:�:|�helpers/indexer/indexer.phpnu�[���PK[�:�{{s
helpers/indexer/parser/html.phpnu�[���PK[�>LO--=helpers/indexer/parser/rtf.phpnu�[���PK[�/AB���
helpers/indexer/parser/txt.phpnu�[���PK[1
�G���#helpers/indexer/parser.phpnu�[���PK[��,̋̋0helpers/indexer/query.phpnu�[���PK[��FL"",�helpers/indexer/result.phpnu�[���PK[��h(h(��helpers/indexer/stemmer/fr.phpnu�[���PK[�(''%J	helpers/indexer/stemmer/porter_en.phpnu�[���PK[�Ӷ��
�
$�.	helpers/indexer/stemmer/snowball.phpnu�[���PK[x����9	helpers/indexer/stemmer.phpnu�[���PK[̝Vqj'j'�A	helpers/indexer/taxonomy.phpnu�[���PK[�j���li	helpers/indexer/token.phpnu�[���PK[(��~##qy	helpers/language.phpnu�[���PK[�x���؅	models/fields/branches.phpnu�[���PK[F�=�zz�	models/fields/contentmap.phpnu�[���PK[KY!��ҕ	models/fields/contenttypes.phpnu�[���PK[�����ߝ	models/fields/directories.phpnu�[���PK[�K
��Ʀ	models/fields/searchfilter.phpnu�[���PK[^o	D}}��	models/filter.phpnu�[���PK[[�l.Z�	models/filters.phpnu�[���PK[?�=�jj��	models/forms/filter.xmlnu�[���PK[�@*$��N�	models/forms/filter_filters.xmlnu�[���PK[}SB((U�	models/forms/filter_index.xmlnu�[���PK[��o�����	models/forms/filter_maps.xmlnu�[���PK[��-�-��	models/index.phpnu�[���PK[�r����
models/indexer.phpnu�[���PK[ᅑ�**�
models/maps.phpnu�[���PK[���VVH
models/statistics.phpnu�[���PK�[��aa;a;�P
sql/install.mysql.sqlnu�[���PK�[�; ��B�
sql/install.postgresql.sqlnu�[���PK�[�1�iwwz.sql/uninstall.mysql.sqlnu�[���PK�[�|��ww83sql/uninstall.postgresql.sqlnu�[���PK�[h�܅���7tables/filter.phpnu�[���PK�[>��u__"Rtables/link.phpnu�[���PK�[�&��	�	�Ttables/map.phpnu�[���PK�[^�M�
�
�^views/filter/tmpl/edit.phpnu�[���PK�[h��h
h
mviews/filter/view.html.phpnu�[���PK�[aۇD���zviews/filters/tmpl/default.phpnu�[���PK�[������views/filters/view.html.phpnu�[���PK�['�e����views/index/tmpl/default.phpnu�[���PK�[۾}__ˮviews/index/view.html.phpnu�[���PK�[�
@�rrs�views/indexer/tmpl/default.phpnu�[���PK�[�����3�views/indexer/view.html.phpnu�[���PK�[{�s���views/maps/tmpl/default.phpnu�[���PK�[
�u����views/maps/view.html.phpnu�[���PK�[^��;OO!��views/statistics/tmpl/default.phpnu�[���PK�[�W�����views/statistics/view.html.phpnu�[���PKbb%"��