first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<access component="com_finder">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" />
<action name="core.options" title="JACTION_OPTIONS" />
<action name="core.manage" title="JACTION_MANAGE" />
<action name="core.create" title="JACTION_CREATE" />
<action name="core.delete" title="JACTION_DELETE" />
<action name="core.edit" title="JACTION_EDIT" />
<action name="core.edit.state" title="JACTION_EDITSTATE" />
</section>
</access>

View File

@ -0,0 +1,414 @@
<?xml version="1.0" encoding="UTF-8"?>
<config>
<help key="Smart_Search:_Options"/>
<inlinehelp button="show"/>
<fieldset
name="search"
label="COM_FINDER_FIELDSET_SEARCH_OPTIONS_LABEL"
>
<field
name="word_match"
type="list"
label="COM_FINDER_CONFIG_WORD_MATCH_LABEL"
description="COM_FINDER_CONFIG_WORD_MATCH_DESC"
default="exact"
>
<option value="exact">COM_FINDER_CONFIG_WORD_MATCH_OPTION_EXACT</option>
<option value="begin">COM_FINDER_CONFIG_WORD_MATCH_OPTION_BEGIN</option>
<option value="fuzzy">COM_FINDER_CONFIG_WORD_MATCH_OPTION_FUZZY</option>
</field>
<field
name="show_taxonomy"
type="radio"
label="COM_FINDER_CONFIG_SHOW_TAXONOMY_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_description"
type="radio"
label="COM_FINDER_CONFIG_SHOW_DESCRIPTION_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="description_length"
type="number"
label="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_LABEL"
default="255"
filter="integer"
showon="show_description:1"
/>
<field
name="show_image"
type="radio"
label="COM_FINDER_CONFIG_SHOW_IMAGE_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="image_class"
type="text"
label="COM_FINDER_CONFIG_IMAGE_CLASS_LABEL"
validate="CssIdentifier"
showon="show_image:1"
/>
<field
name="link_image"
type="radio"
label="COM_FINDER_CONFIG_LINKED_IMAGE_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
showon="show_image:1"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="show_date"
type="radio"
label="COM_FINDER_CONFIG_SHOW_DATE_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_url"
type="radio"
label="COM_FINDER_CONFIG_SHOW_URL_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="gather_search_statistics"
type="radio"
label="COM_FINDER_CONFIG_GATHER_SEARCH_STATISTICS_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="allow_empty_query"
type="radio"
label="COM_FINDER_CONFIG_ALLOW_EMPTY_QUERY_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="show_autosuggest"
type="radio"
label="COM_FINDER_CONFIG_SHOW_AUTOSUGGEST_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_suggested_query"
type="radio"
label="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
validate="options"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_explained_query"
type="radio"
label="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
validate="options"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_advanced"
type="radio"
label="COM_FINDER_CONFIG_SHOW_ADVANCED_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="show_advanced_tips"
type="radio"
label="COM_FINDER_CONFIG_SHOW_ADVANCED_TIPS_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
showon="show_advanced:1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="expand_advanced"
type="radio"
label="COM_FINDER_CONFIG_EXPAND_ADVANCED_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
showon="show_advanced:1"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="show_date_filters"
type="radio"
label="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
showon="show_advanced:1"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="sort_order"
type="list"
label="COM_FINDER_CONFIG_SORT_ORDER_LABEL"
default="relevance"
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="sort_direction"
type="list"
label="COM_FINDER_CONFIG_SORT_DIRECTION_LABEL"
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_HIGHLIGHT_CONTENT_SEARCH_TERMS_DESC"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="opensearch"
type="radio"
label="COM_FINDER_CONFIG_OPENSEARCH_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="opensearch_name"
type="text"
label="COM_FINDER_CONFIG_FIELD_OPENSEARCH_NAME_LABEL"
default=""
showon="opensearch:1"
/>
<field
name="opensearch_description"
type="textarea"
label="COM_FINDER_CONFIG_FIELD_OPENSEARCH_DESCRIPTION_LABEL"
default=""
cols="30"
rows="2"
showon="opensearch:1"
/>
</fieldset>
<fieldset
name="index"
label="COM_FINDER_FIELDSET_INDEX_OPTIONS_LABEL"
description="COM_FINDER_FIELDSET_INDEX_OPTIONS_DESCRIPTION"
>
<field
name="tuplecount"
type="list"
label="COM_FINDER_CONFIG_TUPLECOUNT_LABEL"
default="1"
validate="options"
>
<option value="1">COM_FINDER_CONFIG_TUPLECOUNT_PHRASE_DISABLED</option>
<option value="3">COM_FINDER_CONFIG_TUPLECOUNT_PHRASE_ENABLED</option>
</field>
<field
name="batch_size"
type="list"
label="COM_FINDER_CONFIG_BATCH_SIZE_LABEL"
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="title_multiplier"
type="number"
label="COM_FINDER_CONFIG_TITLE_MULTIPLIER_LABEL"
default="1.7"
min="0"
/>
<field
name="text_multiplier"
type="number"
label="COM_FINDER_CONFIG_TEXT_MULTIPLIER_LABEL"
default="0.7"
min="0"
/>
<field
name="meta_multiplier"
type="number"
label="COM_FINDER_CONFIG_META_MULTIPLIER_LABEL"
default="1.2"
min="0"
/>
<field
name="path_multiplier"
type="number"
label="COM_FINDER_CONFIG_PATH_MULTIPLIER_LABEL"
default="2.0"
min="0"
/>
<field
name="misc_multiplier"
type="number"
label="COM_FINDER_CONFIG_MISC_MULTIPLIER_LABEL"
default="0.3"
min="0"
/>
<field
name="enable_logging"
type="radio"
label="COM_FINDER_CONFIG_ENABLE_LOGGING_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="language_default"
type="language"
label="COM_FINDER_CONFIG_LANGUAGE_DEFAULT_LABEL"
description="COM_FINDER_CONFIG_LANGUAGE_DEFAULT_DESC"
>
<option value="-1">COM_FINDER_CONFIG_LANGUAGE_DEFAULT_DEFAULT_LANGUAGE</option>
<option value="">COM_FINDER_CONFIG_LANGUAGE_DEFAULT_NONE</option>
</field>
<field
name="filter_commonwords"
type="radio"
label="COM_FINDER_CONFIG_FILTER_COMMONWORDS_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="filter_numerics"
type="radio"
label="COM_FINDER_CONFIG_FILTER_NUMERICS_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
filter="rules"
validate="rules"
component="com_finder"
section="component"
/>
</fieldset>
</config>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="component" method="upgrade">
<name>com_finder</name>
<author>Joomla! Project</author>
<copyright>(C) 2011 Open Source Matters, Inc.</copyright>
<creationDate>2011-08</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>4.0.0</version>
<description>COM_FINDER_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Component\Finder</namespace>
<menu link="option=com_finder">COM_FINDER</menu>
<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>
<files folder="site">
<folder>helpers</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<media destination="com_finder" folder="media">
<folder>css</folder>
<folder>js</folder>
</media>
<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>finder.xml</filename>
<folder>forms</folder>
<folder>helpers</folder>
<folder>services</folder>
<folder>src</folder>
<folder>sql</folder>
<folder>tmpl</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:search-plus" link="option=com_finder">COM_FINDER</menu>
</administration>
</extension>

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset>
<field
name="filter_id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
class="readonly"
default="0"
readonly="true"
/>
<field
name="title"
type="text"
label="JGLOBAL_TITLE"
required="true"
/>
<field
name="alias"
type="text"
label="JFIELD_ALIAS_LABEL"
description="JFIELD_ALIAS_DESC"
hint="JFIELD_ALIAS_PLACEHOLDER"
/>
<field
name="created"
type="calendar"
label="JGLOBAL_FIELD_CREATED_LABEL"
translateformat="true"
showtime="true"
filter="user_utc"
/>
<field
name="modified"
type="calendar"
label="JGLOBAL_FIELD_MODIFIED_LABEL"
class="readonly"
translateformat="true"
showtime="true"
readonly="true"
filter="user_utc"
/>
<field
name="created_by"
type="user"
label="COM_FINDER_FIELD_CREATED_BY_LABEL"
validate="UserId"
/>
<field
name="created_by_alias"
type="text"
label="COM_FINDER_FIELD_CREATED_BY_ALIAS_LABEL"
/>
<field
name="modified_by"
type="user"
label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
class="readonly"
readonly="true"
filter="unset"
validate="UserId"
/>
<field
name="checked_out"
type="hidden"
filter="unset"
/>
<field
name="checked_out_time"
type="hidden"
filter="unset"
/>
<field
name="state"
type="list"
label="JSTATUS"
class="form-select-color-state"
filter="intval"
default="1"
validate="options"
>
<option value="1">JPUBLISHED</option>
<option value="0">JUNPUBLISHED</option>
</field>
<field
name="map_count"
type="text"
label="COM_FINDER_FILTER_MAP_COUNT"
class="readonly"
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"
default=""
filter="string"
validate="options"
>
<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"
translateformat="true"
filter="user_utc"
/>
<field
name="w2"
type="list"
label="COM_FINDER_FILTER_WHEN_END_DATE_LABEL"
default=""
filter="string"
validate="options"
>
<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"
translateformat="true"
filter="user_utc"
/>
</fieldset>
</fields>
</form>

View File

@ -0,0 +1,56 @@
<?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="JSTATUS"
optionsFilter="0,1"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_PUBLISHED</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
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"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<form addfieldprefix="Joomla\Component\Finder\Administrator\Field">
<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="JSTATUS"
optionsFilter="0,1"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_PUBLISHED</option>
</field>
<field
name="type"
type="contenttypes"
label="COM_FINDER_MAPS_HEADING_TYPE"
class="js-select-submit-on-change"
>
<option value="">COM_FINDER_MAPS_SELECT_TYPE</option>
</field>
<field
name="content_map"
type="contentmap"
label="COM_FINDER_MAPS_HEADING_CONTENT_MAP"
class="js-select-submit-on-change"
>
<option value="">COM_FINDER_FILTER_SELECT_CONTENT_MAP</option>
</field>
<field
name="language"
type="contentlanguage"
label="JGRID_HEADING_LANGUAGE"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_LANGUAGE</option>
<option value="*">JALL</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
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.language ASC" requires="multilanguage">JGRID_HEADING_LANGUAGE_ASC</option>
<option value="l.language DESC" requires="multilanguage">JGRID_HEADING_LANGUAGE_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"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<form addfieldprefix="Joomla\Component\Finder\Administrator\Field">
<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="JSTATUS"
optionsFilter="0,1"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_PUBLISHED</option>
</field>
<field
name="branch"
type="branches"
label="COM_FINDER_MAPS_HEADING_BRANCH"
default="0"
class="js-select-submit-on-change"
/>
<field
name="level"
type="integer"
label="JGLOBAL_MAXLEVEL_LABEL"
first="1"
last="2"
step="1"
languages="*"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_MAX_LEVELS</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="branch_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="branch_title ASC, a.lft ASC">JGLOBAL_TITLE_ASC</option>
<option value="branch_title DESC, a.lft DESC">JGLOBAL_TITLE_DESC</option>
<option value="a.language ASC" requires="multilanguage">JGRID_HEADING_LANGUAGE_ASC</option>
<option value="a.language DESC" requires="multilanguage">JGRID_HEADING_LANGUAGE_DESC</option>
</field>
<field
name="limit"
type="limitbox"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fields name="filter">
<field
name="search"
type="text"
inputmode="search"
label="COM_FINDER_SEARCH_IN_PHRASE_LABEL"
description="COM_FINDER_SEARCH_IN_PHRASE_DESC"
hint="JSEARCH_FILTER"
/>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="a.hits ASC"
validate="options"
>
<option value="">JGLOBAL_SORT_BY</option>
<option value="a.searchterm ASC">COM_FINDER_HEADING_SEARCH_TERM_ASC</option>
<option value="a.searchterm DESC">COM_FINDER_HEADING_SEARCH_TERM_DESC</option>
<option value="a.hits ASC">JGLOBAL_HITS_ASC</option>
<option value="a.hits DESC">JGLOBAL_HITS_DESC</option>
</field>
<field
name="limit"
type="limitbox"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset name="form">
<field
name="plugin"
type="plugins"
label="COM_FINDER_FIELD_FINDER_PLUGIN_LABEL"
folder="finder"
required="true"
/>
<field
name="id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
required="true"
/>
</fieldset>
</form>

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Adapter class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Adapter instead
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Helper class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Helper instead
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Parser class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* 5.0 Use \Joomla\Component\Finder\Administrator\Indexer\Parser instead
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Query class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Query instead
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Result class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Result instead
*/

View File

@ -0,0 +1,19 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Taxonomy class.
*
* @since 2.5
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Taxonomy instead
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @package Joomla.Administrator
* @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;
/**
* Finder Indexer Token class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Indexer\Token instead
*/

View File

@ -0,0 +1,29 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2012 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\Administrator\Helper\LanguageHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Finder language helper class.
*
* @since 2.5
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Finder\Administrator\Helper\LanguageHelper instead
*/
class FinderHelperLanguage extends LanguageHelper
{
}

View File

@ -0,0 +1,58 @@
<?php
/**
* @package Joomla.Administrator
* @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
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\Extension\Service\Provider\RouterFactory;
use Joomla\CMS\HTML\Registry;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\Component\Finder\Administrator\Extension\FinderComponent;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
/**
* The finder service provider.
*
* @since 4.0.0
*/
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.0.0
*/
public function register(Container $container)
{
$container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\Finder'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Finder'));
$container->registerServiceProvider(new RouterFactory('\\Joomla\\Component\\Finder'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new FinderComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
$component->setRouterFactory($container->get(RouterFactoryInterface::class));
$component->setRegistry($container->get(Registry::class));
return $component;
}
);
}
};

View File

@ -0,0 +1,391 @@
--
-- Table structure for table `#__finder_filters`
--
CREATE TABLE IF NOT EXISTS `#__finder_filters` (
`filter_id` int unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`alias` varchar(255) NOT NULL,
`state` tinyint NOT NULL DEFAULT 1,
`created` datetime NOT NULL,
`created_by` int unsigned NOT NULL DEFAULT 0,
`created_by_alias` varchar(255) NOT NULL DEFAULT '',
`modified` datetime NOT NULL,
`modified_by` int unsigned NOT NULL DEFAULT 0,
`checked_out` int unsigned NOT NULL DEFAULT 0,
`checked_out_time` datetime,
`map_count` int unsigned NOT NULL DEFAULT 0,
`data` text,
`params` mediumtext,
PRIMARY KEY (`filter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `#__finder_links`
--
CREATE TABLE IF NOT EXISTS `#__finder_links` (
`link_id` int unsigned NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`route` varchar(255) NOT NULL,
`title` varchar(400) DEFAULT NULL,
`description` text,
`indexdate` datetime NOT NULL,
`md5sum` varchar(32) DEFAULT NULL,
`published` tinyint NOT NULL DEFAULT 1,
`state` int NOT NULL DEFAULT 1,
`access` int NOT NULL DEFAULT 0,
`language` char(7) NOT NULL DEFAULT '',
`publish_start_date` datetime,
`publish_end_date` datetime,
`start_date` datetime,
`end_date` datetime,
`list_price` double unsigned NOT NULL DEFAULT 0,
`sale_price` double unsigned NOT NULL DEFAULT 0,
`type_id` int NOT NULL,
`object` mediumblob,
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_language` (`language`),
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_unicode_ci;
--
-- Table structure for table `#__finder_links_terms`
--
CREATE TABLE IF NOT EXISTS `#__finder_links_terms` (
`link_id` int unsigned NOT NULL,
`term_id` int unsigned NOT NULL,
`weight` float unsigned NOT NULL DEFAULT 0,
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_unicode_ci;
--
-- Table structure for table `#__finder_logging`
--
CREATE TABLE IF NOT EXISTS `#__finder_logging` (
`searchterm` VARCHAR(255) NOT NULL DEFAULT '',
`md5sum` VARCHAR(32) NOT NULL DEFAULT '',
`query` BLOB NOT NULL,
`hits` int NOT NULL DEFAULT 1,
`results` int NOT NULL DEFAULT 0,
PRIMARY KEY (`md5sum`),
INDEX `searchterm` (`searchterm`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `#__finder_taxonomy`
--
CREATE TABLE IF NOT EXISTS `#__finder_taxonomy` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`parent_id` int UNSIGNED NOT NULL DEFAULT '0',
`lft` int NOT NULL DEFAULT '0',
`rgt` int NOT NULL DEFAULT '0',
`level` int UNSIGNED NOT NULL DEFAULT '0',
`path` VARCHAR(400) NOT NULL DEFAULT '',
`title` VARCHAR(255) NOT NULL DEFAULT '',
`alias` VARCHAR(400) NOT NULL DEFAULT '',
`state` tinyint UNSIGNED NOT NULL DEFAULT '1',
`access` tinyint UNSIGNED NOT NULL DEFAULT '1',
`language` CHAR(7) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
INDEX `idx_state` (`state`),
INDEX `idx_access` (`access`),
INDEX `idx_path` (`path`(100)),
INDEX `idx_left_right` (`lft`, `rgt`),
INDEX `idx_alias` (`alias`(100)),
INDEX `idx_language` (`language`),
INDEX `idx_parent_published` (`parent_id`, `state`, `access`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `#__finder_taxonomy`
--
INSERT INTO `#__finder_taxonomy` (`id`, `parent_id`, `lft`, `rgt`, `level`, `path`, `title`, `alias`, `state`, `access`, `language`) VALUES
(1, 0, 0, 1, 0, '', 'ROOT', 'root', 1, 1, '*');
--
-- Table structure for table `#__finder_taxonomy_map`
--
CREATE TABLE IF NOT EXISTS `#__finder_taxonomy_map` (
`link_id` int unsigned NOT NULL,
`node_id` int 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_unicode_ci;
--
-- Table structure for table `#__finder_terms`
--
CREATE TABLE IF NOT EXISTS `#__finder_terms` (
`term_id` int unsigned NOT NULL AUTO_INCREMENT,
`term` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`stem` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`common` tinyint unsigned NOT NULL DEFAULT 0,
`phrase` tinyint unsigned NOT NULL DEFAULT 0,
`weight` float unsigned NOT NULL DEFAULT 0,
`soundex` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`links` int NOT NULL DEFAULT 0,
`language` char(7) NOT NULL DEFAULT '',
PRIMARY KEY (`term_id`),
UNIQUE KEY `idx_term_language` (`term`,`language`),
KEY `idx_stem` (`stem`),
KEY `idx_term_phrase` (`term`,`phrase`),
KEY `idx_stem_phrase` (`stem`,`phrase`),
KEY `idx_soundex_phrase` (`soundex`,`phrase`),
KEY `idx_language` (`language`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `#__finder_terms_common`
--
CREATE TABLE IF NOT EXISTS `#__finder_terms_common` (
`term` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`language` char(7) NOT NULL DEFAULT '',
`custom` int NOT NULL DEFAULT '0',
UNIQUE KEY `idx_term_language` (`term`,`language`),
KEY `idx_lang` (`language`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `#__finder_terms_common`
--
INSERT INTO `#__finder_terms_common` (`term`, `language`, `custom`) VALUES
('i', 'en', 0),
('me', 'en', 0),
('my', 'en', 0),
('myself', 'en', 0),
('we', 'en', 0),
('our', 'en', 0),
('ours', 'en', 0),
('ourselves', 'en', 0),
('you', 'en', 0),
('your', 'en', 0),
('yours', 'en', 0),
('yourself', 'en', 0),
('yourselves', 'en', 0),
('he', 'en', 0),
('him', 'en', 0),
('his', 'en', 0),
('himself', 'en', 0),
('she', 'en', 0),
('her', 'en', 0),
('hers', 'en', 0),
('herself', 'en', 0),
('it', 'en', 0),
('its', 'en', 0),
('itself', 'en', 0),
('they', 'en', 0),
('them', 'en', 0),
('their', 'en', 0),
('theirs', 'en', 0),
('themselves', 'en', 0),
('what', 'en', 0),
('which', 'en', 0),
('who', 'en', 0),
('whom', 'en', 0),
('this', 'en', 0),
('that', 'en', 0),
('these', 'en', 0),
('those', 'en', 0),
('am', 'en', 0),
('is', 'en', 0),
('are', 'en', 0),
('was', 'en', 0),
('were', 'en', 0),
('be', 'en', 0),
('been', 'en', 0),
('being', 'en', 0),
('have', 'en', 0),
('has', 'en', 0),
('had', 'en', 0),
('having', 'en', 0),
('do', 'en', 0),
('does', 'en', 0),
('did', 'en', 0),
('doing', 'en', 0),
('would', 'en', 0),
('should', 'en', 0),
('could', 'en', 0),
('ought', 'en', 0),
('i\'m', 'en', 0),
('you\'re', 'en', 0),
('he\'s', 'en', 0),
('she\'s', 'en', 0),
('it\'s', 'en', 0),
('we\'re', 'en', 0),
('they\'re', 'en', 0),
('i\'ve', 'en', 0),
('you\'ve', 'en', 0),
('we\'ve', 'en', 0),
('they\'ve', 'en', 0),
('i\'d', 'en', 0),
('you\'d', 'en', 0),
('he\'d', 'en', 0),
('she\'d', 'en', 0),
('we\'d', 'en', 0),
('they\'d', 'en', 0),
('i\'ll', 'en', 0),
('you\'ll', 'en', 0),
('he\'ll', 'en', 0),
('she\'ll', 'en', 0),
('we\'ll', 'en', 0),
('they\'ll', 'en', 0),
('isn\'t', 'en', 0),
('aren\'t', 'en', 0),
('wasn\'t', 'en', 0),
('weren\'t', 'en', 0),
('hasn\'t', 'en', 0),
('haven\'t', 'en', 0),
('hadn\'t', 'en', 0),
('doesn\'t', 'en', 0),
('don\'t', 'en', 0),
('didn\'t', 'en', 0),
('won\'t', 'en', 0),
('wouldn\'t', 'en', 0),
('shan\'t', 'en', 0),
('shouldn\'t', 'en', 0),
('can\'t', 'en', 0),
('cannot', 'en', 0),
('couldn\'t', 'en', 0),
('mustn\'t', 'en', 0),
('let\'s', 'en', 0),
('that\'s', 'en', 0),
('who\'s', 'en', 0),
('what\'s', 'en', 0),
('here\'s', 'en', 0),
('there\'s', 'en', 0),
('when\'s', 'en', 0),
('where\'s', 'en', 0),
('why\'s', 'en', 0),
('how\'s', 'en', 0),
('a', 'en', 0),
('an', 'en', 0),
('the', 'en', 0),
('and', 'en', 0),
('but', 'en', 0),
('if', 'en', 0),
('or', 'en', 0),
('because', 'en', 0),
('as', 'en', 0),
('until', 'en', 0),
('while', 'en', 0),
('of', 'en', 0),
('at', 'en', 0),
('by', 'en', 0),
('for', 'en', 0),
('with', 'en', 0),
('about', 'en', 0),
('against', 'en', 0),
('between', 'en', 0),
('into', 'en', 0),
('through', 'en', 0),
('during', 'en', 0),
('before', 'en', 0),
('after', 'en', 0),
('above', 'en', 0),
('below', 'en', 0),
('to', 'en', 0),
('from', 'en', 0),
('up', 'en', 0),
('down', 'en', 0),
('in', 'en', 0),
('out', 'en', 0),
('on', 'en', 0),
('off', 'en', 0),
('over', 'en', 0),
('under', 'en', 0),
('again', 'en', 0),
('further', 'en', 0),
('then', 'en', 0),
('once', 'en', 0),
('here', 'en', 0),
('there', 'en', 0),
('when', 'en', 0),
('where', 'en', 0),
('why', 'en', 0),
('how', 'en', 0),
('all', 'en', 0),
('any', 'en', 0),
('both', 'en', 0),
('each', 'en', 0),
('few', 'en', 0),
('more', 'en', 0),
('most', 'en', 0),
('other', 'en', 0),
('some', 'en', 0),
('such', 'en', 0),
('no', 'en', 0),
('nor', 'en', 0),
('not', 'en', 0),
('only', 'en', 0),
('own', 'en', 0),
('same', 'en', 0),
('so', 'en', 0),
('than', 'en', 0),
('too', 'en', 0),
('very', 'en', 0);
--
-- Table structure for table `#__finder_tokens`
--
CREATE TABLE IF NOT EXISTS `#__finder_tokens` (
`term` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`stem` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`common` tinyint unsigned NOT NULL DEFAULT 0,
`phrase` tinyint unsigned NOT NULL DEFAULT 0,
`weight` float unsigned NOT NULL DEFAULT 1,
`context` tinyint unsigned NOT NULL DEFAULT 2,
`language` char(7) NOT NULL DEFAULT '',
KEY `idx_word` (`term`),
KEY `idx_stem` (`stem`),
KEY `idx_context` (`context`),
KEY `idx_language` (`language`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `#__finder_tokens_aggregate`
--
CREATE TABLE IF NOT EXISTS `#__finder_tokens_aggregate` (
`term_id` int unsigned NOT NULL,
`term` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`stem` varchar(75) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`common` tinyint unsigned NOT NULL DEFAULT 0,
`phrase` tinyint unsigned NOT NULL DEFAULT 0,
`term_weight` float unsigned NOT NULL DEFAULT 0,
`context` tinyint unsigned NOT NULL DEFAULT 2,
`context_weight` float unsigned NOT NULL DEFAULT 0,
`total_weight` float unsigned NOT NULL DEFAULT 0,
`language` char(7) NOT NULL DEFAULT '',
KEY `token` (`term`),
KEY `keyword_id` (`term_id`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `#__finder_types`
--
CREATE TABLE IF NOT EXISTS `#__finder_types` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`mime` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;

View File

@ -0,0 +1,455 @@
--
-- Table structure for table `#__finder_filters`
--
CREATE TABLE IF NOT EXISTS "#__finder_filters" (
"filter_id" serial NOT NULL,
"title" varchar(255) NOT NULL,
"alias" varchar(255) NOT NULL,
"state" smallint DEFAULT 1 NOT NULL,
"created" timestamp without time zone NOT NULL,
"created_by" integer DEFAULT 0 NOT NULL,
"created_by_alias" varchar(255) DEFAULT '' NOT NULL,
"modified" timestamp without time zone NOT NULL,
"modified_by" integer DEFAULT 0 NOT NULL,
"checked_out" integer DEFAULT 0 NOT NULL,
"checked_out_time" timestamp without time zone,
"map_count" integer DEFAULT 0 NOT NULL,
"data" text,
"params" text,
PRIMARY KEY ("filter_id")
);
--
-- Table structure for table `#__finder_links`
--
CREATE TABLE IF NOT EXISTS "#__finder_links" (
"link_id" serial NOT NULL,
"url" varchar(255) NOT NULL,
"route" varchar(255) NOT NULL,
"title" varchar(400) DEFAULT NULL,
"description" text,
"indexdate" timestamp without time zone NOT NULL,
"md5sum" varchar(32) DEFAULT NULL,
"published" smallint DEFAULT 1 NOT NULL,
"state" integer DEFAULT 1 NOT NULL,
"access" integer DEFAULT 0 NOT NULL,
"language" varchar(7) DEFAULT '' NOT NULL,
"publish_start_date" timestamp without time zone,
"publish_end_date" timestamp without time zone,
"start_date" timestamp without time zone,
"end_date" timestamp without time zone,
"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,
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_language" on "#__finder_links" ("language");
CREATE INDEX "#__finder_links_idx_url" on "#__finder_links" (substr(url,0,76));
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 structure for table `#__finder_links_terms`
--
CREATE TABLE IF NOT EXISTS "#__finder_links_terms" (
"link_id" integer NOT NULL,
"term_id" integer NOT NULL,
"weight" numeric(8,2) DEFAULT 0 NOT NULL,
PRIMARY KEY ("link_id", "term_id")
);
CREATE INDEX "#__finder_links_terms_idx_term_weight" on "#__finder_links_terms" ("term_id", "weight");
CREATE INDEX "#__finder_links_terms_idx_link_term_weight" on "#__finder_links_terms" ("link_id", "term_id", "weight");
--
-- Table structure for table `#__finder_logging`
--
CREATE TABLE IF NOT EXISTS "#__finder_logging" (
"searchterm" character varying(255) NOT NULL DEFAULT '',
"md5sum" character varying(32) NOT NULL DEFAULT '',
"query" bytea NOT NULL,
"hits" integer NOT NULL DEFAULT 1,
"results" integer NOT NULL DEFAULT 0,
PRIMARY KEY ("md5sum")
);
CREATE INDEX "#__finder_logging_idx_md5sum" on "#__finder_logging" ("md5sum");
CREATE INDEX "#__finder_logging_idx_searchterm" on "#__finder_logging" ("searchterm");
--
-- Table structure for table `#__finder_taxonomy`
--
CREATE TABLE IF NOT EXISTS "#__finder_taxonomy" (
"id" serial NOT NULL,
"parent_id" integer DEFAULT 0 NOT NULL,
"lft" integer DEFAULT 0 NOT NULL,
"rgt" integer DEFAULT 0 NOT NULL,
"level" integer DEFAULT 0 NOT NULL,
"path" VARCHAR(400) NOT NULL DEFAULT '',
"title" VARCHAR(255) NOT NULL DEFAULT '',
"alias" VARCHAR(400) NOT NULL DEFAULT '',
"state" smallint DEFAULT 1 NOT NULL,
"access" smallint DEFAULT 1 NOT NULL,
"language" varchar(7) DEFAULT '' NOT NULL,
PRIMARY KEY ("id")
);
CREATE INDEX "#__finder_taxonomy_state" on "#__finder_taxonomy" ("state");
CREATE INDEX "#__finder_taxonomy_access" on "#__finder_taxonomy" ("access");
CREATE INDEX "#__finder_taxonomy_path" on "#__finder_taxonomy" ("path");
CREATE INDEX "#__finder_taxonomy_lft_rgt" on "#__finder_taxonomy" ("lft", "rgt");
CREATE INDEX "#__finder_taxonomy_alias" on "#__finder_taxonomy" ("alias");
CREATE INDEX "#__finder_taxonomy_language" on "#__finder_taxonomy" ("language");
CREATE INDEX "#__finder_taxonomy_idx_parent_published" on "#__finder_taxonomy" ("parent_id", "state", "access");
--
-- Dumping data for table `#__finder_taxonomy`
--
INSERT INTO "#__finder_taxonomy" ("id", "parent_id", "lft", "rgt", "level", "path", "title", "alias", "state", "access", "language") VALUES
(1, 0, 0, 1, 0, '', 'ROOT', 'root', 1, 1, '*');
SELECT setval('#__finder_taxonomy_id_seq', 2, false);
--
-- Table structure for table `#__finder_taxonomy_map`
--
CREATE TABLE IF NOT EXISTS "#__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 structure for table `#__finder_terms`
--
CREATE TABLE IF NOT EXISTS "#__finder_terms" (
"term_id" serial NOT NULL,
"term" varchar(75) NOT NULL,
"stem" varchar(75) DEFAULT '' NOT NULL,
"common" smallint DEFAULT 0 NOT NULL,
"phrase" smallint DEFAULT 0 NOT NULL,
"weight" numeric(8,2) DEFAULT 0 NOT NULL,
"soundex" varchar(75) DEFAULT '' NOT NULL,
"links" integer DEFAULT 0 NOT NULL,
"language" varchar(7) DEFAULT '' NOT NULL,
PRIMARY KEY ("term_id"),
CONSTRAINT "#__finder_terms_idx_term_language" UNIQUE ("term", "language")
);
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");
CREATE INDEX "#__finder_terms_idx_language" on "#__finder_terms" ("language");
--
-- Table structure for table `#__finder_terms_common`
--
CREATE TABLE IF NOT EXISTS "#__finder_terms_common" (
"term" varchar(75) NOT NULL,
"language" varchar(7) DEFAULT '' NOT NULL,
"custom" integer DEFAULT 0 NOT NULL,
CONSTRAINT "#__finder_terms_common_idx_term_language" UNIQUE ("term", "language")
);
CREATE INDEX "#__finder_terms_common_idx_lang" on "#__finder_terms_common" ("language");
--
-- Dumping data for table `#__finder_terms_common`
--
INSERT INTO "#__finder_terms_common" ("term", "language", "custom") VALUES
('i', 'en', 0),
('me', 'en', 0),
('my', 'en', 0),
('myself', 'en', 0),
('we', 'en', 0),
('our', 'en', 0),
('ours', 'en', 0),
('ourselves', 'en', 0),
('you', 'en', 0),
('your', 'en', 0),
('yours', 'en', 0),
('yourself', 'en', 0),
('yourselves', 'en', 0),
('he', 'en', 0),
('him', 'en', 0),
('his', 'en', 0),
('himself', 'en', 0),
('she', 'en', 0),
('her', 'en', 0),
('hers', 'en', 0),
('herself', 'en', 0),
('it', 'en', 0),
('its', 'en', 0),
('itself', 'en', 0),
('they', 'en', 0),
('them', 'en', 0),
('their', 'en', 0),
('theirs', 'en', 0),
('themselves', 'en', 0),
('what', 'en', 0),
('which', 'en', 0),
('who', 'en', 0),
('whom', 'en', 0),
('this', 'en', 0),
('that', 'en', 0),
('these', 'en', 0),
('those', 'en', 0),
('am', 'en', 0),
('is', 'en', 0),
('are', 'en', 0),
('was', 'en', 0),
('were', 'en', 0),
('be', 'en', 0),
('been', 'en', 0),
('being', 'en', 0),
('have', 'en', 0),
('has', 'en', 0),
('had', 'en', 0),
('having', 'en', 0),
('do', 'en', 0),
('does', 'en', 0),
('did', 'en', 0),
('doing', 'en', 0),
('would', 'en', 0),
('should', 'en', 0),
('could', 'en', 0),
('ought', 'en', 0),
('i''m', 'en', 0),
('you''re', 'en', 0),
('he''s', 'en', 0),
('she''s', 'en', 0),
('it''s', 'en', 0),
('we''re', 'en', 0),
('they''re', 'en', 0),
('i''ve', 'en', 0),
('you''ve', 'en', 0),
('we''ve', 'en', 0),
('they''ve', 'en', 0),
('i''d', 'en', 0),
('you''d', 'en', 0),
('he''d', 'en', 0),
('she''d', 'en', 0),
('we''d', 'en', 0),
('they''d', 'en', 0),
('i''ll', 'en', 0),
('you''ll', 'en', 0),
('he''ll', 'en', 0),
('she''ll', 'en', 0),
('we''ll', 'en', 0),
('they''ll', 'en', 0),
('isn''t', 'en', 0),
('aren''t', 'en', 0),
('wasn''t', 'en', 0),
('weren''t', 'en', 0),
('hasn''t', 'en', 0),
('haven''t', 'en', 0),
('hadn''t', 'en', 0),
('doesn''t', 'en', 0),
('don''t', 'en', 0),
('didn''t', 'en', 0),
('won''t', 'en', 0),
('wouldn''t', 'en', 0),
('shan''t', 'en', 0),
('shouldn''t', 'en', 0),
('can''t', 'en', 0),
('cannot', 'en', 0),
('couldn''t', 'en', 0),
('mustn''t', 'en', 0),
('let''s', 'en', 0),
('that''s', 'en', 0),
('who''s', 'en', 0),
('what''s', 'en', 0),
('here''s', 'en', 0),
('there''s', 'en', 0),
('when''s', 'en', 0),
('where''s', 'en', 0),
('why''s', 'en', 0),
('how''s', 'en', 0),
('a', 'en', 0),
('an', 'en', 0),
('the', 'en', 0),
('and', 'en', 0),
('but', 'en', 0),
('if', 'en', 0),
('or', 'en', 0),
('because', 'en', 0),
('as', 'en', 0),
('until', 'en', 0),
('while', 'en', 0),
('of', 'en', 0),
('at', 'en', 0),
('by', 'en', 0),
('for', 'en', 0),
('with', 'en', 0),
('about', 'en', 0),
('against', 'en', 0),
('between', 'en', 0),
('into', 'en', 0),
('through', 'en', 0),
('during', 'en', 0),
('before', 'en', 0),
('after', 'en', 0),
('above', 'en', 0),
('below', 'en', 0),
('to', 'en', 0),
('from', 'en', 0),
('up', 'en', 0),
('down', 'en', 0),
('in', 'en', 0),
('out', 'en', 0),
('on', 'en', 0),
('off', 'en', 0),
('over', 'en', 0),
('under', 'en', 0),
('again', 'en', 0),
('further', 'en', 0),
('then', 'en', 0),
('once', 'en', 0),
('here', 'en', 0),
('there', 'en', 0),
('when', 'en', 0),
('where', 'en', 0),
('why', 'en', 0),
('how', 'en', 0),
('all', 'en', 0),
('any', 'en', 0),
('both', 'en', 0),
('each', 'en', 0),
('few', 'en', 0),
('more', 'en', 0),
('most', 'en', 0),
('other', 'en', 0),
('some', 'en', 0),
('such', 'en', 0),
('no', 'en', 0),
('nor', 'en', 0),
('not', 'en', 0),
('only', 'en', 0),
('own', 'en', 0),
('same', 'en', 0),
('so', 'en', 0),
('than', 'en', 0),
('too', 'en', 0),
('very', 'en', 0);
--
-- Table structure for table `#__finder_tokens`
--
CREATE TABLE IF NOT EXISTS "#__finder_tokens" (
"term" varchar(75) NOT NULL,
"stem" varchar(75) DEFAULT '' 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,
"language" varchar(7) DEFAULT '' NOT NULL
);
CREATE INDEX "#__finder_tokens_idx_word" on "#__finder_tokens" ("term");
CREATE INDEX "#__finder_tokens_idx_stem" on "#__finder_tokens" ("stem");
CREATE INDEX "#__finder_tokens_idx_context" on "#__finder_tokens" ("context");
CREATE INDEX "#__finder_tokens_idx_language" on "#__finder_tokens" ("language");
--
-- Table structure for table `#__finder_tokens_aggregate`
--
CREATE TABLE IF NOT EXISTS "#__finder_tokens_aggregate" (
"term_id" integer NOT NULL,
"term" varchar(75) NOT NULL,
"stem" varchar(75) DEFAULT '' NOT NULL,
"common" smallint DEFAULT 0 NOT NULL,
"phrase" smallint DEFAULT 0 NOT NULL,
"term_weight" numeric(8,2) NOT NULL DEFAULT 0,
"context" smallint DEFAULT 2 NOT NULL,
"context_weight" numeric(8,2) NOT NULL DEFAULT 0,
"total_weight" numeric(8,2) NOT NULL DEFAULT 0,
"language" varchar(7) DEFAULT '' 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 structure for table `#__finder_types`
--
CREATE TABLE IF NOT EXISTS "#__finder_types" (
"id" serial NOT NULL,
"title" varchar(100) NOT NULL,
"mime" varchar(100) DEFAULT '' NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "#__finder_types_title" UNIQUE ("title")
);
--
-- Here is SOUNDEX replacement for those who can't enable fuzzystrmatch module
-- from contrib folder.
-- This function comes from https://wiki.postgresql.org/wiki/Soundex
-- and is distributed with GPL license.
-- Thanks to its author, Marti Raudsepp, that published this piece of code.
--
CREATE OR REPLACE FUNCTION soundex(input text) RETURNS text
IMMUTABLE STRICT COST 500 LANGUAGE plpgsql
AS $$
DECLARE
soundex text = '';
char text;
symbol text;
last_symbol text = '';
pos int = 1;
BEGIN
WHILE length(soundex) < 4 LOOP
char = upper(substr(input, pos, 1));
pos = pos + 1;
CASE char
WHEN '' THEN
-- End of input string
IF soundex = '' THEN
RETURN '';
ELSE
RETURN rpad(soundex, 4, '0');
END IF;
WHEN 'B', 'F', 'P', 'V' THEN
symbol = '1';
WHEN 'C', 'G', 'J', 'K', 'Q', 'S', 'X', 'Z' THEN
symbol = '2';
WHEN 'D', 'T' THEN
symbol = '3';
WHEN 'L' THEN
symbol = '4';
WHEN 'M', 'N' THEN
symbol = '5';
WHEN 'R' THEN
symbol = '6';
ELSE
-- Not a consonant; no output, but next similar consonant will be re-recorded
symbol = '';
END CASE;
IF soundex = '' THEN
-- First character; only accept strictly English ASCII characters
IF char ~>=~ 'A' AND char ~<=~ 'Z' THEN
soundex = char;
last_symbol = symbol;
END IF;
ELSIF last_symbol != symbol THEN
soundex = soundex || symbol;
last_symbol = symbol;
END IF;
END LOOP;
RETURN soundex;
END;
$$;

View File

@ -0,0 +1,11 @@
DROP TABLE IF EXISTS `#__finder_filters`;
DROP TABLE IF EXISTS `#__finder_links`;
DROP TABLE IF EXISTS `#__finder_links_terms`;
DROP TABLE IF EXISTS `#__finder_logging`;
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`;

View File

@ -0,0 +1,11 @@
DROP TABLE IF EXISTS "#__finder_filters";
DROP TABLE IF EXISTS "#__finder_links";
DROP TABLE IF EXISTS "#__finder_links_terms";
DROP TABLE IF EXISTS "#__finder_logging";
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";

View File

@ -0,0 +1,85 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Finder\Administrator\Helper\FinderHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Base controller class for Finder.
*
* @since 2.5
*/
class DisplayController extends BaseController
{
/**
* 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
* @see \Joomla\CMS\Filter\InputFilter::clean() for valid values.
*
* @return static|boolean A Controller object to support chaining or false on failure.
*
* @since 2.5
*/
public function display($cachable = false, $urlparams = [])
{
$view = $this->input->get('view', 'index', 'word');
$layout = $this->input->get('layout', 'index', 'word');
$filterId = $this->input->get('filter_id', null, 'int');
if ($view === 'index') {
$pluginEnabled = PluginHelper::isEnabled('content', 'finder');
if (!$pluginEnabled) {
$finderPluginId = FinderHelper::getFinderPluginId();
$link = HTMLHelper::_(
'link',
'#plugin' . $finderPluginId . 'Modal',
Text::_('COM_FINDER_CONTENT_PLUGIN'),
'class="alert-link" data-bs-toggle="modal" id="title-' . $finderPluginId . '"'
);
$this->app->enqueueMessage(Text::sprintf('COM_FINDER_INDEX_PLUGIN_CONTENT_NOT_ENABLED_LINK', $link), 'warning');
}
}
// 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.
if (!\count($this->app->getMessageQueue())) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $filterId), 'error');
}
$this->setRedirect(Route::_('index.php?option=com_finder&view=filters', false));
return false;
}
return parent::display();
}
}

View File

@ -0,0 +1,229 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\CMS\Router\Route;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Indexer controller class for Finder.
*
* @since 2.5
*/
class FilterController extends FormController
{
/**
* 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();
/** @var \Joomla\Component\Finder\Administrator\Model\FilterModel $model */
$model = $this->getModel();
$table = $model->getTable();
$data = $this->input->post->get('jform', [], 'array');
$checkin = $table->hasField('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 = $this->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->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $recordId), 'error');
$this->setRedirect(Route::_('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.
if (!\count($this->app->getMessageQueue())) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->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->setMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
$this->setRedirect(Route::_('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) {
$this->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) {
$this->app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$this->app->enqueueMessage($errors[$i], 'warning');
}
}
// Save the data in the session.
$this->app->setUserState($context . '.data', $data);
// Redirect back to the edit screen.
$this->setRedirect(
Route::_('index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $key), false)
);
return false;
}
// Get and sanitize the filter data.
$validData['data'] = $this->input->post->get('t', [], '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.
$this->app->setUserState($context . '.data', $validData);
// Redirect back to the edit screen.
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()), 'error');
$this->setRedirect(
Route::_('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.
$this->app->setUserState($context . '.data', $validData);
// Check-in failed, so go back to the record and display a notice.
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()), 'error');
$this->setRedirect('index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $key));
return false;
}
$this->setMessage(
Text::_(
($this->app->getLanguage()->hasKey($this->text_prefix . ($recordId === 0 && $this->app->isClient('site') ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
? $this->text_prefix : 'JLIB_APPLICATION') . ($recordId === 0 && $this->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);
$this->app->setUserState($context . '.data', null);
$model->checkout($recordId);
// Redirect back to the edit screen.
$this->setRedirect(
Route::_('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);
$this->app->setUserState($context . '.data', null);
// Redirect back to the edit screen.
$this->setRedirect(
Route::_('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);
$this->app->setUserState($context . '.data', null);
// Redirect to the list screen.
$this->setRedirect(
Route::_('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;
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\MVC\Controller\AdminController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filters controller class for Finder.
*
* @since 2.5
*/
class FiltersController extends AdminController
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 4.0.0
*/
protected $text_prefix = 'COM_FINDER_FILTERS';
/**
* 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 \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since 2.5
*/
public function getModel($name = 'Filter', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\Event\Finder\GarbageCollectionEvent;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Component\Finder\Administrator\Indexer\Indexer;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Index controller class for Finder.
*
* @since 2.5
*/
class IndexController extends AdminController
{
/**
* 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 \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since 2.5
*/
public function getModel($name = 'Index', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
/**
* Method to optimise the index by removing orphaned entries.
*
* @return boolean True on success.
*
* @since 4.2.0
*/
public function optimise()
{
$this->checkToken();
$dispatcher = $this->getDispatcher();
// Optimise the index by first running the garbage collection
PluginHelper::importPlugin('finder', null, true, $dispatcher);
$dispatcher->dispatch('onFinderGarbageCollection', new GarbageCollectionEvent('onFinderGarbageCollection', []));
// Now run the optimisation method from the indexer
$indexer = new Indexer();
$indexer->optimize();
$message = Text::_('COM_FINDER_INDEX_OPTIMISE_FINISHED');
$this->setRedirect('index.php?option=com_finder&view=index', $message);
return true;
}
/**
* 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.
if (\function_exists('set_time_limit')) {
set_time_limit(0);
}
/** @var \Joomla\Component\Finder\Administrator\Model\IndexModel $model */
$model = $this->getModel('Index', 'Administrator');
// Attempt to purge the index.
$return = $model->purge();
if (!$return) {
$message = Text::_('COM_FINDER_INDEX_PURGE_FAILED', $model->getError());
$this->setRedirect('index.php?option=com_finder&view=index', $message);
return false;
}
$message = Text::_('COM_FINDER_INDEX_PURGE_SUCCESS');
$this->setRedirect('index.php?option=com_finder&view=index', $message);
return true;
}
}

View File

@ -0,0 +1,421 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Event\Finder\BeforeIndexEvent;
use Joomla\CMS\Event\Finder\BuildIndexEvent;
use Joomla\CMS\Event\Finder\StartIndexEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Session\Session;
use Joomla\Component\Finder\Administrator\Indexer\Adapter;
use Joomla\Component\Finder\Administrator\Indexer\DebugAdapter;
use Joomla\Component\Finder\Administrator\Indexer\DebugIndexer;
use Joomla\Component\Finder\Administrator\Indexer\Indexer;
use Joomla\Component\Finder\Administrator\Response\Response;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Indexer controller class for Finder.
*
* @since 2.5
*/
class IndexerController extends BaseController
{
/**
* Method to start the indexer.
*
* @return void
*
* @since 2.5
*/
public function start()
{
// Check for a valid token. If invalid, send a 403 with the error message.
if (!Session::checkToken('request')) {
static::sendResponse(new \Exception(Text::_('JINVALID_TOKEN_NOTICE'), 403));
return;
}
$params = ComponentHelper::getParams('com_finder');
$dispatcher = $this->getDispatcher();
if ($params->get('enable_logging', '0')) {
$options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
$options['text_file'] = 'indexer.php';
Log::addLogger($options);
}
// Log the start
try {
Log::add('Starting the indexer', Log::INFO);
} catch (\RuntimeException $exception) {
// Informational log only
}
// We don't want this form to be cached.
$this->app->allowCache(false);
// Put in a buffer to silence noise.
ob_start();
// Reset the indexer state.
Indexer::resetState();
// Import the finder plugins.
PluginHelper::importPlugin('finder', null, true, $dispatcher);
// Add the indexer language to \JS
Text::script('COM_FINDER_AN_ERROR_HAS_OCCURRED');
Text::script('COM_FINDER_NO_ERROR_RETURNED');
// Start the indexer.
try {
// Trigger the onStartIndex event.
$dispatcher->dispatch('onStartIndex', new StartIndexEvent('onStartIndex', []));
// Get the indexer state.
$state = Indexer::getState();
$state->start = 1;
$output = ob_get_contents();
if ($output) {
throw new \Exception(Text::_('COM_FINDER_AN_ERROR_HAS_OCCURRED'));
}
// Send the response.
static::sendResponse($state);
} catch (\Exception $e) {
// Catch an exception and return the response.
static::sendResponse($e);
}
}
/**
* Method to run the next batch of content through the indexer.
*
* @return void
*
* @since 2.5
*/
public function batch()
{
// Check for a valid token. If invalid, send a 403 with the error message.
if (!Session::checkToken('request')) {
static::sendResponse(new \Exception(Text::_('JINVALID_TOKEN_NOTICE'), 403));
return;
}
$params = ComponentHelper::getParams('com_finder');
$dispatcher = $this->getDispatcher();
if ($params->get('enable_logging', '0')) {
$options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}';
$options['text_file'] = 'indexer.php';
Log::addLogger($options);
}
// Log the start
try {
Log::add('Starting the indexer batch process', Log::INFO);
} catch (\RuntimeException $exception) {
// Informational log only
}
// We don't want this form to be cached.
$this->app->allowCache(false);
// Put in a buffer to silence noise.
ob_start();
// Remove the script time limit.
if (\function_exists('set_time_limit')) {
set_time_limit(0);
}
// Get the indexer state.
$state = Indexer::getState();
// Reset the batch offset.
$state->batchOffset = 0;
// Update the indexer state.
Indexer::setState($state);
// Import the finder plugins.
PluginHelper::importPlugin('finder', null, true, $dispatcher);
/*
* 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.
*/
$lang = $this->app->getLanguage();
// Get the document properties.
$attributes = [
'charset' => 'utf-8',
'lineend' => 'unix',
'tab' => ' ',
'language' => $lang->getTag(),
'direction' => $lang->isRtl() ? 'rtl' : 'ltr',
];
// Start the indexer.
try {
// Trigger the onBeforeIndex event.
$dispatcher->dispatch('onBeforeIndex', new BeforeIndexEvent('onBeforeIndex', []));
// Trigger the onBuildIndex event.
$dispatcher->dispatch('onBuildIndex', new BuildIndexEvent('onBuildIndex', []));
// Get the indexer state.
$state = Indexer::getState();
$state->start = 0;
$state->complete = 0;
// Log batch completion and memory high-water mark.
try {
Log::add('Batch completed, peak memory usage: ' . number_format(memory_get_peak_usage(true)) . ' bytes', Log::INFO);
} catch (\RuntimeException $exception) {
// Informational log only
}
$output = ob_get_contents();
if ($output) {
throw new \Exception(Text::_('COM_FINDER_INDEXER_ERROR_PLUGIN_FAILURE'));
}
// Send the response.
static::sendResponse($state);
} catch (\Exception $e) {
// Catch an exception and return the response.
// Send the response.
static::sendResponse($e);
}
}
/**
* Method to optimize the index and perform any necessary cleanup.
*
* @return void
*
* @since 2.5
*/
public function optimize()
{
// Check for a valid token. If invalid, send a 403 with the error message.
if (!Session::checkToken('request')) {
static::sendResponse(new \Exception(Text::_('JINVALID_TOKEN_NOTICE'), 403));
return;
}
// We don't want this form to be cached.
$this->app->allowCache(false);
// Put in a buffer to silence noise.
ob_start();
// Import the finder plugins.
PluginHelper::importPlugin('finder', null, true, $this->getDispatcher());
try {
// Optimize the index
$indexer = new Indexer();
$indexer->optimize();
// Get the indexer state.
$state = Indexer::getState();
$state->start = 0;
$state->complete = 1;
$output = ob_get_contents();
if ($output) {
throw new \Exception(Text::_('COM_FINDER_AN_ERROR_HAS_OCCURRED'));
}
// Send the response.
static::sendResponse($state);
} catch (\Exception $e) {
// Catch an exception and return the response.
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 CMSObject for a good response.
*
* @param \Joomla\CMS\Object\CMSObject|\Exception $data CMSObject on success, \Exception on error. [optional]
*
* @return void
*
* @since 2.5
*/
public static function sendResponse($data = null)
{
$app = Factory::getApplication();
$params = ComponentHelper::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';
Log::addLogger($options);
}
// Send the assigned error code if we are catching an exception.
if ($data instanceof \Exception) {
try {
Log::add($data->getMessage(), Log::ERROR);
} catch (\RuntimeException $exception) {
// Informational log only
}
$app->setHeader('status', $data->getCode());
}
// Create the response object.
$response = new Response($data);
if (\JDEBUG) {
// Add the buffer and memory usage
$response->buffer = ob_get_contents();
$response->memory = memory_get_usage(true);
}
ob_clean();
// Send the JSON response.
echo json_encode($response);
}
/**
* Method to call a specific indexing plugin and return debug info
*
* @return void
*
* @since 5.0.0
* @internal
*/
public function debug()
{
// Check for a valid token. If invalid, send a 403 with the error message.
if (!Session::checkToken('request')) {
static::sendResponse(new \Exception(Text::_('JINVALID_TOKEN_NOTICE'), 403));
return;
}
// We don't want this form to be cached.
$this->app->allowCache(false);
// Put in a buffer to silence noise.
ob_start();
// Remove the script time limit.
@set_time_limit(0);
// Get the indexer state.
Indexer::resetState();
$state = Indexer::getState();
// Reset the batch offset.
$state->batchOffset = 0;
// Update the indexer state.
Indexer::setState($state);
// Start the indexer.
try {
// Import the finder plugins.
class_alias(DebugAdapter::class, Adapter::class);
$plugin = $this->app->bootPlugin($this->app->getInput()->get('plugin'), 'finder');
$plugin->setIndexer(new DebugIndexer());
$plugin->debug($this->app->getInput()->get('id'));
$output = '';
// Create list of attributes
$output .= '<fieldset><legend>' . Text::_('COM_FINDER_INDEXER_FIELDSET_ATTRIBUTES') . '</legend>';
$output .= '<dl class="row">';
foreach (DebugIndexer::$item as $key => $value) {
$output .= '<dt class="col-sm-2">' . $key . '</dt><dd class="col-sm-10">' . $value . '</dd>';
}
$output .= '</dl>';
$output .= '</fieldset>';
$output .= '<fieldset><legend>' . Text::_('COM_FINDER_INDEXER_FIELDSET_ELEMENTS') . '</legend>';
$output .= '<dl class="row">';
foreach (DebugIndexer::$item->getElements() as $key => $element) {
$output .= '<dt class="col-sm-2">' . $key . '</dt><dd class="col-sm-10">' . $element . '</dd>';
}
$output .= '</dl>';
$output .= '</fieldset>';
$output .= '<fieldset><legend>' . Text::_('COM_FINDER_INDEXER_FIELDSET_INSTRUCTIONS') . '</legend>';
$output .= '<dl class="row">';
$contexts = [
1 => 'Title context',
2 => 'Text context',
3 => 'Meta context',
4 => 'Path context',
5 => 'Misc context',
];
foreach (DebugIndexer::$item->getInstructions() as $key => $element) {
$output .= '<dt class="col-sm-2">' . $contexts[$key] . '</dt><dd class="col-sm-10">' . json_encode($element) . '</dd>';
}
$output .= '</dl>';
$output .= '</fieldset>';
$output .= '<fieldset><legend>' . Text::_('COM_FINDER_INDEXER_FIELDSET_TAXONOMIES') . '</legend>';
$output .= '<dl class="row">';
foreach (DebugIndexer::$item->getTaxonomy() as $key => $element) {
$output .= '<dt class="col-sm-2">' . $key . '</dt><dd class="col-sm-10">' . json_encode($element) . '</dd>';
}
$output .= '</dl>';
$output .= '</fieldset>';
// Get the indexer state.
$state = Indexer::getState();
$state->start = 0;
$state->complete = 0;
$state->rendered = $output;
echo json_encode($state);
} catch (\Exception $e) {
// Catch an exception and return the response.
// Send the response.
static::sendResponse($e);
}
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\MVC\Controller\AdminController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Maps controller class for Finder.
*
* @since 2.5
*/
class MapsController extends AdminController
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 4.0.0
*/
protected $text_prefix = 'COM_FINDER_MAPS';
/**
* 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 \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since 1.6
*/
public function getModel($name = 'Maps', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Session\Session;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of search terms.
*
* @since 4.0.0
*/
class SearchesController extends BaseController
{
/**
* Method to reset the search log table.
*
* @return void
*/
public function reset()
{
// Check for request forgeries.
Session::checkToken() or jexit(Text::_('JINVALID_TOKEN'));
$model = $this->getModel('Searches');
if (!$model->reset()) {
$this->app->enqueueMessage($model->getError(), 'error');
}
$this->setRedirect('index.php?option=com_finder&view=searches');
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Extension;
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\CMS\Component\Router\RouterServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use Joomla\Component\Finder\Administrator\Service\HTML\Filter;
use Joomla\Component\Finder\Administrator\Service\HTML\Finder;
use Joomla\Component\Finder\Administrator\Service\HTML\Query;
use Joomla\Database\DatabaseInterface;
use Psr\Container\ContainerInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component class for com_finder
*
* @since 4.0.0
*/
class FinderComponent extends MVCComponent implements BootableExtensionInterface, RouterServiceInterface
{
use RouterServiceTrait;
use HTMLRegistryAwareTrait;
/**
* Booting the extension. This is the function to set up the environment of the extension like
* registering new class loaders, etc.
*
* If required, some initial set up can be done from services of the container, eg.
* registering HTML services.
*
* @param ContainerInterface $container The container
*
* @return void
*
* @since 4.0.0
*/
public function boot(ContainerInterface $container)
{
$finder = new Finder();
$finder->setDatabase($container->get(DatabaseInterface::class));
$this->getRegistry()->register('finder', $finder);
$filter = new Filter();
$filter->setDatabase($container->get(DatabaseInterface::class));
$this->getRegistry()->register('filter', $filter);
$this->getRegistry()->register('query', new Query());
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\HTML\HTMLHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Search Branches field for the Finder package.
*
* @since 3.5
*/
class BranchesField extends ListField
{
/**
* 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()
{
Factory::getApplication()->bootComponent('com_finder');
return HTMLHelper::_('finder.mapslist');
}
}

View File

@ -0,0 +1,128 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\GroupedlistField;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Supports a select grouped list of finder content map.
*
* @since 3.6.0
*/
class ContentmapField extends GroupedlistField
{
/**
* 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 = [];
// Get the database object and a new query object.
$db = $this->getDatabase();
// Main query.
$query = $db->getQuery(true)
->select($db->quoteName('a.title', 'text'))
->select($db->quoteName('a.id', 'value'))
->select($db->quoteName('a.parent_id'))
->select($db->quoteName('a.level'))
->from($db->quoteName('#__finder_taxonomy', 'a'))
->where($db->quoteName('a.parent_id') . ' <> 0')
->order('a.title ASC');
$db->setQuery($query);
try {
$contentMap = $db->loadObjectList();
} catch (\RuntimeException $e) {
return [];
}
// Build the grouped list array.
if ($contentMap) {
$parents = [];
foreach ($contentMap as $item) {
if (!isset($parents[$item->parent_id])) {
$parents[$item->parent_id] = [];
}
$parents[$item->parent_id][] = $item;
}
foreach ($parents[1] as $branch) {
$text = Text::_(LanguageHelper::branchSingular($branch->text));
$groups[$text] = $this->prepareLevel($branch->value, $parents);
}
}
// Merge any additional groups in the XML definition.
$groups = array_merge(parent::getGroups(), $groups);
return $groups;
}
/**
* Indenting and translating options for the list
*
* @param int $parent Parent ID to process
* @param array $parents Array of arrays of items with parent IDs as keys
*
* @return array The indented list of entries for this branch
*
* @since 4.1.5
*/
private function prepareLevel($parent, $parents)
{
$lang = Factory::getLanguage();
$entries = [];
foreach ($parents[$parent] as $item) {
$levelPrefix = str_repeat('- ', $item->level - 1);
if (trim($item->text, '*') === 'Language') {
$text = LanguageHelper::branchLanguageTitle($item->text);
} else {
$key = LanguageHelper::branchSingular($item->text);
$text = $lang->hasKey($key) ? Text::_($key) : $item->text;
}
$entries[] = HTMLHelper::_('select.option', $item->value, $levelPrefix . $text);
if (isset($parents[$item->value])) {
$entries = array_merge($entries, $this->prepareLevel($item->value, $parents));
}
}
return $entries;
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Content Types Filter field for the Finder package.
*
* @since 3.6.0
*/
class ContenttypesField extends ListField
{
/**
* 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 = Factory::getLanguage();
$options = [];
$db = $this->getDatabase();
$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) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
}
// Translate.
foreach ($contentTypes as $contentType) {
$key = LanguageHelper::branchSingular($contentType->text);
$contentType->translatedText = $lang->hasKey($key) ? Text::_($key) : $contentType->text;
}
// Order by title.
$contentTypes = ArrayHelper::sortObjects($contentTypes, 'translatedText', 1, true, true);
// Convert the values to options.
foreach ($contentTypes as $contentType) {
$options[] = HTMLHelper::_('select.option', $contentType->value, $contentType->translatedText);
}
// Merge any additional options in the XML definition.
$options = array_merge(parent::getOptions(), $options);
return $options;
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Field;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Search Filter field for the Finder package.
*
* @since 2.5
*/
class SearchfilterField extends ListField
{
/**
* 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 = $this->getDatabase();
$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, HTMLHelper::_('select.option', '', Text::_('COM_FINDER_SELECT_SEARCH_FILTER'), 'value', 'text'));
return $options;
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Field;
use Joomla\CMS\Form\Field\CheckboxesField;
/**
* Taxonomy Types field for the Finder package.
* This is a helper to allow to save an empty set of
* options by having a hidden field with a "none" value.
*
* @since 5.0.0
*/
class TaxonomytypesField extends CheckboxesField
{
/**
* The form field type.
*
* @var string
* @since 5.0.0
*/
protected $type = 'TaxonomyTypes';
/**
* Method to get the field input markup for a generic list.
* Use the multiple attribute to enable multiselect.
*
* @return string The field input markup.
*
* @since 5.0.0
*/
protected function getInput()
{
$html = parent::getInput();
$data = $this->getLayoutData();
$data['id'] .= '_hidden';
$data['value'] = 'none';
return $html . $this->getRenderer('joomla.form.field.hidden')->render($data);
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Helper;
use Joomla\CMS\Extension\ExtensionHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Helper class for Finder.
*
* @since 2.5
*/
class FinderHelper
{
/**
* The extension name.
*
* @var string
* @since 2.5
*/
public static $extension = 'com_finder';
/**
* Gets the finder system plugin extension id.
*
* @return integer The finder system plugin extension id.
*
* @since 3.6.0
*/
public static function getFinderPluginId()
{
$pluginRecord = ExtensionHelper::getExtensionRecord('finder', 'plugin', null, 'content');
return $pluginRecord !== null ? $pluginRecord->extension_id : 0;
}
}

View File

@ -0,0 +1,150 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Helper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\LanguageHelper as CMSLanguageHelper;
use Joomla\CMS\Language\Text;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Finder language helper class.
*
* @since 2.5
*/
class LanguageHelper
{
/**
* 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));
$language = Factory::getApplication()->getLanguage();
if ($language->hasKey('PLG_FINDER_QUERY_FILTER_BRANCH_S_' . $return) || JDEBUG) {
return 'PLG_FINDER_QUERY_FILTER_BRANCH_S_' . $return;
}
return $branchName;
}
/**
* 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 = Text::_('JALL_LANGUAGE');
} else {
$languages = CMSLanguageHelper::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()
{
Factory::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 = Factory::getDbo();
$query = $db->getQuery(true)
->select([$db->quoteName('name'), $db->quoteName('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 = Factory::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);
}
}
}

View File

@ -0,0 +1,942 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\QueryInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Prototype adapter class for the Finder indexer package.
*
* @since 2.5
*/
abstract class Adapter extends CMSPlugin
{
/**
* 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 DatabaseInterface
* @since 2.5
*/
protected $db;
/**
* The table name.
*
* @var string
* @since 2.5
*/
protected $table;
/**
* The indexer object.
*
* @var Indexer
* @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 DispatcherInterface $dispatcher The object to observe.
* @param array $config An array that holds the plugin configuration.
*
* @since 2.5
*/
public function __construct(DispatcherInterface $dispatcher, array $config)
{
// Call the parent constructor.
parent::__construct($dispatcher, $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 = Helper::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 = new Indexer($this->db);
}
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return [
'onBeforeIndex' => 'onBeforeIndex',
'onBuildIndex' => 'onBuildIndex',
'onFinderGarbageCollection' => 'onFinderGarbageCollection',
'onStartIndex' => 'onStartIndex',
];
}
/**
* 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 = Indexer::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.
Indexer::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 = Indexer::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 = Indexer::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;
Indexer::setState($iState);
return true;
}
/**
* Method to remove outdated index entries
*
* @return integer
*
* @since 4.2.0
*/
public function onFinderGarbageCollection()
{
$db = $this->db;
$type_id = $this->getTypeId();
$query = $db->getQuery(true);
$subquery = $db->getQuery(true);
$subquery->select('CONCAT(' . $db->quote($this->getUrl('', $this->extension, $this->layout)) . ', id)')
->from($db->quoteName($this->table));
$query->select($db->quoteName('l.link_id'))
->from($db->quoteName('#__finder_links', 'l'))
->where($db->quoteName('l.type_id') . ' = ' . $type_id)
->where($db->quoteName('l.url') . ' LIKE ' . $db->quote($this->getUrl('%', $this->extension, $this->layout)))
->where($db->quoteName('l.url') . ' NOT IN (' . $subquery . ')');
$db->setQuery($query);
$items = $db->loadColumn();
foreach ($items as $item) {
$this->indexer->remove($item);
}
return \count($items);
}
/**
* 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 Result $item The item to index as a Result object.
*
* @return boolean True on success.
*
* @since 2.5
* @throws \Exception on database error.
*/
abstract protected function index(Result $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();
// Get the item.
$item = $this->getItem($id);
// Index the item.
$this->index($item);
Taxonomy::removeOrphanNodes();
}
/**
* Method to remove an item from the index.
*
* @param string $id The ID of the item to remove.
* @param bool $removeTaxonomies Remove empty taxonomies
*
* @return boolean True on success.
*
* @since 2.5
* @throws \Exception on database error.
*/
protected function remove($id, $removeTaxonomies = true)
{
// 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)) {
Factory::getApplication()->triggerEvent('onFinderIndexAfterDelete', [$id]);
return true;
}
// Remove the items.
foreach ($items as $item) {
$this->indexer->remove($item, $removeTaxonomies);
}
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 Table $row A Table 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);
}
}
/**
* 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);
}
}
}
/**
* Method to check the existing access level for categories
*
* @param Table $row A Table 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 Table $row A Table 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 QueryInterface) {
$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 Result A Result 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);
$item = $this->db->loadAssoc();
// Convert the item to a result object.
$item = ArrayHelper::toObject((array) $item, Result::class);
// 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 QueryInterface $query A QueryInterface object. [optional]
*
* @return Result[] An array of Result objects.
*
* @since 2.5
* @throws \Exception on database error.
*/
protected function getItems($offset, $limit, $query = null)
{
// Get the content items to index.
$this->db->setQuery($this->getListQuery($query)->setLimit($limit, $offset));
$items = $this->db->loadAssocList();
foreach ($items as &$item) {
$item = ArrayHelper::toObject($item, Result::class);
// 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;
}
return $items;
}
/**
* Method to get the SQL query used to retrieve the list of content items.
*
* @param mixed $query A QueryInterface object. [optional]
*
* @return QueryInterface A database object.
*
* @since 2.5
*/
protected function getListQuery($query = null)
{
// Check if we can use the supplied SQL query.
return $query instanceof QueryInterface ? $query : $this->db->getQuery(true);
}
/**
* Method to get the plugin type
*
* @param integer $id The plugin ID
*
* @return string|null 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('folder') . ' = ' . $this->db->quote('finder'))
->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 QueryInterface 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 QueryInterface 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 QueryInterface 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 = Factory::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 Table $row A Table 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);
}
}
/**
* 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);
}
// Stop processing plugins
break;
}
}
}
/**
* 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 items should always show up in search results
case 1:
return 1;
// Archived items should only show up when option is enabled
case 2:
if ($this->params->get('search_archived', 1) == 0) {
return 0;
}
return 1;
// All other states should return an unpublished state
default:
return 0;
}
}
}

View File

@ -0,0 +1,969 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Indexer;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\QueryInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Utilities\ArrayHelper;
/**
* Prototype debug adapter class for the Finder indexer package.
* THIS CLASS IS ONLY TO BE USED FOR DEBUGGING PURPOSES! DON'T
* USE IT FOR PRODUCTIVE USE!
*
* @since 5.0.0
* @internal
*/
abstract class DebugAdapter extends CMSPlugin
{
/**
* 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 5.0.0
*/
protected $context;
/**
* The extension name.
*
* @var string
* @since 5.0.0
*/
protected $extension;
/**
* The sublayout to use when rendering the results.
*
* @var string
* @since 5.0.0
*/
protected $layout;
/**
* The mime type of the content the adapter indexes.
*
* @var string
* @since 5.0.0
*/
protected $mime;
/**
* The access level of an item before save.
*
* @var integer
* @since 5.0.0
*/
protected $old_access;
/**
* The access level of a category before save.
*
* @var integer
* @since 5.0.0
*/
protected $old_cataccess;
/**
* The type of content the adapter indexes.
*
* @var string
* @since 5.0.0
*/
protected $type_title;
/**
* The type id of the content.
*
* @var integer
* @since 5.0.0
*/
protected $type_id;
/**
* The database object.
*
* @var DatabaseInterface
* @since 5.0.0
*/
protected $db;
/**
* The table name.
*
* @var string
* @since 5.0.0
*/
protected $table;
/**
* The indexer object.
*
* @var Indexer
* @since 5.0.0
*/
protected $indexer;
/**
* The field the published state is stored in.
*
* @var string
* @since 5.0.0
*/
protected $state_field = 'state';
/**
* Method to instantiate the indexer adapter.
*
* @param DispatcherInterface $dispatcher The object to observe.
* @param array $config An array that holds the plugin configuration.
*
* @since 5.0.0
*/
public function __construct(DispatcherInterface $dispatcher, array $config)
{
// Call the parent constructor.
parent::__construct($dispatcher, $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 = Helper::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 = new Indexer($this->db);
}
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return [
'onBeforeIndex' => 'onBeforeIndex',
'onBuildIndex' => 'onBuildIndex',
'onFinderGarbageCollection' => 'onFinderGarbageCollection',
'onStartIndex' => 'onStartIndex',
];
}
/**
* Method to get the adapter state and push it into the indexer.
*
* @return void
*
* @since 5.0.0
* @throws \Exception on error.
*/
public function onStartIndex()
{
// Get the indexer state.
$iState = Indexer::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.
Indexer::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 5.0.0
* @throws \Exception on error.
*/
public function onBeforeIndex()
{
// Get the indexer and adapter state.
$iState = Indexer::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 5.0.0
* @throws \Exception on error.
*/
public function onBuildIndex()
{
// Get the indexer and adapter state.
$iState = Indexer::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;
Indexer::setState($iState);
return true;
}
/**
* Method to remove outdated index entries
*
* @return integer
*
* @since 5.0.0
*/
public function onFinderGarbageCollection()
{
$db = $this->db;
$type_id = $this->getTypeId();
$query = $db->getQuery(true);
$subquery = $db->getQuery(true);
$subquery->select('CONCAT(' . $db->quote($this->getUrl('', $this->extension, $this->layout)) . ', id)')
->from($db->quoteName($this->table));
$query->select($db->quoteName('l.link_id'))
->from($db->quoteName('#__finder_links', 'l'))
->where($db->quoteName('l.type_id') . ' = ' . $type_id)
->where($db->quoteName('l.url') . ' LIKE ' . $db->quote($this->getUrl('%', $this->extension, $this->layout)))
->where($db->quoteName('l.url') . ' NOT IN (' . $subquery . ')');
$db->setQuery($query);
$items = $db->loadColumn();
foreach ($items as $item) {
$this->indexer->remove($item);
}
return \count($items);
}
/**
* 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 5.0.0
* @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 Result $item The item to index as a Result object.
*
* @return boolean True on success.
*
* @since 5.0.0
* @throws \Exception on database error.
*/
abstract protected function index(Result $item);
/**
* Method to reindex an item.
*
* @param integer $id The ID of the item to reindex.
*
* @return void
*
* @since 5.0.0
* @throws \Exception on database error.
*/
protected function reindex($id)
{
// Run the setup method.
$this->setup();
// Remove the old item.
$this->remove($id, false);
// Get the item.
$item = $this->getItem($id);
// Index the item.
$this->index($item);
Taxonomy::removeOrphanNodes();
}
/**
* Method to remove an item from the index.
*
* @param string $id The ID of the item to remove.
* @param bool $removeTaxonomies Remove empty taxonomies
*
* @return boolean True on success.
*
* @since 5.0.0
* @throws \Exception on database error.
*/
protected function remove($id, $removeTaxonomies = true)
{
// 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)) {
$this->getApplication()->triggerEvent('onFinderIndexAfterDelete', [$id]);
return true;
}
// Remove the items.
foreach ($items as $item) {
$this->indexer->remove($item, $removeTaxonomies);
}
return true;
}
/**
* Method to setup the adapter before indexing.
*
* @return boolean True on success, false on failure.
*
* @since 5.0.0
* @throws \Exception on database error.
*/
abstract protected function setup();
/**
* Method to update index data on category access level changes
*
* @param Table $row A Table object
*
* @return void
*
* @since 5.0.0
*/
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);
}
}
/**
* 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 5.0.0
*/
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);
}
}
}
/**
* Method to check the existing access level for categories
*
* @param Table $row A Table object
*
* @return void
*
* @since 5.0.0
*/
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 Table $row A Table object
*
* @return void
*
* @since 5.0.0
*/
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 5.0.0
* @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 QueryInterface) {
$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 Result A Result object.
*
* @since 5.0.0
* @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);
$item = $this->db->loadAssoc();
// Convert the item to a result object.
$item = ArrayHelper::toObject((array) $item, Result::class);
// 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 QueryInterface $query A QueryInterface object. [optional]
*
* @return Result[] An array of Result objects.
*
* @since 5.0.0
* @throws \Exception on database error.
*/
protected function getItems($offset, $limit, $query = null)
{
// Get the content items to index.
$this->db->setQuery($this->getListQuery($query)->setLimit($limit, $offset));
$items = $this->db->loadAssocList();
foreach ($items as &$item) {
$item = ArrayHelper::toObject($item, Result::class);
// 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;
}
return $items;
}
/**
* Method to get the SQL query used to retrieve the list of content items.
*
* @param mixed $query A QueryInterface object. [optional]
*
* @return QueryInterface A database object.
*
* @since 5.0.0
*/
protected function getListQuery($query = null)
{
// Check if we can use the supplied SQL query.
return $query instanceof QueryInterface ? $query : $this->db->getQuery(true);
}
/**
* Method to get the plugin type
*
* @param integer $id The plugin ID
*
* @return string The plugin type
*
* @since 5.0.0
*/
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 QueryInterface A database object.
*
* @since 5.0.0
*/
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 QueryInterface A database object.
*
* @since 5.0.0
*/
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 QueryInterface A database object.
*
* @since 5.0.0
*/
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 5.0.0
* @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 5.0.0
*/
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 5.0.0
* @throws \Exception on database error.
*/
protected function getItemMenuTitle($url)
{
$return = null;
// Set variables
$user = $this->getApplication()->getIdentity();
$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 Table $row A Table object
*
* @return void
*
* @since 5.0.0
*/
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 5.0.0
*/
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);
}
}
/**
* 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 5.0.0
*/
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 5.0.0
*/
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:
return 0;
}
}
/**
* Debug method to set the used indexer
*
* @param Indexer $indexer Indexer object
*
* @return void
*
* @since 5.0.0
*/
public function setIndexer(Indexer $indexer)
{
$this->indexer = $indexer;
}
/**
* Debug method to run a specific plugin to prepare a result object.
* The object is then stored in the indexer object to debug further.
*
* @param mixed $id ID to index
*
* @return void
*
* @since 5.0.0
*/
public function debug($id)
{
// Run the setup method.
$this->setup();
// Get the item.
$item = $this->getItem($id);
// Index the item.
$this->index($item);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Indexer;
/**
* Debugging indexer class for the Finder indexer package.
*
* @since 5.0.0
* @internal
*/
class DebugIndexer extends Indexer
{
/**
* The result object from the last call to self::index()
*
* @var Result
*
* @since 5.0.0
*/
public static $item;
/**
* Stub for index() in indexer class
*
* @param Result $item Result object to index
* @param string $format Format to index
*
* @return void
*
* @since 5.0.0
*/
public function index($item, $format = 'html')
{
self::$item = $item;
}
}

View File

@ -0,0 +1,484 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Event\Finder\PrepareContentEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
use Joomla\Registry\Registry;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Helper class for the Finder indexer package.
*
* @since 2.5
*/
class Helper
{
public const CUSTOMFIELDS_DONT_INDEX = 0;
public const CUSTOMFIELDS_ADD_TO_INDEX = 1;
public const CUSTOMFIELDS_ADD_TO_TAXONOMY = 2;
public const CUSTOMFIELDS_ADD_TO_BOTH = 3;
/**
* 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 Parser::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 Token[] An array of Token objects.
*
* @since 2.5
*/
public static function tokenize($input, $lang, $phrase = false)
{
static $cache = [], $tuplecount;
static $multilingual;
static $defaultLanguage;
if (!$tuplecount) {
$params = ComponentHelper::getParams('com_finder');
$tuplecount = $params->get('tuplecount', 1);
}
if (\is_null($multilingual)) {
$multilingual = Multilanguage::isEnabled();
$config = ComponentHelper::getParams('com_finder');
if ($config->get('language_default', '') == '') {
$defaultLang = '*';
} elseif ($config->get('language_default', '') == '-1') {
$defaultLang = self::getDefaultLanguage();
} else {
$defaultLang = $config->get('language_default');
}
/*
* The default language always has the language code '*'.
* In order to not overwrite the language code of the language
* object that we are using, we are cloning it here.
*/
$obj = Language::getInstance($defaultLang);
$defaultLanguage = clone $obj;
$defaultLanguage->language = '*';
}
if (!$multilingual || $lang == '*') {
$language = $defaultLanguage;
} else {
$language = Language::getInstance($lang);
}
if (!isset($cache[$lang])) {
$cache[$lang] = [];
}
$tokens = [];
$terms = $language->tokenise($input);
// @todo: array_filter removes any number 0's from the terms. Not sure this is entirely intended
$terms = array_filter($terms);
$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 Token($terms, $language->language, $language->spacer);
} else {
// Create tokens from the terms.
for ($i = 0, $n = \count($terms); $i < $n; $i++) {
if (isset($cache[$lang][$terms[$i]])) {
$tokens[] = $cache[$lang][$terms[$i]];
} else {
$token = new Token($terms[$i], $language->language);
$tokens[] = $token;
$cache[$lang][$terms[$i]] = $token;
}
}
// Create multi-word phrase tokens from the individual words.
if ($tuplecount > 1) {
for ($i = 0, $n = \count($tokens); $i < $n; $i++) {
$temp = [$tokens[$i]->term];
// Create tokens for 2 to $tuplecount length phrases
for ($j = 1; $j < $tuplecount; $j++) {
if ($i + $j >= $n || !isset($tokens[$i + $j])) {
break;
}
$temp[] = $tokens[$i + $j]->term;
$key = implode('::', $temp);
if (isset($cache[$lang][$key])) {
$tokens[] = $cache[$lang][$key];
} else {
$token = new Token($temp, $language->language, $language->spacer);
$token->derived = true;
$tokens[] = $token;
$cache[$lang][$key] = $token;
}
}
}
}
}
// Prevent the cache to fill up the memory
while (\count($cache[$lang]) > 1024) {
/**
* We want to cache the most common words/tokens. At the same time
* we don't want to cache too much. The most common words will also
* be early in the text, so we are dropping all terms/tokens which
* have been cached later.
*/
array_pop($cache[$lang]);
}
return $tokens;
}
/**
* Method to get the base word of a token.
*
* @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)
{
static $multilingual;
static $defaultStemmer;
if (\is_null($multilingual)) {
$multilingual = Multilanguage::isEnabled();
$config = ComponentHelper::getParams('com_finder');
if ($config->get('language_default', '') == '') {
$defaultStemmer = Language::getInstance('*');
} elseif ($config->get('language_default', '') == '-1') {
$defaultStemmer = Language::getInstance(self::getDefaultLanguage());
} else {
$defaultStemmer = Language::getInstance($config->get('language_default'));
}
}
if (!$multilingual || $lang == '*') {
$language = $defaultStemmer;
} else {
$language = Language::getInstance($lang);
}
return $language->stem($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 = Factory::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([$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 = [], $default, $multilingual;
if (\is_null($multilingual)) {
$multilingual = Multilanguage::isEnabled();
$config = ComponentHelper::getParams('com_finder');
if ($config->get('language_default', '') == '') {
$default = '*';
} elseif ($config->get('language_default', '') == '-1') {
$default = self::getPrimaryLanguage(self::getDefaultLanguage());
} else {
$default = self::getPrimaryLanguage($config->get('language_default'));
}
}
if (!$multilingual || $lang == '*') {
$lang = $default;
}
// Load the common tokens for the language if necessary.
if (!isset($data[$lang])) {
$data[$lang] = self::getCommonWords($lang);
}
// Check if the token is in the common array.
return \in_array($token, $data[$lang], 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 = Factory::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 = ComponentHelper::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(['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 extra data for a content before being indexed. This is how
* we add Comments, Tags, Labels, etc. that should be available to Finder.
*
* @param Result $item The item to index as a Result object.
*
* @return boolean True on success, false on failure.
*
* @since 2.5
* @throws \Exception on database error.
*/
public static function getContentExtras(Result $item)
{
$dispatcher = Factory::getApplication()->getDispatcher();
// Load the finder plugin group.
PluginHelper::importPlugin('finder', null, true, $dispatcher);
$dispatcher->dispatch('onPrepareFinderContent', new PrepareContentEvent('onPrepareFinderContent', [
'subject' => $item,
]));
return true;
}
/**
* Add custom fields for the item to the Result object
*
* @param Result $item Result object to add the custom fields to
* @param string $context Context of the item in the custom fields
*
* @return void
*
* @since 5.0.0
*/
public static function addCustomFields(Result $item, $context)
{
if (!ComponentHelper::getParams(strstr($context, '.', true))->get('custom_fields_enable', 1)) {
return;
}
$obj = new \stdClass();
$obj->id = $item->id;
$fields = FieldsHelper::getFields($context, $obj, true);
foreach ($fields as $field) {
$searchindex = $field->params->get('searchindex', 0);
// We want to add this field to the search index
if ($searchindex == self::CUSTOMFIELDS_ADD_TO_INDEX || $searchindex == self::CUSTOMFIELDS_ADD_TO_BOTH) {
$name = 'jsfield_' . $field->name;
$item->$name = $field->value;
$item->addInstruction(Indexer::META_CONTEXT, $name);
}
// We want to add this field as a taxonomy
if (
($searchindex == self::CUSTOMFIELDS_ADD_TO_TAXONOMY || $searchindex == self::CUSTOMFIELDS_ADD_TO_BOTH)
&& $field->value
) {
$item->addTaxonomy($field->title, $field->value, $field->state, $field->access, $field->language);
}
}
}
/**
* 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 ?Result $item The item which get prepared. [optional]
*
* @return string The processed content.
*
* @since 2.5
*/
public static function prepareContent($text, $params = null, Result $item = null)
{
static $loaded;
// Load the content plugins if necessary.
if (empty($loaded)) {
PluginHelper::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 = Table::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.
Factory::getApplication()->triggerEvent('onContentPrepare', ['com_finder.indexer', &$content, &$params, 0]);
return $content->text;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\String\StringHelper;
use Wamania\Snowball\NotFoundException;
use Wamania\Snowball\Stemmer\Stemmer;
use Wamania\Snowball\StemmerFactory;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Language support class for the Finder indexer package.
*
* @since 4.0.0
*/
class Language
{
/**
* Language support instances container.
*
* @var Language[]
* @since 4.0.0
*/
protected static $instances = [];
/**
* Language locale of the class
*
* @var string
* @since 4.0.0
*/
public $language;
/**
* Spacer to use between terms
*
* @var string
* @since 4.0.0
*/
public $spacer = ' ';
/**
* The stemmer object.
*
* @var Stemmer
* @since 4.0.0
*/
protected $stemmer = null;
/**
* Method to construct the language object.
*
* @since 4.0.0
*/
public function __construct($locale = null)
{
if ($locale !== null) {
$this->language = $locale;
}
// Use our generic language handler if no language is set
if ($this->language === null) {
$this->language = '*';
}
try {
foreach (StemmerFactory::LANGS as $classname => $isoCodes) {
if (\in_array($this->language, $isoCodes)) {
$this->stemmer = StemmerFactory::create($this->language);
break;
}
}
} catch (NotFoundException $e) {
// We don't have a stemmer for the language
}
}
/**
* Method to get a language support object.
*
* @param string $language The language of the support object.
*
* @return Language A Language instance.
*
* @since 4.0.0
*/
public static function getInstance($language)
{
if (isset(self::$instances[$language])) {
return self::$instances[$language];
}
$locale = '*';
if ($language !== '*') {
$locale = Helper::getPrimaryLanguage($language);
$class = '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Language\\' . ucfirst($locale);
if (class_exists($class)) {
self::$instances[$language] = new $class();
return self::$instances[$language];
}
}
self::$instances[$language] = new self($locale);
return self::$instances[$language];
}
/**
* Method to tokenise a text string.
*
* @param string $input The input to tokenise.
*
* @return array An array of term strings.
*
* @since 4.0.0
*/
public function tokenise($input)
{
$quotes = html_entity_decode('&#8216;&#8217;&#39;', ENT_QUOTES, 'UTF-8');
/*
* 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, 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);
return $terms;
}
/**
* Method to stem a token.
*
* @param string $token The token to stem.
*
* @return string The stemmed token.
*
* @since 4.0.0
*/
public function stem($token)
{
if ($this->stemmer !== null) {
return $this->stemmer->stem($token);
}
return $token;
}
}

View File

@ -0,0 +1,934 @@
<?php
/**
* @package Joomla.Administrator
* @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
*
* The Greek stemmer was adapted for Joomla! 4 by Nicholas K. Dionysopoulos <nicholas@akeebabackup.com>. This is
* derivative work, based on the Greek stemmer for Drupal, see
* https://github.com/magaras/greek_stemmer/blob/master/mod_stemmer.php
*/
namespace Joomla\Component\Finder\Administrator\Indexer\Language;
use Joomla\Component\Finder\Administrator\Indexer\Language;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Greek language support class for the Finder indexer package.
*
* @since 4.0.0
*/
class El extends Language
{
/**
* Language locale of the class
*
* @var string
* @since 4.0.0
*/
public $language = 'el';
/**
* Method to construct the language object.
*
* @since 4.0.0
*/
public function __construct($locale = null)
{
// Override parent constructor since we don't need to load an external stemmer
}
/**
* Method to tokenise a text string. It takes into account the odd punctuation commonly used in Greek text, mapping
* it to ASCII punctuation.
*
* Reference: http://www.teicrete.gr/users/kutrulis/Glosika/Stixi.htm
*
* @param string $input The input to tokenise.
*
* @return array An array of term strings.
*
* @since 4.0.0
*/
public function tokenise($input)
{
// Replace Greek calligraphic double quotes (various styles) to dumb double quotes
$input = str_replace(['“', '”', '„', '«' ,'»'], '"', $input);
// Replace Greek calligraphic single quotes (various styles) to dumb single quotes
$input = str_replace(['','',''], "'", $input);
// Replace the middle dot (ano teleia) with a comma, adequate for the purpose of stemming
$input = str_replace('·', ',', $input);
// Dot and dash (τελεία και παύλα), used to denote the end of a context at the end of a paragraph.
$input = str_replace('.', '.', $input);
// Ellipsis, two styles (separate dots or single glyph)
$input = str_replace(['...', '…'], '.', $input);
// Cross. Marks the death date of a person. Removed.
$input = str_replace('†', '', $input);
// Star. Reference, supposition word (in philology), birth date of a person.
$input = str_replace('*', '', $input);
// Paragraph. Indicates change of subject.
$input = str_replace('§', '.', $input);
// Plus/minus. Shows approximation. Not relevant for the stemmer, hence its conversion to a space.
$input = str_replace('±', ' ', $input);
return parent::tokenise($input);
}
/**
* Method to stem a token.
*
* @param string $token The token to stem.
*
* @return string The stemmed token.
*
* @since 4.0.0
*/
public function stem($token)
{
$token = $this->toUpperCase($token, $wCase);
// Stop-word removal
$stop_words = '/^(ΕΚΟ|ΑΒΑ|ΑΓΑ|ΑΓΗ|ΑΓΩ|ΑΔΗ|ΑΔΩ|ΑΕ|ΑΕΙ|ΑΘΩ|ΑΙ|ΑΙΚ|ΑΚΗ|ΑΚΟΜΑ|ΑΚΟΜΗ|ΑΚΡΙΒΩΣ|ΑΛΑ|ΑΛΗΘΕΙΑ|ΑΛΗΘΙΝΑ|ΑΛΛΑΧΟΥ|ΑΛΛΙΩΣ|ΑΛΛΙΩΤΙΚΑ|'
. 'ΑΛΛΟΙΩΣ|ΑΛΛΟΙΩΤΙΚΑ|ΑΛΛΟΤΕ|ΑΛΤ|ΑΛΩ|ΑΜΑ|ΑΜΕ|ΑΜΕΣΑ|ΑΜΕΣΩΣ|ΑΜΩ|ΑΝ|ΑΝΑ|ΑΝΑΜΕΣΑ|ΑΝΑΜΕΤΑΞΥ|ΑΝΕΥ|ΑΝΤΙ|ΑΝΤΙΠΕΡΑ|ΑΝΤΙΣ|ΑΝΩ|ΑΝΩΤΕΡΩ|ΑΞΑΦΝΑ|'
. 'ΑΠ|ΑΠΕΝΑΝΤΙ|ΑΠΟ|ΑΠΟΨΕ|ΑΠΩ|ΑΡΑ|ΑΡΑΓΕ|ΑΡΕ|ΑΡΚ|ΑΡΚΕΤΑ|ΑΡΛ|ΑΡΜ|ΑΡΤ|ΑΡΥ|ΑΡΩ|ΑΣ|ΑΣΑ|ΑΣΟ|ΑΤΑ|ΑΤΕ|ΑΤΗ|ΑΤΙ|ΑΤΜ|ΑΤΟ|ΑΥΡΙΟ|ΑΦΗ|ΑΦΟΤΟΥ|ΑΦΟΥ|'
. 'ΑΧ|ΑΧΕ|ΑΧΟ|ΑΨΑ|ΑΨΕ|ΑΨΗ|ΑΨΥ|ΑΩΕ|ΑΩΟ|ΒΑΝ|ΒΑΤ|ΒΑΧ|ΒΕΑ|ΒΕΒΑΙΟΤΑΤΑ|ΒΗΞ|ΒΙΑ|ΒΙΕ|ΒΙΗ|ΒΙΟ|ΒΟΗ|ΒΟΩ|ΒΡΕ|ΓΑ|ΓΑΒ|ΓΑΡ|ΓΕΝ|ΓΕΣ||ΓΗ|ΓΗΝ|ΓΙ|ΓΙΑ|'
. 'ΓΙΕ|ΓΙΝ|ΓΙΟ|ΓΚΙ|ΓΙΑΤΙ|ΓΚΥ|ΓΟΗ|ΓΟΟ|ΓΡΗΓΟΡΑ|ΓΡΙ|ΓΡΥ|ΓΥΗ|ΓΥΡΩ|ΔΑ|ΔΕ|ΔΕΗ|ΔΕΙ|ΔΕΝ|ΔΕΣ|ΔΗ|ΔΗΘΕΝ|ΔΗΛΑΔΗ|ΔΗΩ|ΔΙ|ΔΙΑ|ΔΙΑΡΚΩΣ|ΔΙΟΛΟΥ|ΔΙΣ|'
. 'ΔΙΧΩΣ|ΔΟΛ|ΔΟΝ|ΔΡΑ|ΔΡΥ|ΔΡΧ|ΔΥΕ|ΔΥΟ|ΔΩ|ΕΑΜ|ΕΑΝ|ΕΑΡ|ΕΘΗ|ΕΙ|ΕΙΔΕΜΗ|ΕΙΘΕ|ΕΙΜΑΙ|ΕΙΜΑΣΤΕ|ΕΙΝΑΙ|ΕΙΣ|ΕΙΣΑΙ|ΕΙΣΑΣΤΕ|ΕΙΣΤΕ|ΕΙΤΕ|ΕΙΧΑ|ΕΙΧΑΜΕ|'
. 'ΕΙΧΑΝ|ΕΙΧΑΤΕ|ΕΙΧΕ|ΕΙΧΕΣ|ΕΚ|ΕΚΕΙ|ΕΛΑ|ΕΛΙ|ΕΜΠ|ΕΝ|ΕΝΤΕΛΩΣ|ΕΝΤΟΣ|ΕΝΤΩΜΕΤΑΞΥ|ΕΝΩ|ΕΞ|ΕΞΑΦΝΑ|ΕΞΙ|ΕΞΙΣΟΥ|ΕΞΩ|ΕΟΚ|ΕΠΑΝΩ|ΕΠΕΙΔΗ|ΕΠΕΙΤΑ|ΕΠΗ|'
. 'ΕΠΙ|ΕΠΙΣΗΣ|ΕΠΟΜΕΝΩΣ|ΕΡΑ|ΕΣ|ΕΣΑΣ|ΕΣΕ|ΕΣΕΙΣ|ΕΣΕΝΑ|ΕΣΗ|ΕΣΤΩ|ΕΣΥ|ΕΣΩ|ΕΤΙ|ΕΤΣΙ|ΕΥ|ΕΥΑ|ΕΥΓΕ|ΕΥΘΥΣ|ΕΥΤΥΧΩΣ|ΕΦΕ|ΕΦΕΞΗΣ|ΕΦΤ|ΕΧΕ|ΕΧΕΙ|'
. 'ΕΧΕΙΣ|ΕΧΕΤΕ|ΕΧΘΕΣ|ΕΧΟΜΕ|ΕΧΟΥΜΕ|ΕΧΟΥΝ|ΕΧΤΕΣ|ΕΧΩ|ΕΩΣ|ΖΕΑ|ΖΕΗ|ΖΕΙ|ΖΕΝ|ΖΗΝ|ΖΩ|Η|ΗΔΗ|ΗΔΥ|ΗΘΗ|ΗΛΟ|ΗΜΙ|ΗΠΑ|ΗΣΑΣΤΕ|ΗΣΟΥΝ|ΗΤΑ|ΗΤΑΝ|ΗΤΑΝΕ|'
. 'ΗΤΟΙ|ΗΤΤΟΝ|ΗΩ|ΘΑ|ΘΥΕ|ΘΩΡ|Ι|ΙΑ|ΙΒΟ|ΙΔΗ|ΙΔΙΩΣ|ΙΕ|ΙΙ|ΙΙΙ|ΙΚΑ|ΙΛΟ|ΙΜΑ|ΙΝΑ|ΙΝΩ|ΙΞΕ|ΙΞΟ|ΙΟ|ΙΟΙ|ΙΣΑ|ΙΣΑΜΕ|ΙΣΕ|ΙΣΗ|ΙΣΙΑ|ΙΣΟ|ΙΣΩΣ|ΙΩΒ|ΙΩΝ|'
. 'ΙΩΣ|ΙΑΝ|ΚΑΘ|ΚΑΘΕ|ΚΑΘΕΤΙ|ΚΑΘΟΛΟΥ|ΚΑΘΩΣ|ΚΑΙ|ΚΑΝ|ΚΑΠΟΤΕ|ΚΑΠΟΥ|ΚΑΠΩΣ|ΚΑΤ|ΚΑΤΑ|ΚΑΤΙ|ΚΑΤΙΤΙ|ΚΑΤΟΠΙΝ|ΚΑΤΩ|ΚΑΩ|ΚΒΟ|ΚΕΑ|ΚΕΙ|ΚΕΝ|ΚΙ|ΚΙΜ|'
. 'ΚΙΟΛΑΣ|ΚΙΤ|ΚΙΧ|ΚΚΕ|ΚΛΙΣΕ|ΚΛΠ|ΚΟΚ|ΚΟΝΤΑ|ΚΟΧ|ΚΤΛ|ΚΥΡ|ΚΥΡΙΩΣ|ΚΩ|ΚΩΝ|ΛΑ|ΛΕΑ|ΛΕΝ|ΛΕΟ|ΛΙΑ|ΛΙΓΑΚΙ|ΛΙΓΟΥΛΑΚΙ|ΛΙΓΟ|ΛΙΓΩΤΕΡΟ|ΛΙΟ|ΛΙΡ|ΛΟΓΩ|'
. 'ΛΟΙΠΑ|ΛΟΙΠΟΝ|ΛΟΣ|ΛΣ|ΛΥΩ|ΜΑ|ΜΑΖΙ|ΜΑΚΑΡΙ|ΜΑΛΙΣΤΑ|ΜΑΛΛΟΝ|ΜΑΝ|ΜΑΞ|ΜΑΣ|ΜΑΤ|ΜΕ|ΜΕΘΑΥΡΙΟ|ΜΕΙ|ΜΕΙΟΝ|ΜΕΛ|ΜΕΛΕΙ|ΜΕΛΛΕΤΑΙ|ΜΕΜΙΑΣ|ΜΕΝ|ΜΕΣ|'
. 'ΜΕΣΑ|ΜΕΤ|ΜΕΤΑ|ΜΕΤΑΞΥ|ΜΕΧΡΙ|ΜΗ|ΜΗΔΕ|ΜΗΝ|ΜΗΠΩΣ|ΜΗΤΕ|ΜΙ|ΜΙΞ|ΜΙΣ|ΜΜΕ|ΜΝΑ|ΜΟΒ|ΜΟΛΙΣ|ΜΟΛΟΝΟΤΙ|ΜΟΝΑΧΑ|ΜΟΝΟΜΙΑΣ|ΜΙΑ|ΜΟΥ|ΜΠΑ|ΜΠΟΡΕΙ|'
. 'ΜΠΟΡΟΥΝ|ΜΠΡΑΒΟ|ΜΠΡΟΣ|ΜΠΩ|ΜΥ|ΜΥΑ|ΜΥΝ|ΝΑ|ΝΑΕ|ΝΑΙ|ΝΑΟ|ΝΔ|ΝΕΐ|ΝΕΑ|ΝΕΕ|ΝΕΟ|ΝΙ|ΝΙΑ|ΝΙΚ|ΝΙΛ|ΝΙΝ|ΝΙΟ|ΝΤΑ|ΝΤΕ|ΝΤΙ|ΝΤΟ|ΝΥΝ|ΝΩΕ|ΝΩΡΙΣ|ΞΑΝΑ|'
. 'ΞΑΦΝΙΚΑ|ΞΕΩ|ΞΙ|Ο|ΟΑ|ΟΑΠ|ΟΔΟ|ΟΕ|ΟΖΟ|ΟΗΕ|ΟΙ|ΟΙΑ|ΟΙΗ|ΟΚΑ|ΟΛΟΓΥΡΑ|ΟΛΟΝΕΝ|ΟΛΟΤΕΛΑ|ΟΛΩΣΔΙΟΛΟΥ|ΟΜΩΣ|ΟΝ|ΟΝΕ|ΟΝΟ|ΟΠΑ|ΟΠΕ|ΟΠΗ|ΟΠΟ|'
. 'ΟΠΟΙΑΔΗΠΟΤΕ|ΟΠΟΙΑΝΔΗΠΟΤΕ|ΟΠΟΙΑΣΔΗΠΟΤΕ|ΟΠΟΙΔΗΠΟΤΕ|ΟΠΟΙΕΣΔΗΠΟΤΕ|ΟΠΟΙΟΔΗΠΟΤΕ|ΟΠΟΙΟΝΔΗΠΟΤΕ|ΟΠΟΙΟΣΔΗΠΟΤΕ|ΟΠΟΙΟΥΔΗΠΟΤΕ|ΟΠΟΙΟΥΣΔΗΠΟΤΕ|'
. 'ΟΠΟΙΩΝΔΗΠΟΤΕ|ΟΠΟΤΕΔΗΠΟΤΕ|ΟΠΟΥ|ΟΠΟΥΔΗΠΟΤΕ|ΟΠΩΣ|ΟΡΑ|ΟΡΕ|ΟΡΗ|ΟΡΟ|ΟΡΦ|ΟΡΩ|ΟΣΑ|ΟΣΑΔΗΠΟΤΕ|ΟΣΕ|ΟΣΕΣΔΗΠΟΤΕ|ΟΣΗΔΗΠΟΤΕ|ΟΣΗΝΔΗΠΟΤΕ|'
. 'ΟΣΗΣΔΗΠΟΤΕ|ΟΣΟΔΗΠΟΤΕ|ΟΣΟΙΔΗΠΟΤΕ|ΟΣΟΝΔΗΠΟΤΕ|ΟΣΟΣΔΗΠΟΤΕ|ΟΣΟΥΔΗΠΟΤΕ|ΟΣΟΥΣΔΗΠΟΤΕ|ΟΣΩΝΔΗΠΟΤΕ|ΟΤΑΝ|ΟΤΕ|ΟΤΙ|ΟΤΙΔΗΠΟΤΕ|ΟΥ|ΟΥΔΕ|ΟΥΚ|ΟΥΣ|'
. 'ΟΥΤΕ|ΟΥΦ|ΟΧΙ|ΟΨΑ|ΟΨΕ|ΟΨΗ|ΟΨΙ|ΟΨΟ|ΠΑ|ΠΑΛΙ|ΠΑΝ|ΠΑΝΤΟΤΕ|ΠΑΝΤΟΥ|ΠΑΝΤΩΣ|ΠΑΠ|ΠΑΡ|ΠΑΡΑ|ΠΕΙ|ΠΕΡ|ΠΕΡΑ|ΠΕΡΙ|ΠΕΡΙΠΟΥ|ΠΕΡΣΙ|ΠΕΡΥΣΙ|ΠΕΣ|ΠΙ|'
. 'ΠΙΑ|ΠΙΘΑΝΟΝ|ΠΙΚ|ΠΙΟ|ΠΙΣΩ|ΠΙΤ|ΠΙΩ|ΠΛΑΙ|ΠΛΕΟΝ|ΠΛΗΝ|ΠΛΩ|ΠΜ|ΠΟΑ|ΠΟΕ|ΠΟΛ|ΠΟΛΥ|ΠΟΠ|ΠΟΤΕ|ΠΟΥ|ΠΟΥΘΕ|ΠΟΥΘΕΝΑ|ΠΡΕΠΕΙ|ΠΡΙ|ΠΡΙΝ|ΠΡΟ|'
. 'ΠΡΟΚΕΙΜΕΝΟΥ|ΠΡΟΚΕΙΤΑΙ|ΠΡΟΠΕΡΣΙ|ΠΡΟΣ|ΠΡΟΤΟΥ|ΠΡΟΧΘΕΣ|ΠΡΟΧΤΕΣ|ΠΡΩΤΥΤΕΡΑ|ΠΥΑ|ΠΥΞ|ΠΥΟ|ΠΥΡ|ΠΧ|ΠΩ|ΠΩΛ|ΠΩΣ|ΡΑ|ΡΑΙ|ΡΑΠ|ΡΑΣ|ΡΕ|ΡΕΑ|ΡΕΕ|ΡΕΙ|'
. 'ΡΗΣ|ΡΘΩ|ΡΙΟ|ΡΟ|ΡΟΐ|ΡΟΕ|ΡΟΖ|ΡΟΗ|ΡΟΘ|ΡΟΙ|ΡΟΚ|ΡΟΛ|ΡΟΝ|ΡΟΣ|ΡΟΥ|ΣΑΙ|ΣΑΝ|ΣΑΟ|ΣΑΣ|ΣΕ|ΣΕΙΣ|ΣΕΚ|ΣΕΞ|ΣΕΡ|ΣΕΤ|ΣΕΦ|ΣΗΜΕΡΑ|ΣΙ|ΣΙΑ|ΣΙΓΑ|ΣΙΚ|'
. 'ΣΙΧ|ΣΚΙ|ΣΟΙ|ΣΟΚ|ΣΟΛ|ΣΟΝ|ΣΟΣ|ΣΟΥ|ΣΡΙ|ΣΤΑ|ΣΤΗ|ΣΤΗΝ|ΣΤΗΣ|ΣΤΙΣ|ΣΤΟ|ΣΤΟΝ|ΣΤΟΥ|ΣΤΟΥΣ|ΣΤΩΝ|ΣΥ|ΣΥΓΧΡΟΝΩΣ|ΣΥΝ|ΣΥΝΑΜΑ|ΣΥΝΕΠΩΣ|ΣΥΝΗΘΩΣ|'
. 'ΣΧΕΔΟΝ|ΣΩΣΤΑ|ΤΑ|ΤΑΔΕ|ΤΑΚ|ΤΑΝ|ΤΑΟ|ΤΑΥ|ΤΑΧΑ|ΤΑΧΑΤΕ|ΤΕ|ΤΕΙ|ΤΕΛ|ΤΕΛΙΚΑ|ΤΕΛΙΚΩΣ|ΤΕΣ|ΤΕΤ|ΤΖΟ|ΤΗ|ΤΗΛ|ΤΗΝ|ΤΗΣ|ΤΙ|ΤΙΚ|ΤΙΜ|ΤΙΠΟΤΑ|ΤΙΠΟΤΕ|'
. 'ΤΙΣ|ΤΝΤ|ΤΟ|ΤΟΙ|ΤΟΚ|ΤΟΜ|ΤΟΝ|ΤΟΠ|ΤΟΣ|ΤΟΣ?Ν|ΤΟΣΑ|ΤΟΣΕΣ|ΤΟΣΗ|ΤΟΣΗΝ|ΤΟΣΗΣ|ΤΟΣΟ|ΤΟΣΟΙ|ΤΟΣΟΝ|ΤΟΣΟΣ|ΤΟΣΟΥ|ΤΟΣΟΥΣ|ΤΟΤΕ|ΤΟΥ|ΤΟΥΛΑΧΙΣΤΟ|'
. 'ΤΟΥΛΑΧΙΣΤΟΝ|ΤΟΥΣ|ΤΣ|ΤΣΑ|ΤΣΕ|ΤΥΧΟΝ|ΤΩ|ΤΩΝ|ΤΩΡΑ|ΥΑΣ|ΥΒΑ|ΥΒΟ|ΥΙΕ|ΥΙΟ|ΥΛΑ|ΥΛΗ|ΥΝΙ|ΥΠ|ΥΠΕΡ|ΥΠΟ|ΥΠΟΨΗ|ΥΠΟΨΙΝ|ΥΣΤΕΡΑ|ΥΦΗ|ΥΨΗ|ΦΑ|ΦΑΐ|ΦΑΕ|'
. 'ΦΑΝ|ΦΑΞ|ΦΑΣ|ΦΑΩ|ΦΕΖ|ΦΕΙ|ΦΕΤΟΣ|ΦΕΥ|ΦΙ|ΦΙΛ|ΦΙΣ|ΦΟΞ|ΦΠΑ|ΦΡΙ|ΧΑ|ΧΑΗ|ΧΑΛ|ΧΑΝ|ΧΑΦ|ΧΕ|ΧΕΙ|ΧΘΕΣ|ΧΙ|ΧΙΑ|ΧΙΛ|ΧΙΟ|ΧΛΜ|ΧΜ|ΧΟΗ|ΧΟΛ|ΧΡΩ|ΧΤΕΣ|'
. 'ΧΩΡΙΣ|ΧΩΡΙΣΤΑ|ΨΕΣ|ΨΗΛΑ|ΨΙ|ΨΙΤ|Ω|ΩΑ|ΩΑΣ|ΩΔΕ|ΩΕΣ|ΩΘΩ|ΩΜΑ|ΩΜΕ|ΩΝ|ΩΟ|ΩΟΝ|ΩΟΥ|ΩΣ|ΩΣΑΝ|ΩΣΗ|ΩΣΟΤΟΥ|ΩΣΠΟΥ|ΩΣΤΕ|ΩΣΤΟΣΟ|ΩΤΑ|ΩΧ|ΩΩΝ)$/';
if (preg_match($stop_words, $token)) {
return $this->toLowerCase($token, $wCase);
}
// Vowels
$v = '(Α|Ε|Η|Ι|Ο|Υ|Ω)';
// Vowels without Y
$v2 = '(Α|Ε|Η|Ι|Ο|Ω)';
$test1 = true;
// Step S1. 14 stems
$re = '/^(.+?)(ΙΖΑ|ΙΖΕΣ|ΙΖΕ|ΙΖΑΜΕ|ΙΖΑΤΕ|ΙΖΑΝ|ΙΖΑΝΕ|ΙΖΩ|ΙΖΕΙΣ|ΙΖΕΙ|ΙΖΟΥΜΕ|ΙΖΕΤΕ|ΙΖΟΥΝ|ΙΖΟΥΝΕ)$/';
$exceptS1 = '/^(ΑΝΑΜΠΑ|ΕΜΠΑ|ΕΠΑ|ΞΑΝΑΠΑ|ΠΑ|ΠΕΡΙΠΑ|ΑΘΡΟ|ΣΥΝΑΘΡΟ|ΔΑΝΕ)$/';
$exceptS2 = '/^(ΜΑΡΚ|ΚΟΡΝ|ΑΜΠΑΡ|ΑΡΡ|ΒΑΘΥΡΙ|ΒΑΡΚ|Β|ΒΟΛΒΟΡ|ΓΚΡ|ΓΛΥΚΟΡ|ΓΛΥΚΥΡ|ΙΜΠ|Λ|ΛΟΥ|ΜΑΡ|Μ|ΠΡ|ΜΠΡ|ΠΟΛΥΡ|Π|Ρ|ΠΙΠΕΡΟΡ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= 'I';
}
if (preg_match($exceptS2, $token)) {
$token .= 'IΖ';
}
return $this->toLowerCase($token, $wCase);
}
// Step S2. 7 stems
$re = '/^(.+?)(ΩΘΗΚΑ|ΩΘΗΚΕΣ|ΩΘΗΚΕ|ΩΘΗΚΑΜΕ|ΩΘΗΚΑΤΕ|ΩΘΗΚΑΝ|ΩΘΗΚΑΝΕ)$/';
$exceptS1 = '/^(ΑΛ|ΒΙ|ΕΝ|ΥΨ|ΛΙ|ΖΩ|Σ|Χ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= 'ΩΝ';
}
return $this->toLowerCase($token, $wCase);
}
// Step S3. 7 stems
$re = '/^(.+?)(ΙΣΑ|ΙΣΕΣ|ΙΣΕ|ΙΣΑΜΕ|ΙΣΑΤΕ|ΙΣΑΝ|ΙΣΑΝΕ)$/';
$exceptS1 = '/^(ΑΝΑΜΠΑ|ΑΘΡΟ|ΕΜΠΑ|ΕΣΕ|ΕΣΩΚΛΕ|ΕΠΑ|ΞΑΝΑΠΑ|ΕΠΕ|ΠΕΡΙΠΑ|ΑΘΡΟ|ΣΥΝΑΘΡΟ|ΔΑΝΕ|ΚΛΕ|ΧΑΡΤΟΠΑ|ΕΞΑΡΧΑ|ΜΕΤΕΠΕ|ΑΠΟΚΛΕ|ΑΠΕΚΛΕ|ΕΚΛΕ|ΠΕ|ΠΕΡΙΠΑ)$/';
$exceptS2 = '/^(ΑΝ|ΑΦ|ΓΕ|ΓΙΓΑΝΤΟΑΦ|ΓΚΕ|ΔΗΜΟΚΡΑΤ|ΚΟΜ|ΓΚ|Μ|Π|ΠΟΥΚΑΜ|ΟΛΟ|ΛΑΡ)$/';
if ($token == "ΙΣΑ") {
$token = "ΙΣ";
return $token;
}
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= 'Ι';
}
if (preg_match($exceptS2, $token)) {
$token .= 'ΙΣ';
}
return $this->toLowerCase($token, $wCase);
}
// Step S4. 7 stems
$re = '/^(.+?)(ΙΣΩ|ΙΣΕΙΣ|ΙΣΕΙ|ΙΣΟΥΜΕ|ΙΣΕΤΕ|ΙΣΟΥΝ|ΙΣΟΥΝΕ)$/';
$exceptS1 = '/^(ΑΝΑΜΠΑ|ΕΜΠΑ|ΕΣΕ|ΕΣΩΚΛΕ|ΕΠΑ|ΞΑΝΑΠΑ|ΕΠΕ|ΠΕΡΙΠΑ|ΑΘΡΟ|ΣΥΝΑΘΡΟ|ΔΑΝΕ|ΚΛΕ|ΧΑΡΤΟΠΑ|ΕΞΑΡΧΑ|ΜΕΤΕΠΕ|ΑΠΟΚΛΕ|ΑΠΕΚΛΕ|ΕΚΛΕ|ΠΕ|ΠΕΡΙΠΑ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= 'Ι';
}
return $this->toLowerCase($token, $wCase);
}
// Step S5. 11 stems
$re = '/^(.+?)(ΙΣΤΟΣ|ΙΣΤΟΥ|ΙΣΤΟ|ΙΣΤΕ|ΙΣΤΟΙ|ΙΣΤΩΝ|ΙΣΤΟΥΣ|ΙΣΤΗ|ΙΣΤΗΣ|ΙΣΤΑ|ΙΣΤΕΣ)$/';
$exceptS1 = '/^(Μ|Π|ΑΠ|ΑΡ|ΗΔ|ΚΤ|ΣΚ|ΣΧ|ΥΨ|ΦΑ|ΧΡ|ΧΤ|ΑΚΤ|ΑΟΡ|ΑΣΧ|ΑΤΑ|ΑΧΝ|ΑΧΤ|ΓΕΜ|ΓΥΡ|ΕΜΠ|ΕΥΠ|ΕΧΘ|ΗΦΑ|ΚΑΘ|ΚΑΚ|ΚΥΛ|ΛΥΓ|ΜΑΚ|ΜΕΓ|ΤΑΧ|ΦΙΛ|ΧΩΡ)$/';
$exceptS2 = '/^(ΔΑΝΕ|ΣΥΝΑΘΡΟ|ΚΛΕ|ΣΕ|ΕΣΩΚΛΕ|ΑΣΕ|ΠΛΕ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= 'ΙΣΤ';
}
if (preg_match($exceptS2, $token)) {
$token .= 'Ι';
}
return $this->toLowerCase($token, $wCase);
}
// Step S6. 6 stems
$re = '/^(.+?)(ΙΣΜΟ|ΙΣΜΟΙ|ΙΣΜΟΣ|ΙΣΜΟΥ|ΙΣΜΟΥΣ|ΙΣΜΩΝ)$/';
$exceptS1 = '/^(ΑΓΝΩΣΤΙΚ|ΑΤΟΜΙΚ|ΓΝΩΣΤΙΚ|ΕΘΝΙΚ|ΕΚΛΕΚΤΙΚ|ΣΚΕΠΤΙΚ|ΤΟΠΙΚ)$/';
$exceptS2 = '/^(ΣΕ|ΜΕΤΑΣΕ|ΜΙΚΡΟΣΕ|ΕΓΚΛΕ|ΑΠΟΚΛΕ)$/';
$exceptS3 = '/^(ΔΑΝΕ|ΑΝΤΙΔΑΝΕ)$/';
$exceptS4 = '/^(ΑΛΕΞΑΝΔΡΙΝ|ΒΥΖΑΝΤΙΝ|ΘΕΑΤΡΙΝ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token = str_replace('ΙΚ', "", $token);
}
if (preg_match($exceptS2, $token)) {
$token .= "ΙΣΜ";
}
if (preg_match($exceptS3, $token)) {
$token .= "Ι";
}
if (preg_match($exceptS4, $token)) {
$token = str_replace('ΙΝ', "", $token);
}
return $this->toLowerCase($token, $wCase);
}
// Step S7. 4 stems
$re = '/^(.+?)(ΑΡΑΚΙ|ΑΡΑΚΙΑ|ΟΥΔΑΚΙ|ΟΥΔΑΚΙΑ)$/';
$exceptS1 = '/^(Σ|Χ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= "AΡΑΚ";
}
return $this->toLowerCase($token, $wCase);
}
// Step S8. 8 stems
$re = '/^(.+?)(ΑΚΙ|ΑΚΙΑ|ΙΤΣΑ|ΙΤΣΑΣ|ΙΤΣΕΣ|ΙΤΣΩΝ|ΑΡΑΚΙ|ΑΡΑΚΙΑ)$/';
$exceptS1 = '/^(ΑΝΘΡ|ΒΑΜΒ|ΒΡ|ΚΑΙΜ|ΚΟΝ|ΚΟΡ|ΛΑΒΡ|ΛΟΥΛ|ΜΕΡ|ΜΟΥΣΤ|ΝΑΓΚΑΣ|ΠΛ|Ρ|ΡΥ|Σ|ΣΚ|ΣΟΚ|ΣΠΑΝ|ΤΖ|ΦΑΡΜ|Χ|'
. 'ΚΑΠΑΚ|ΑΛΙΣΦ|ΑΜΒΡ|ΑΝΘΡ|Κ|ΦΥΛ|ΚΑΤΡΑΠ|ΚΛΙΜ|ΜΑΛ|ΣΛΟΒ|Φ|ΣΦ|ΤΣΕΧΟΣΛΟΒ)$/';
$exceptS2 = '/^(Β|ΒΑΛ|ΓΙΑΝ|ΓΛ|Ζ|ΗΓΟΥΜΕΝ|ΚΑΡΔ|ΚΟΝ|ΜΑΚΡΥΝ|ΝΥΦ|ΠΑΤΕΡ|Π|ΣΚ|ΤΟΣ|ΤΡΙΠΟΛ)$/';
// For words like ΠΛΟΥΣΙΟΚΟΡΙΤΣΑ, ΠΑΛΙΟΚΟΡΙΤΣΑ etc
$exceptS3 = '/(ΚΟΡ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= "ΑΚ";
}
if (preg_match($exceptS2, $token)) {
$token .= "ΙΤΣ";
}
if (preg_match($exceptS3, $token)) {
$token .= "ΙΤΣ";
}
return $this->toLowerCase($token, $wCase);
}
// Step S9. 3 stems
$re = '/^(.+?)(ΙΔΙΟ|ΙΔΙΑ|ΙΔΙΩΝ)$/';
$exceptS1 = '/^(ΑΙΦΝ|ΙΡ|ΟΛΟ|ΨΑΛ)$/';
$exceptS2 = '/(Ε|ΠΑΙΧΝ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= "ΙΔ";
}
if (preg_match($exceptS2, $token)) {
$token .= "ΙΔ";
}
return $this->toLowerCase($token, $wCase);
}
// Step S10. 4 stems
$re = '/^(.+?)(ΙΣΚΟΣ|ΙΣΚΟΥ|ΙΣΚΟ|ΙΣΚΕ)$/';
$exceptS1 = '/^(Δ|ΙΒ|ΜΗΝ|Ρ|ΦΡΑΓΚ|ΛΥΚ|ΟΒΕΛ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
if (preg_match($exceptS1, $token)) {
$token .= "ΙΣΚ";
}
return $this->toLowerCase($token, $wCase);
}
// Step 1
// step1list is used in Step 1. 41 stems
$step1list = [];
$step1list["ΦΑΓΙΑ"] = "ΦΑ";
$step1list["ΦΑΓΙΟΥ"] = "ΦΑ";
$step1list["ΦΑΓΙΩΝ"] = "ΦΑ";
$step1list["ΣΚΑΓΙΑ"] = "ΣΚΑ";
$step1list["ΣΚΑΓΙΟΥ"] = "ΣΚΑ";
$step1list["ΣΚΑΓΙΩΝ"] = "ΣΚΑ";
$step1list["ΟΛΟΓΙΟΥ"] = "ΟΛΟ";
$step1list["ΟΛΟΓΙΑ"] = "ΟΛΟ";
$step1list["ΟΛΟΓΙΩΝ"] = "ΟΛΟ";
$step1list["ΣΟΓΙΟΥ"] = "ΣΟ";
$step1list["ΣΟΓΙΑ"] = "ΣΟ";
$step1list["ΣΟΓΙΩΝ"] = "ΣΟ";
$step1list["ΤΑΤΟΓΙΑ"] = "ΤΑΤΟ";
$step1list["ΤΑΤΟΓΙΟΥ"] = "ΤΑΤΟ";
$step1list["ΤΑΤΟΓΙΩΝ"] = "ΤΑΤΟ";
$step1list["ΚΡΕΑΣ"] = "ΚΡΕ";
$step1list["ΚΡΕΑΤΟΣ"] = "ΚΡΕ";
$step1list["ΚΡΕΑΤΑ"] = "ΚΡΕ";
$step1list["ΚΡΕΑΤΩΝ"] = "ΚΡΕ";
$step1list["ΠΕΡΑΣ"] = "ΠΕΡ";
$step1list["ΠΕΡΑΤΟΣ"] = "ΠΕΡ";
// Added by Spyros. Also at $re in step1
$step1list["ΠΕΡΑΤΗ"] = "ΠΕΡ";
$step1list["ΠΕΡΑΤΑ"] = "ΠΕΡ";
$step1list["ΠΕΡΑΤΩΝ"] = "ΠΕΡ";
$step1list["ΤΕΡΑΣ"] = "ΤΕΡ";
$step1list["ΤΕΡΑΤΟΣ"] = "ΤΕΡ";
$step1list["ΤΕΡΑΤΑ"] = "ΤΕΡ";
$step1list["ΤΕΡΑΤΩΝ"] = "ΤΕΡ";
$step1list["ΦΩΣ"] = "ΦΩ";
$step1list["ΦΩΤΟΣ"] = "ΦΩ";
$step1list["ΦΩΤΑ"] = "ΦΩ";
$step1list["ΦΩΤΩΝ"] = "ΦΩ";
$step1list["ΚΑΘΕΣΤΩΣ"] = "ΚΑΘΕΣΤ";
$step1list["ΚΑΘΕΣΤΩΤΟΣ"] = "ΚΑΘΕΣΤ";
$step1list["ΚΑΘΕΣΤΩΤΑ"] = "ΚΑΘΕΣΤ";
$step1list["ΚΑΘΕΣΤΩΤΩΝ"] = "ΚΑΘΕΣΤ";
$step1list["ΓΕΓΟΝΟΣ"] = "ΓΕΓΟΝ";
$step1list["ΓΕΓΟΝΟΤΟΣ"] = "ΓΕΓΟΝ";
$step1list["ΓΕΓΟΝΟΤΑ"] = "ΓΕΓΟΝ";
$step1list["ΓΕΓΟΝΟΤΩΝ"] = "ΓΕΓΟΝ";
$re = '/(.*)(ΦΑΓΙΑ|ΦΑΓΙΟΥ|ΦΑΓΙΩΝ|ΣΚΑΓΙΑ|ΣΚΑΓΙΟΥ|ΣΚΑΓΙΩΝ|ΟΛΟΓΙΟΥ|ΟΛΟΓΙΑ|ΟΛΟΓΙΩΝ|ΣΟΓΙΟΥ|ΣΟΓΙΑ|ΣΟΓΙΩΝ|ΤΑΤΟΓΙΑ|ΤΑΤΟΓΙΟΥ|ΤΑΤΟΓΙΩΝ|ΚΡΕΑΣ|ΚΡΕΑΤΟΣ|'
. 'ΚΡΕΑΤΑ|ΚΡΕΑΤΩΝ|ΠΕΡΑΣ|ΠΕΡΑΤΟΣ|ΠΕΡΑΤΗ|ΠΕΡΑΤΑ|ΠΕΡΑΤΩΝ|ΤΕΡΑΣ|ΤΕΡΑΤΟΣ|ΤΕΡΑΤΑ|ΤΕΡΑΤΩΝ|ΦΩΣ|ΦΩΤΟΣ|ΦΩΤΑ|ΦΩΤΩΝ|ΚΑΘΕΣΤΩΣ|ΚΑΘΕΣΤΩΤΟΣ|'
. 'ΚΑΘΕΣΤΩΤΑ|ΚΑΘΕΣΤΩΤΩΝ|ΓΕΓΟΝΟΣ|ΓΕΓΟΝΟΤΟΣ|ΓΕΓΟΝΟΤΑ|ΓΕΓΟΝΟΤΩΝ)$/';
if (preg_match($re, $token, $match)) {
$stem = $match[1];
$suffix = $match[2];
$token = $stem . (\array_key_exists($suffix, $step1list) ? $step1list[$suffix] : '');
$test1 = false;
}
// Step 2a. 2 stems
$re = '/^(.+?)(ΑΔΕΣ|ΑΔΩΝ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1];
$re = '/(ΟΚ|ΜΑΜ|ΜΑΝ|ΜΠΑΜΠ|ΠΑΤΕΡ|ΓΙΑΓΙ|ΝΤΑΝΤ|ΚΥΡ|ΘΕΙ|ΠΕΘΕΡ)$/';
if (!preg_match($re, $token)) {
$token .= "ΑΔ";
}
}
// Step 2b. 2 stems
$re = '/^(.+?)(ΕΔΕΣ|ΕΔΩΝ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$exept2 = '/(ΟΠ|ΙΠ|ΕΜΠ|ΥΠ|ΓΗΠ|ΔΑΠ|ΚΡΑΣΠ|ΜΙΛ)$/';
if (preg_match($exept2, $token)) {
$token .= 'ΕΔ';
}
}
// Step 2c
$re = '/^(.+?)(ΟΥΔΕΣ|ΟΥΔΩΝ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$exept3 = '/(ΑΡΚ|ΚΑΛΙΑΚ|ΠΕΤΑΛ|ΛΙΧ|ΠΛΕΞ|ΣΚ|Σ|ΦΛ|ΦΡ|ΒΕΛ|ΛΟΥΛ|ΧΝ|ΣΠ|ΤΡΑΓ|ΦΕ)$/';
if (preg_match($exept3, $token)) {
$token .= 'ΟΥΔ';
}
}
// Step 2d
$re = '/^(.+?)(ΕΩΣ|ΕΩΝ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept4 = '/^(Θ|Δ|ΕΛ|ΓΑΛ|Ν|Π|ΙΔ|ΠΑΡ)$/';
if (preg_match($exept4, $token)) {
$token .= 'Ε';
}
}
// Step 3
$re = '/^(.+?)(ΙΑ|ΙΟΥ|ΙΩΝ)$/';
if (preg_match($re, $token, $fp)) {
$stem = $fp[1];
$token = $stem;
$re = '/' . $v . '$/';
$test1 = false;
if (preg_match($re, $token)) {
$token = $stem . 'Ι';
}
}
// Step 4
$re = '/^(.+?)(ΙΚΑ|ΙΚΟ|ΙΚΟΥ|ΙΚΩΝ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$re = '/' . $v . '$/';
$exept5 = '/^(ΑΛ|ΑΔ|ΕΝΔ|ΑΜΑΝ|ΑΜΜΟΧΑΛ|ΗΘ|ΑΝΗΘ|ΑΝΤΙΔ|ΦΥΣ|ΒΡΩΜ|ΓΕΡ|ΕΞΩΔ|ΚΑΛΠ|ΚΑΛΛΙΝ|ΚΑΤΑΔ|ΜΟΥΛ|ΜΠΑΝ|ΜΠΑΓΙΑΤ|ΜΠΟΛ|ΜΠΟΣ|ΝΙΤ|ΞΙΚ|ΣΥΝΟΜΗΛ|ΠΕΤΣ|'
. 'ΠΙΤΣ|ΠΙΚΑΝΤ|ΠΛΙΑΤΣ|ΠΟΣΤΕΛΝ|ΠΡΩΤΟΔ|ΣΕΡΤ|ΣΥΝΑΔ|ΤΣΑΜ|ΥΠΟΔ|ΦΙΛΟΝ|ΦΥΛΟΔ|ΧΑΣ)$/';
if (preg_match($re, $token) || preg_match($exept5, $token)) {
$token .= 'ΙΚ';
}
}
// Step 5a
$re = '/^(.+?)(ΑΜΕ)$/';
$re2 = '/^(.+?)(ΑΓΑΜΕ|ΗΣΑΜΕ|ΟΥΣΑΜΕ|ΗΚΑΜΕ|ΗΘΗΚΑΜΕ)$/';
if ($token == "ΑΓΑΜΕ") {
$token = "ΑΓΑΜ";
}
if (preg_match($re2, $token)) {
preg_match($re2, $token, $match);
$token = $match[1];
$test1 = false;
}
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept6 = '/^(ΑΝΑΠ|ΑΠΟΘ|ΑΠΟΚ|ΑΠΟΣΤ|ΒΟΥΒ|ΞΕΘ|ΟΥΛ|ΠΕΘ|ΠΙΚΡ|ΠΟΤ|ΣΙΧ|Χ)$/';
if (preg_match($exept6, $token)) {
$token .= "ΑΜ";
}
}
// Step 5b
$re2 = '/^(.+?)(ΑΝΕ)$/';
$re3 = '/^(.+?)(ΑΓΑΝΕ|ΗΣΑΝΕ|ΟΥΣΑΝΕ|ΙΟΝΤΑΝΕ|ΙΟΤΑΝΕ|ΙΟΥΝΤΑΝΕ|ΟΝΤΑΝΕ|ΟΤΑΝΕ|ΟΥΝΤΑΝΕ|ΗΚΑΝΕ|ΗΘΗΚΑΝΕ)$/';
if (preg_match($re3, $token)) {
preg_match($re3, $token, $match);
$token = $match[1];
$test1 = false;
$re3 = '/^(ΤΡ|ΤΣ)$/';
if (preg_match($re3, $token)) {
$token .= "ΑΓΑΝ";
}
}
if (preg_match($re2, $token)) {
preg_match($re2, $token, $match);
$token = $match[1];
$test1 = false;
$re2 = '/' . $v2 . '$/';
$exept7 = '/^(ΒΕΤΕΡ|ΒΟΥΛΚ|ΒΡΑΧΜ|Γ|ΔΡΑΔΟΥΜ|Θ|ΚΑΛΠΟΥΖ|ΚΑΣΤΕΛ|ΚΟΡΜΟΡ|ΛΑΟΠΛ|ΜΩΑΜΕΘ|Μ|ΜΟΥΣΟΥΛΜ|Ν|ΟΥΛ|Π|ΠΕΛΕΚ|ΠΛ|ΠΟΛΙΣ|ΠΟΡΤΟΛ|ΣΑΡΑΚΑΤΣ|ΣΟΥΛΤ|'
. 'ΤΣΑΡΛΑΤ|ΟΡΦ|ΤΣΙΓΓ|ΤΣΟΠ|ΦΩΤΟΣΤΕΦ|Χ|ΨΥΧΟΠΛ|ΑΓ|ΟΡΦ|ΓΑΛ|ΓΕΡ|ΔΕΚ|ΔΙΠΛ|ΑΜΕΡΙΚΑΝ|ΟΥΡ|ΠΙΘ|ΠΟΥΡΙΤ|Σ|ΖΩΝΤ|ΙΚ|ΚΑΣΤ|ΚΟΠ|ΛΙΧ|ΛΟΥΘΗΡ|ΜΑΙΝΤ|'
. 'ΜΕΛ|ΣΙΓ|ΣΠ|ΣΤΕΓ|ΤΡΑΓ|ΤΣΑΓ|Φ|ΕΡ|ΑΔΑΠ|ΑΘΙΓΓ|ΑΜΗΧ|ΑΝΙΚ|ΑΝΟΡΓ|ΑΠΗΓ|ΑΠΙΘ|ΑΤΣΙΓΓ|ΒΑΣ|ΒΑΣΚ|ΒΑΘΥΓΑΛ|ΒΙΟΜΗΧ|ΒΡΑΧΥΚ|ΔΙΑΤ|ΔΙΑΦ|ΕΝΟΡΓ|'
. 'ΘΥΣ|ΚΑΠΝΟΒΙΟΜΗΧ|ΚΑΤΑΓΑΛ|ΚΛΙΒ|ΚΟΙΛΑΡΦ|ΛΙΒ|ΜΕΓΛΟΒΙΟΜΗΧ|ΜΙΚΡΟΒΙΟΜΗΧ|ΝΤΑΒ|ΞΗΡΟΚΛΙΒ|ΟΛΙΓΟΔΑΜ|ΟΛΟΓΑΛ|ΠΕΝΤΑΡΦ|ΠΕΡΗΦ|ΠΕΡΙΤΡ|ΠΛΑΤ|'
. 'ΠΟΛΥΔΑΠ|ΠΟΛΥΜΗΧ|ΣΤΕΦ|ΤΑΒ|ΤΕΤ|ΥΠΕΡΗΦ|ΥΠΟΚΟΠ|ΧΑΜΗΛΟΔΑΠ|ΨΗΛΟΤΑΒ)$/';
if (preg_match($re2, $token) || preg_match($exept7, $token)) {
$token .= "ΑΝ";
}
}
// Step 5c
$re3 = '/^(.+?)(ΕΤΕ)$/';
$re4 = '/^(.+?)(ΗΣΕΤΕ)$/';
if (preg_match($re4, $token)) {
preg_match($re4, $token, $match);
$token = $match[1];
$test1 = false;
}
if (preg_match($re3, $token)) {
preg_match($re3, $token, $match);
$token = $match[1];
$test1 = false;
$re3 = '/' . $v2 . '$/';
$exept8 = '/(ΟΔ|ΑΙΡ|ΦΟΡ|ΤΑΘ|ΔΙΑΘ|ΣΧ|ΕΝΔ|ΕΥΡ|ΤΙΘ|ΥΠΕΡΘ|ΡΑΘ|ΕΝΘ|ΡΟΘ|ΣΘ|ΠΥΡ|ΑΙΝ|ΣΥΝΔ|ΣΥΝ|ΣΥΝΘ|ΧΩΡ|ΠΟΝ|ΒΡ|ΚΑΘ|ΕΥΘ|ΕΚΘ|ΝΕΤ|ΡΟΝ|ΑΡΚ|ΒΑΡ|ΒΟΛ|ΩΦΕΛ)$/';
$exept9 = '/^(ΑΒΑΡ|ΒΕΝ|ΕΝΑΡ|ΑΒΡ|ΑΔ|ΑΘ|ΑΝ|ΑΠΛ|ΒΑΡΟΝ|ΝΤΡ|ΣΚ|ΚΟΠ|ΜΠΟΡ|ΝΙΦ|ΠΑΓ|ΠΑΡΑΚΑΛ|ΣΕΡΠ|ΣΚΕΛ|ΣΥΡΦ|ΤΟΚ|Υ|Δ|ΕΜ|ΘΑΡΡ|Θ)$/';
if (preg_match($re3, $token) || preg_match($exept8, $token) || preg_match($exept9, $token)) {
$token .= "ΕΤ";
}
}
// Step 5d
$re = '/^(.+?)(ΟΝΤΑΣ|ΩΝΤΑΣ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept10 = '/^(ΑΡΧ)$/';
$exept11 = '/(ΚΡΕ)$/';
if (preg_match($exept10, $token)) {
$token .= "ΟΝΤ";
}
if (preg_match($exept11, $token)) {
$token .= "ΩΝΤ";
}
}
// Step 5e
$re = '/^(.+?)(ΟΜΑΣΤΕ|ΙΟΜΑΣΤΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept11 = '/^(ΟΝ)$/';
if (preg_match($exept11, $token)) {
$token .= "ΟΜΑΣΤ";
}
}
// Step 5f
$re = '/^(.+?)(ΕΣΤΕ)$/';
$re2 = '/^(.+?)(ΙΕΣΤΕ)$/';
if (preg_match($re2, $token)) {
preg_match($re2, $token, $match);
$token = $match[1];
$test1 = false;
$re2 = '/^(Π|ΑΠ|ΣΥΜΠ|ΑΣΥΜΠ|ΑΚΑΤΑΠ|ΑΜΕΤΑΜΦ)$/';
if (preg_match($re2, $token)) {
$token .= "ΙΕΣΤ";
}
}
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept12 = '/^(ΑΛ|ΑΡ|ΕΚΤΕΛ|Ζ|Μ|Ξ|ΠΑΡΑΚΑΛ|ΠΡΟ|ΝΙΣ)$/';
if (preg_match($exept12, $token)) {
$token .= "ΕΣΤ";
}
}
// Step 5g
$re = '/^(.+?)(ΗΚΑ|ΗΚΕΣ|ΗΚΕ)$/';
$re2 = '/^(.+?)(ΗΘΗΚΑ|ΗΘΗΚΕΣ|ΗΘΗΚΕ)$/';
if (preg_match($re2, $token)) {
preg_match($re2, $token, $match);
$token = $match[1];
$test1 = false;
}
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept13 = '/(ΣΚΩΛ|ΣΚΟΥΛ|ΝΑΡΘ|ΣΦ|ΟΘ|ΠΙΘ)$/';
$exept14 = '/^(ΔΙΑΘ|Θ|ΠΑΡΑΚΑΤΑΘ|ΠΡΟΣΘ|ΣΥΝΘ|)$/';
if (preg_match($exept13, $token) || preg_match($exept14, $token)) {
$token .= "ΗΚ";
}
}
// Step 5h
$re = '/^(.+?)(ΟΥΣΑ|ΟΥΣΕΣ|ΟΥΣΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept15 = '/^(ΦΑΡΜΑΚ|ΧΑΔ|ΑΓΚ|ΑΝΑΡΡ|ΒΡΟΜ|ΕΚΛΙΠ|ΛΑΜΠΙΔ|ΛΕΧ|Μ|ΠΑΤ|Ρ|Λ|ΜΕΔ|ΜΕΣΑΖ|ΥΠΟΤΕΙΝ|ΑΜ|ΑΙΘ|ΑΝΗΚ|ΔΕΣΠΟΖ|ΕΝΔΙΑΦΕΡ|ΔΕ|ΔΕΥΤΕΡΕΥ|ΚΑΘΑΡΕΥ|ΠΛΕ|ΤΣΑ)$/';
$exept16 = '/(ΠΟΔΑΡ|ΒΛΕΠ|ΠΑΝΤΑΧ|ΦΡΥΔ|ΜΑΝΤΙΛ|ΜΑΛΛ|ΚΥΜΑΤ|ΛΑΧ|ΛΗΓ|ΦΑΓ|ΟΜ|ΠΡΩΤ)$/';
if (preg_match($exept15, $token) || preg_match($exept16, $token)) {
$token .= "ΟΥΣ";
}
}
// Step 5i
$re = '/^(.+?)(ΑΓΑ|ΑΓΕΣ|ΑΓΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept17 = '/^(ΨΟΦ|ΝΑΥΛΟΧ)$/';
$exept20 = '/(ΚΟΛΛ)$/';
$exept18 = '/^(ΑΒΑΣΤ|ΠΟΛΥΦ|ΑΔΗΦ|ΠΑΜΦ|Ρ|ΑΣΠ|ΑΦ|ΑΜΑΛ|ΑΜΑΛΛΙ|ΑΝΥΣΤ|ΑΠΕΡ|ΑΣΠΑΡ|ΑΧΑΡ|ΔΕΡΒΕΝ|ΔΡΟΣΟΠ|ΞΕΦ|ΝΕΟΠ|ΝΟΜΟΤ|ΟΛΟΠ|ΟΜΟΤ|ΠΡΟΣΤ|ΠΡΟΣΩΠΟΠ|'
. 'ΣΥΜΠ|ΣΥΝΤ|Τ|ΥΠΟΤ|ΧΑΡ|ΑΕΙΠ|ΑΙΜΟΣΤ|ΑΝΥΠ|ΑΠΟΤ|ΑΡΤΙΠ|ΔΙΑΤ|ΕΝ|ΕΠΙΤ|ΚΡΟΚΑΛΟΠ|ΣΙΔΗΡΟΠ|Λ|ΝΑΥ|ΟΥΛΑΜ|ΟΥΡ|Π|ΤΡ|Μ)$/';
$exept19 = '/(ΟΦ|ΠΕΛ|ΧΟΡΤ|ΛΛ|ΣΦ|ΡΠ|ΦΡ|ΠΡ|ΛΟΧ|ΣΜΗΝ)$/';
if (
(preg_match($exept18, $token) || preg_match($exept19, $token))
&& !(preg_match($exept17, $token) || preg_match($exept20, $token))
) {
$token .= "ΑΓ";
}
}
// Step 5j
$re = '/^(.+?)(ΗΣΕ|ΗΣΟΥ|ΗΣΑ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept21 = '/^(Ν|ΧΕΡΣΟΝ|ΔΩΔΕΚΑΝ|ΕΡΗΜΟΝ|ΜΕΓΑΛΟΝ|ΕΠΤΑΝ)$/';
if (preg_match($exept21, $token)) {
$token .= "ΗΣ";
}
}
// Step 5k
$re = '/^(.+?)(ΗΣΤΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept22 = '/^(ΑΣΒ|ΣΒ|ΑΧΡ|ΧΡ|ΑΠΛ|ΑΕΙΜΝ|ΔΥΣΧΡ|ΕΥΧΡ|ΚΟΙΝΟΧΡ|ΠΑΛΙΜΨ)$/';
if (preg_match($exept22, $token)) {
$token .= "ΗΣΤ";
}
}
// Step 5l
$re = '/^(.+?)(ΟΥΝΕ|ΗΣΟΥΝΕ|ΗΘΟΥΝΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept23 = '/^(Ν|Ρ|ΣΠΙ|ΣΤΡΑΒΟΜΟΥΤΣ|ΚΑΚΟΜΟΥΤΣ|ΕΞΩΝ)$/';
if (preg_match($exept23, $token)) {
$token .= "ΟΥΝ";
}
}
// Step 5m
$re = '/^(.+?)(ΟΥΜΕ|ΗΣΟΥΜΕ|ΗΘΟΥΜΕ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
$test1 = false;
$exept24 = '/^(ΠΑΡΑΣΟΥΣ|Φ|Χ|ΩΡΙΟΠΛ|ΑΖ|ΑΛΛΟΣΟΥΣ|ΑΣΟΥΣ)$/';
if (preg_match($exept24, $token)) {
$token .= "ΟΥΜ";
}
}
// Step 6
$re = '/^(.+?)(ΜΑΤΑ|ΜΑΤΩΝ|ΜΑΤΟΣ)$/';
$re2 = '/^(.+?)(Α|ΑΓΑΤΕ|ΑΓΑΝ|ΑΕΙ|ΑΜΑΙ|ΑΝ|ΑΣ|ΑΣΑΙ|ΑΤΑΙ|ΑΩ|Ε|ΕΙ|ΕΙΣ|ΕΙΤΕ|ΕΣΑΙ|ΕΣ|ΕΤΑΙ|Ι|ΙΕΜΑΙ|ΙΕΜΑΣΤΕ|ΙΕΤΑΙ|ΙΕΣΑΙ|ΙΕΣΑΣΤΕ|ΙΟΜΑΣΤΑΝ|ΙΟΜΟΥΝ|'
. 'ΙΟΜΟΥΝΑ|ΙΟΝΤΑΝ|ΙΟΝΤΟΥΣΑΝ|ΙΟΣΑΣΤΑΝ|ΙΟΣΑΣΤΕ|ΙΟΣΟΥΝ|ΙΟΣΟΥΝΑ|ΙΟΤΑΝ|ΙΟΥΜΑ|ΙΟΥΜΑΣΤΕ|ΙΟΥΝΤΑΙ|ΙΟΥΝΤΑΝ|Η|ΗΔΕΣ|ΗΔΩΝ|ΗΘΕΙ|ΗΘΕΙΣ|ΗΘΕΙΤΕ|'
. 'ΗΘΗΚΑΤΕ|ΗΘΗΚΑΝ|ΗΘΟΥΝ|ΗΘΩ|ΗΚΑΤΕ|ΗΚΑΝ|ΗΣ|ΗΣΑΝ|ΗΣΑΤΕ|ΗΣΕΙ|ΗΣΕΣ|ΗΣΟΥΝ|ΗΣΩ|Ο|ΟΙ|ΟΜΑΙ|ΟΜΑΣΤΑΝ|ΟΜΟΥΝ|ΟΜΟΥΝΑ|ΟΝΤΑΙ|ΟΝΤΑΝ|ΟΝΤΟΥΣΑΝ|ΟΣ|'
. 'ΟΣΑΣΤΑΝ|ΟΣΑΣΤΕ|ΟΣΟΥΝ|ΟΣΟΥΝΑ|ΟΤΑΝ|ΟΥ|ΟΥΜΑΙ|ΟΥΜΑΣΤΕ|ΟΥΝ|ΟΥΝΤΑΙ|ΟΥΝΤΑΝ|ΟΥΣ|ΟΥΣΑΝ|ΟΥΣΑΤΕ|Υ|ΥΣ|Ω|ΩΝ)$/';
if (preg_match($re, $token, $match)) {
$token = $match[1] . "ΜΑ";
}
if (preg_match($re2, $token) && $test1) {
preg_match($re2, $token, $match);
$token = $match[1];
}
// Step 7 (ΠΑΡΑΘΕΤΙΚΑ)
$re = '/^(.+?)(ΕΣΤΕΡ|ΕΣΤΑΤ|ΟΤΕΡ|ΟΤΑΤ|ΥΤΕΡ|ΥΤΑΤ|ΩΤΕΡ|ΩΤΑΤ)$/';
if (preg_match($re, $token)) {
preg_match($re, $token, $match);
$token = $match[1];
}
return $this->toLowerCase($token, $wCase);
}
/**
* Converts the token to uppercase, suppressing accents and diaeresis. The array $wCase contains a special map of
* the uppercase rule used to convert each character at each position.
*
* @param string $token Token to process
* @param array &$wCase Map of uppercase rules
*
* @return string
*
* @since 4.0.0
*/
protected function toUpperCase($token, &$wCase)
{
$wCase = array_fill(0, mb_strlen($token, 'UTF-8'), 0);
$caseConvert = [
"α" => 'Α',
"β" => 'Β',
"γ" => 'Γ',
"δ" => 'Δ',
"ε" => 'Ε',
"ζ" => 'Ζ',
"η" => 'Η',
"θ" => 'Θ',
"ι" => 'Ι',
"κ" => 'Κ',
"λ" => 'Λ',
"μ" => 'Μ',
"ν" => 'Ν',
"ξ" => 'Ξ',
"ο" => 'Ο',
"π" => 'Π',
"ρ" => 'Ρ',
"σ" => 'Σ',
"τ" => 'Τ',
"υ" => 'Υ',
"φ" => 'Φ',
"χ" => 'Χ',
"ψ" => 'Ψ',
"ω" => 'Ω',
"ά" => 'Α',
"έ" => 'Ε',
"ή" => 'Η',
"ί" => 'Ι',
"ό" => 'Ο',
"ύ" => 'Υ',
"ώ" => 'Ω',
"ς" => 'Σ',
"ϊ" => 'Ι',
"ϋ" => 'Ι',
"ΐ" => 'Ι',
"ΰ" => 'Υ',
];
$newToken = '';
for ($i = 0; $i < mb_strlen($token); $i++) {
$char = mb_substr($token, $i, 1);
$isLower = \array_key_exists($char, $caseConvert);
if (!$isLower) {
$newToken .= $char;
continue;
}
$upperCase = $caseConvert[$char];
$newToken .= $upperCase;
$wCase[$i] = 1;
if (\in_array($char, ['ά', 'έ', 'ή', 'ί', 'ό', 'ύ', 'ώ', 'ς'])) {
$wCase[$i] = 2;
}
if (\in_array($char, ['ϊ', 'ϋ'])) {
$wCase[$i] = 3;
}
if (\in_array($char, ['ΐ', 'ΰ'])) {
$wCase[$i] = 4;
}
}
return $newToken;
}
/**
* Converts the suppressed uppercase token back to lowercase, using the $wCase map to add back the accents,
* diaeresis and handle the special case of final sigma (different lowercase glyph than the regular sigma, only
* used at the end of words).
*
* @param string $token Token to process
* @param array $wCase Map of lowercase rules
*
* @return string
*
* @since 4.0.0
*/
protected function toLowerCase($token, $wCase)
{
$newToken = '';
for ($i = 0; $i < mb_strlen($token); $i++) {
$char = mb_substr($token, $i, 1);
// Is $wCase not set at this position? We assume no case conversion ever took place.
if (!isset($wCase[$i])) {
$newToken .= $char;
continue;
}
// The character was not case-converted
if ($wCase[$i] == 0) {
$newToken .= $char;
continue;
}
// Case 1: Unaccented letter
if ($wCase[$i] == 1) {
$newToken .= mb_strtolower($char);
continue;
}
// Case 2: Vowel with accent (tonos); or the special case of final sigma
if ($wCase[$i] == 2) {
$charMap = [
'Α' => 'ά',
'Ε' => 'έ',
'Η' => 'ή',
'Ι' => 'ί',
'Ο' => 'ό',
'Υ' => 'ύ',
'Ω' => 'ώ',
'Σ' => 'ς',
];
$newToken .= $charMap[$char];
continue;
}
// Case 3: vowels with diaeresis (dialytika)
if ($wCase[$i] == 3) {
$charMap = [
'Ι' => 'ϊ',
'Υ' => 'ϋ',
];
$newToken .= $charMap[$char];
continue;
}
// Case 4: vowels with both diaeresis (dialytika) and accent (tonos)
if ($wCase[$i] == 4) {
$charMap = [
'Ι' => 'ΐ',
'Υ' => 'ΰ',
];
$newToken .= $charMap[$char];
continue;
}
// This should never happen!
$newToken .= $char;
}
return $newToken;
}
}

View File

@ -0,0 +1,71 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer\Language;
use Joomla\Component\Finder\Administrator\Indexer\Language;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Chinese (simplified) language support class for the Finder indexer package.
*
* @since 4.0.0
*/
class Zh extends Language
{
/**
* Language locale of the class
*
* @var string
* @since 4.0.0
*/
public $language = 'zh';
/**
* Spacer between terms
*
* @var string
* @since 4.0.0
*/
public $spacer = '';
/**
* Method to construct the language object.
*
* @since 4.0.0
*/
public function __construct($locale = null)
{
// Override parent constructor since we don't need to load an external stemmer
}
/**
* Method to tokenise a text string.
*
* @param string $input The input to tokenise.
*
* @return array An array of term strings.
*
* @since 4.0.0
*/
public function tokenise($input)
{
// We first add whitespace around each Chinese character, so that our later code can easily split on this.
$input = preg_replace('#\p{Han}#mui', ' $0 ', $input);
// Now we split up the input into individual terms
$terms = parent::tokenise($input);
return $terms;
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Text;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Parser base class for the Finder indexer package.
*
* @since 2.5
*/
abstract class Parser
{
/**
* Parser support instances container.
*
* @var Parser[]
* @since 4.0.0
*/
protected static $instances = [];
/**
* Method to get a parser, creating it if necessary.
*
* @param string $format The type of parser to load.
*
* @return Parser A Parser instance.
*
* @since 2.5
* @throws \Exception on invalid parser.
*/
public static function getInstance($format)
{
$format = InputFilter::getInstance()->clean($format, 'cmd');
// Only create one parser for each format.
if (isset(self::$instances[$format])) {
return self::$instances[$format];
}
// Setup the adapter for the parser.
$class = '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Parser\\' . ucfirst($format);
// Check if a parser exists for the format.
if (class_exists($class)) {
self::$instances[$format] = new $class();
return self::$instances[$format];
}
// Throw invalid format exception.
throw new \Exception(Text::sprintf('COM_FINDER_INDEXER_INVALID_PARSER', $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);
}

View File

@ -0,0 +1,158 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer\Parser;
use Joomla\Component\Finder\Administrator\Indexer\Parser;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* HTML Parser class for the Finder indexer package.
*
* @since 2.5
*/
class Html extends Parser
{
/**
* 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.
$oldSetting = ini_get('mbstring.substitute_character');
ini_set('mbstring.substitute_character', 'none');
$input = mb_convert_encoding($input, 'UTF-8', 'UTF-8');
ini_set('mbstring.substitute_character', $oldSetting);
// 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 = [
'<style', '</style',
'<noscript', '</noscript',
];
$replace = [
'<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(['&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;
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer\Parser;
use Joomla\Component\Finder\Administrator\Indexer\Parser;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* RTF Parser class for the Finder indexer package.
*
* @since 2.5
*/
class Rtf extends Parser
{
/**
* 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(['{', '}', "\\\n"], [' ', ' ', "\n"], $input);
$input = preg_replace('#\\\([^;]+?);#m', ' ', $input);
$input = preg_replace('#\\\[\'a-zA-Z0-9]+#mi', ' ', $input);
return $input;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer\Parser;
use Joomla\Component\Finder\Administrator\Indexer\Parser;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Text Parser class for the Finder indexer package.
*
* @since 2.5
*/
class Txt extends Parser
{
/**
* 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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,582 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Tree\ImmutableNodeInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* 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 Result implements \Serializable
{
/**
* An array of extra result properties.
*
* @var array
* @since 2.5
*/
protected $elements = [];
/**
* 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 = [
Indexer::TITLE_CONTEXT => ['title', 'subtitle', 'id'],
Indexer::TEXT_CONTEXT => ['summary', 'body'],
Indexer::META_CONTEXT => ['meta', 'list_price', 'sale_price'],
Indexer::PATH_CONTEXT => ['path', 'alias'],
Indexer::MISC_CONTEXT => ['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 = [];
/**
* 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 = ComponentHelper::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])) {
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]
* @param string $language The language of the taxonomy. [optional]
*
* @return void
*
* @since 2.5
*/
public function addTaxonomy($branch, $title, $state = 1, $access = 1, $language = '')
{
// We can't add taxonomies with empty titles
if (!trim($title)) {
return;
}
// Filter the input.
$branch = preg_replace('#[^\pL\pM\pN\p{Pi}\p{Pf}\'+-.,_]+#mui', ' ', $branch);
// Create the taxonomy node.
$node = new \stdClass();
$node->title = $title;
$node->state = (int) $state;
$node->access = (int) $access;
$node->language = $language;
$node->nested = false;
// Add the node to the taxonomy branch.
$this->taxonomy[$branch][] = $node;
}
/**
* Method to add a nested taxonomy map for an item.
*
* @param string $branch The title of the taxonomy branch to add the node to.
* @param ImmutableNodeInterface $contentNode The node object.
* @param integer $state The published state of the taxonomy node. [optional]
* @param integer $access The access level of the taxonomy node. [optional]
* @param string $language The language of the taxonomy. [optional]
*
* @return void
*
* @since 4.0.0
*/
public function addNestedTaxonomy($branch, ImmutableNodeInterface $contentNode, $state = 1, $access = 1, $language = '')
{
// We can't add taxonomies with empty titles
if (!trim($contentNode->title)) {
return;
}
// Filter the input.
$branch = preg_replace('#[^\pL\pM\pN\p{Pi}\p{Pf}\'+-.,_]+#mui', ' ', $branch);
// Create the taxonomy node.
$node = new \stdClass();
$node->title = $contentNode->title;
$node->state = (int) $state;
$node->access = (int) $access;
$node->language = $language;
$node->nested = true;
$node->node = $contentNode;
// Add the node to the taxonomy branch.
$this->taxonomy[$branch][] = $node;
}
/**
* Method to set the item language
*
* @return void
*
* @since 3.0
*/
public function setLanguage()
{
if ($this->language == '') {
$this->language = $this->defaultLanguage;
}
}
/**
* Helper function to serialise the data of a Result object
*
* @return string The serialised data
*
* @since 4.0.0
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
* Helper function to unserialise the data for this object
*
* @param string $serialized Serialised data to unserialise
*
* @return void
*
* @since 4.0.0
*/
public function unserialize($serialized): void
{
$this->__unserialize(unserialize($serialized));
}
/**
* Magic method used for serializing.
*
* @since 4.1.3
*/
public function __serialize(): array
{
$taxonomy = [];
foreach ($this->taxonomy as $branch => $nodes) {
$taxonomy[$branch] = [];
foreach ($nodes as $node) {
if ($node->nested) {
$n = clone $node;
unset($n->node);
$taxonomy[$branch][] = $n;
} else {
$taxonomy[$branch][] = $node;
}
}
}
// This order must match EXACTLY the order of the $properties in the self::__unserialize method
return [
$this->access,
$this->defaultLanguage,
$this->description,
$this->elements,
$this->end_date,
$this->instructions,
$this->language,
$this->list_price,
$this->publish_end_date,
$this->publish_start_date,
$this->published,
$this->route,
$this->sale_price,
$this->start_date,
$this->state,
$taxonomy,
$this->title,
$this->type_id,
$this->url,
];
}
/**
* Magic method used for unserializing.
*
* @since 4.1.3
*/
public function __unserialize(array $serialized): void
{
// This order must match EXACTLY the order of the array in the self::__serialize method
$properties = [
'access',
'defaultLanguage',
'description',
'elements',
'end_date',
'instructions',
'language',
'list_price',
'publish_end_date',
'publish_start_date',
'published',
'route',
'sale_price',
'start_date',
'state',
'taxonomy',
'title',
'type_id',
'url',
];
foreach ($properties as $k => $v) {
$this->$v = $serialized[$k];
}
foreach ($this->taxonomy as $nodes) {
foreach ($nodes as $node) {
$curTaxonomy = Taxonomy::getTaxonomy($node->id);
$node->state = $curTaxonomy->state;
$node->access = $curTaxonomy->access;
}
}
}
}

View File

@ -0,0 +1,514 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\CMS\Factory;
use Joomla\CMS\Tree\NodeInterface;
use Joomla\Component\Finder\Administrator\Table\MapTable;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Taxonomy base class for the Finder indexer package.
*
* @since 2.5
*/
class Taxonomy
{
/**
* An internal cache of taxonomy data.
*
* @var object[]
* @since 4.0.0
*/
public static $taxonomies = [];
/**
* An internal cache of branch data.
*
* @var object[]
* @since 4.0.0
*/
public static $branches = [];
/**
* An internal cache of taxonomy node data for inserting it.
*
* @var object[]
* @since 2.5
*/
public static $nodes = [];
/**
* 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 \RuntimeException on database error.
*/
public static function addBranch($title, $state = 1, $access = 1)
{
$node = new \stdClass();
$node->title = $title;
$node->access = $access;
$node->parent_id = 1;
$node->language = '';
return self::storeNode($node, 1);
}
/**
* 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]
* @param string $language The language of the node. [optional]
*
* @return integer The id of the node.
*
* @since 2.5
* @throws \RuntimeException on database error.
*/
public static function addNode($branch, $title, $state = 1, $access = 1, $language = '')
{
if ($state != 1) {
return 0;
}
// Get the branch id, insert it if it does not exist.
$branchId = static::addBranch($branch);
$node = new \stdClass();
$node->title = $title;
$node->access = $access;
$node->parent_id = $branchId;
$node->language = $language;
return self::storeNode($node, $branchId);
}
/**
* Method to add a nested node to the taxonomy tree.
*
* @param string $branch The title of the branch to store the node in.
* @param NodeInterface $node The source-node of the taxonomy node.
* @param integer $state The published state of the node. [optional]
* @param integer $access The access state of the node. [optional]
* @param string $language The language of the node. [optional]
* @param integer $branchId ID of a branch if known. [optional]
*
* @return integer The id of the node.
*
* @since 4.0.0
*/
public static function addNestedNode($branch, NodeInterface $node, $state = 1, $access = 1, $language = '', $branchId = null)
{
if ($state != 1) {
return 0;
}
if (!$branchId) {
// Get the branch id, insert it if it does not exist.
$branchId = static::addBranch($branch);
}
$parent = $node->getParent();
$pstate = $node->state ?? ($node->published ?? $state);
$paccess = $node->access ?? $access;
$planguage = $node->language ?? $language;
if ($parent && $parent->title != 'ROOT') {
$parentId = self::addNestedNode($branch, $parent, $pstate, $paccess, $planguage, $branchId);
} else {
$parentId = $branchId;
}
if (!$parentId) {
return 0;
}
$temp = new \stdClass();
$temp->title = $node->title;
$temp->access = $access;
$temp->parent_id = $parentId;
$temp->language = $language;
return self::storeNode($temp, $parentId);
}
/**
* A helper method to store a node in the taxonomy
*
* @param object $node The node data to include
* @param integer $parentId The parent id of the node to add.
*
* @return integer The id of the inserted node.
*
* @since 4.0.0
* @throws \RuntimeException
*/
protected static function storeNode($node, $parentId)
{
// Check to see if the node is in the cache.
if (isset(static::$nodes[$parentId . ':' . $node->title])) {
return static::$nodes[$parentId . ':' . $node->title]->id;
}
// Check to see if the node is in the table.
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__finder_taxonomy'))
->where($db->quoteName('parent_id') . ' = ' . $db->quote($parentId))
->where($db->quoteName('title') . ' = ' . $db->quote($node->title))
->where($db->quoteName('language') . ' = ' . $db->quote($node->language));
$db->setQuery($query);
// Get the result.
$result = $db->loadObject();
// Check if the database matches the input data.
if ((bool) $result && $result->access == $node->access) {
// The data matches, add the item to the cache.
static::$nodes[$parentId . ':' . $node->title] = $result;
return static::$nodes[$parentId . ':' . $node->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.
* @todo: use factory?
*/
$nodeTable = new MapTable($db);
if (empty($result)) {
// Prepare the node object.
$nodeTable->title = $node->title;
$nodeTable->access = (int) $node->access;
$nodeTable->language = $node->language;
$nodeTable->setLocation((int) $parentId, 'last-child');
} else {
// Prepare the node object.
$nodeTable->id = (int) $result->id;
$nodeTable->title = $result->title;
$nodeTable->access = (int) $result->access;
$nodeTable->language = $node->language;
$nodeTable->setLocation($result->parent_id, 'last-child');
}
// Check the data.
if (!$nodeTable->check()) {
$error = $nodeTable->getError();
if ($error instanceof \Exception) {
// \Joomla\CMS\Table\NestedTable sets errors of exceptions, so in this case we can pass on more
// information
throw new \RuntimeException(
$error->getMessage(),
$error->getCode(),
$error
);
}
// Standard string returned. Probably from the \Joomla\CMS\Table\Table class
throw new \RuntimeException($error, 500);
}
// Store the data.
if (!$nodeTable->store()) {
$error = $nodeTable->getError();
if ($error instanceof \Exception) {
// \Joomla\CMS\Table\NestedTable sets errors of exceptions, so in this case we can pass on more
// information
throw new \RuntimeException(
$error->getMessage(),
$error->getCode(),
$error
);
}
// Standard string returned. Probably from the \Joomla\CMS\Table\Table class
throw new \RuntimeException($error, 500);
}
$nodeTable->rebuildPath($nodeTable->id);
// Add the node to the cache.
static::$nodes[$parentId . ':' . $nodeTable->title] = (object) $nodeTable->getProperties();
return static::$nodes[$parentId . ':' . $nodeTable->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 \RuntimeException on database error.
*/
public static function addMap($linkId, $nodeId)
{
// Insert the map.
$db = Factory::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();
if (!$id) {
$map = new \stdClass();
$map->link_id = (int) $linkId;
$map->node_id = (int) $nodeId;
$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 \RuntimeException on database error.
*/
public static function getBranchTitles()
{
$db = Factory::getDbo();
// Set user variables
$groups = implode(',', Factory::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 \RuntimeException on database error.
*/
public static function getNodeByTitle($branch, $title)
{
$db = Factory::getDbo();
// Set user variables
$groups = implode(',', Factory::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.
$query->setLimit(1);
$db->setQuery($query);
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 \RuntimeException on database error.
*/
public static function removeMaps($linkId)
{
// Delete the maps.
$db = Factory::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 maps
*
* @return integer The number of deleted rows.
*
* @since 4.2.0
* @throws \RuntimeException on database error.
*/
public static function removeOrphanMaps()
{
// Delete all orphaned maps
$db = Factory::getDbo();
$query2 = $db->getQuery(true)
->select($db->quoteName('link_id'))
->from($db->quoteName('#__finder_links'));
$query = $db->getQuery(true)
->delete($db->quoteName('#__finder_taxonomy_map'))
->where($db->quoteName('link_id') . ' NOT IN (' . $query2 . ')');
$db->setQuery($query);
$db->execute();
$count = $db->getAffectedRows();
return $count;
}
/**
* Method to remove orphaned taxonomy nodes and branches.
*
* @return integer The number of deleted rows.
*
* @since 2.5
* @throws \RuntimeException on database error.
*/
public static function removeOrphanNodes()
{
// Delete all orphaned nodes.
$affectedRows = 0;
$db = Factory::getDbo();
$nodeTable = new MapTable($db);
$query = $db->getQuery(true);
$query->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('t.lft + 1 = t.rgt')
->where($db->quoteName('m.link_id') . ' IS NULL');
do {
$db->setQuery($query);
$nodes = $db->loadColumn();
foreach ($nodes as $node) {
$nodeTable->delete($node);
$affectedRows++;
}
} while ($nodes);
return $affectedRows;
}
/**
* Get a taxonomy based on its id or all taxonomies
*
* @param integer $id Id of the taxonomy
*
* @return object|object[] A taxonomy object or an array of all taxonomies
*
* @since 4.0.0
*/
public static function getTaxonomy($id = 0)
{
if (!\count(self::$taxonomies)) {
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->select(['id','parent_id','lft','rgt','level','path','title','alias','state','access','language'])
->from($db->quoteName('#__finder_taxonomy'))
->order($db->quoteName('lft'));
$db->setQuery($query);
self::$taxonomies = $db->loadObjectList('id');
}
if ($id == 0) {
return self::$taxonomies;
}
if (isset(self::$taxonomies[$id])) {
return self::$taxonomies[$id];
}
return false;
}
/**
* Get a taxonomy branch object based on its title or all branches
*
* @param string $title Title of the branch
*
* @return object|object[] The object with the branch data or an array of all branches
*
* @since 4.0.0
*/
public static function getBranch($title = '')
{
if (!\count(self::$branches)) {
$taxonomies = self::getTaxonomy();
foreach ($taxonomies as $t) {
if ($t->level == 1) {
self::$branches[$t->title] = $t;
}
}
}
if ($title == '') {
return self::$branches;
}
if (isset(self::$branches[$title])) {
return self::$branches[$title];
}
return false;
}
}

View File

@ -0,0 +1,185 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Indexer;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Token class for the Finder indexer package.
*
* @since 2.5
*/
class Token
{
/**
* 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 = [];
/**
* 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;
/**
* The token required flag
*
* @var boolean
* @since 4.3.0
*/
public $required;
/**
* 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 = ' ')
{
if (!$lang) {
$this->language = '*';
} else {
$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([Helper::class, 'stem'], $term, [$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 = (min($this->length, 30) / 30) + 1;
$this->weight = round($this->weight, 4);
} else {
// Populate the token instance.
$this->term = $term;
$this->stem = Helper::stem($this->term, $lang);
$this->numeric = (is_numeric($this->term) || (bool) preg_match('#^[0-9,.\-\+]+$#', $this->term));
$this->common = $this->numeric ? false : Helper::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 = min($this->length, 15) / 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);
}
}
}

View File

@ -0,0 +1,154 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\Component\Finder\Administrator\Table\FilterTable;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filter model class for Finder.
*
* @since 2.5
*/
class FilterModel extends AdminModel
{
/**
* 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 No longer used, will be removed without replacement
* @deprecated 4.3 will be removed in 6.0
*
* @return void
*
* @since 2.5
*/
protected function cleanCache($group = 'com_finder', $clientId = 0)
{
parent::cleanCache($group);
}
/**
* Method to get the filter data.
*
* @return FilterTable|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 = [];
}
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 Form|boolean A Form object on success, false on failure
*
* @since 2.5
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_finder.filter', 'filter', ['control' => 'jform', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
return $form;
}
/**
* 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 = Factory::getApplication()->getUserState('com_finder.edit.filter.data', []);
if (empty($data)) {
$data = $this->getItem();
}
$this->preprocessData('com_finder.filter', $data);
return $data;
}
/**
* Method to get the total indexed items
*
* @return integer The count of indexed items
*
* @since 3.5
*/
public function getTotal()
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('MAX(link_id)')
->from('#__finder_links');
return $db->setQuery($query)->loadResult();
}
}

View File

@ -0,0 +1,141 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filters model class for Finder.
*
* @since 2.5
*/
class FiltersModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param ?MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.7
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'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, $factory);
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*
* @since 2.5
*/
protected function getListQuery()
{
$db = $this->getDatabase();
$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 = ComponentHelper::getParams('com_finder');
$this->setState('params', $params);
// List state information.
parent::populateState($ordering, $direction);
}
}

View File

@ -0,0 +1,466 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Index model class for Finder.
*
* @since 2.5
*/
class IndexModel extends ListModel
{
/**
* 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';
/**
* The event to trigger after purging the data.
*
* @var string
* @since 4.0.0
*/
protected $event_after_purge = 'onFinderIndexAfterPurge';
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param ?MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.7
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'state', 'published', 'l.published',
'title', 'l.title',
'type', 'type_id', 'l.type_id',
't.title', 't_title',
'url', 'l.url',
'language', 'l.language',
'indexdate', 'l.indexdate',
'content_map',
];
}
parent::__construct($config, $factory);
}
/**
* 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 $this->getCurrentUser()->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 $this->getCurrentUser()->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)
{
$pks = (array) $pks;
$table = $this->getTable();
// Include the content plugins for the on delete events.
PluginHelper::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 = Factory::getApplication()->triggerEvent($this->event_before_delete, [$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.
Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]);
} else {
// Prune items that you can't change.
unset($pks[$i]);
$error = $this->getError();
if ($error) {
$this->setError($error);
} else {
$this->setError(Text::_('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 \Joomla\Database\DatabaseQuery
*
* @since 2.5
*/
protected function getListQuery()
{
$db = $this->getDatabase();
$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');
// Join over the language
$query->select('la.title AS language_title, la.image AS language_image')
->join('LEFT', $db->quoteName('#__languages') . ' AS la ON la.lang_code = l.language');
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);
}
// Filter on the language.
if ($language = $this->getState('filter.language')) {
$query->where($db->quoteName('l.language') . ' = ' . $db->quote($language));
}
// 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->getDatabase();
$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->getDatabase();
$query = $db->getQuery(true)
->select('COUNT(link_id)')
->from($db->quoteName('#__finder_links'));
$db->setQuery($query);
return (int) $db->loadResult();
}
/**
* Returns a Table 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 \Joomla\CMS\Table\Table A database object
*
* @since 2.5
*/
public function getTable($type = 'Link', $prefix = 'Administrator', $config = [])
{
return parent::getTable($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->getDatabase();
// Truncate the links table.
$db->truncateTable('#__finder_links');
// Truncate the links terms tables.
$db->truncateTable('#__finder_links_terms');
// Truncate the terms table.
$db->truncateTable('#__finder_terms');
// Truncate the taxonomy map table.
$db->truncateTable('#__finder_taxonomy_map');
// Truncate the taxonomy table and insert the root node.
$db->truncateTable('#__finder_taxonomy');
$root = (object) [
'id' => 1,
'parent_id' => 0,
'lft' => 0,
'rgt' => 1,
'level' => 0,
'path' => '',
'title' => 'ROOT',
'alias' => 'root',
'state' => 1,
'access' => 1,
'language' => '*',
];
$db->insertObject('#__finder_taxonomy', $root);
// Truncate the tokens tables.
$db->truncateTable('#__finder_tokens');
// Truncate the tokens aggregate table.
$db->truncateTable('#__finder_tokens_aggregate');
// Include the finder plugins for the on purge events.
PluginHelper::importPlugin('finder');
Factory::getApplication()->triggerEvent($this->event_after_purge);
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'));
$this->setState('filter.language', $this->getUserStateFromRequest($this->context . '.filter.language', 'filter_language', ''));
// Load the parameters.
$params = ComponentHelper::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)
{
$user = $this->getCurrentUser();
$table = $this->getTable();
$pks = (array) $pks;
// Include the content plugins for the change of state event.
PluginHelper::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(Text::_('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 = Factory::getApplication()->triggerEvent('onContentChangeState', [$context, $pks, $value]);
if (\in_array(false, $result, true)) {
$this->setError($table->getError());
return false;
}
// Clear the component's cache
$this->cleanCache();
return true;
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Form\Form;
use Joomla\CMS\MVC\Model\FormModel;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Indexer model class for Finder.
*
* @since 2.5
*/
class IndexerModel extends FormModel
{
/**
* Method for getting a form.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form
*
* @since 5.0.0
*
* @throws \Exception
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_finder.indexer', 'indexer', ['control' => '', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
return $form;
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\Database\ParameterType;
/**
* Index Item model class for Finder.
*
* @since 5.0.0
*/
class ItemModel extends BaseDatabaseModel
{
/**
* Stock method to auto-populate the model state.
*
* @return void
*
* @since 5.0.0
*/
protected function populateState()
{
// Get the pk of the record from the request.
$pk = Factory::getApplication()->getInput()->getInt('id');
$this->setState('item.link_id', $pk);
}
/**
* Get a finder link object
*
* @return object
*
* @since 5.0.0
*/
public function getItem()
{
$link_id = (int) $this->getState('item.link_id');
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__finder_links', 'l'))
->where($db->quoteName('l.link_id') . ' = :link_id')
->bind(':link_id', $link_id, ParameterType::INTEGER);
$db->setQuery($query);
return $db->loadObject();
}
/**
* Get terms associated with a finder link
*
* @return object[]
*
* @since 5.0.0
*/
public function getTerms()
{
$link_id = (int) $this->getState('item.link_id');
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('t.*, l.*')
->from($db->quoteName('#__finder_links_terms', 'l'))
->leftJoin($db->quoteName('#__finder_terms', 't') . ' ON ' . $db->quoteName('t.term_id') . ' = ' . $db->quoteName('l.term_id'))
->where($db->quoteName('l.link_id') . ' = :link_id')
->order('l.weight')
->bind(':link_id', $link_id, ParameterType::INTEGER);
$db->setQuery($query);
return $db->loadObjectList();
}
/**
* Get taxonomies associated with a finder link
*
* @return \stdClass[]
*
* @since 5.0.0
*/
public function getTaxonomies()
{
$link_id = (int) $this->getState('item.link_id');
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('t.*, m.*')
->from($db->quoteName('#__finder_taxonomy_map', 'm'))
->leftJoin($db->quoteName('#__finder_taxonomy', 't') . ' ON ' . $db->quoteName('t.id') . ' = ' . $db->quoteName('m.node_id'))
->where($db->quoteName('m.link_id') . ' = :link_id')
->order('t.title')
->bind(':link_id', $link_id, ParameterType::INTEGER);
$db->setQuery($query);
return $db->loadObjectList();
}
}

View File

@ -0,0 +1,403 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseQuery;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Maps model for the Finder package.
*
* @since 2.5
*/
class MapsModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param ?MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.7
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'state', 'a.state',
'title', 'a.title',
'branch',
'branch_title', 'd.branch_title',
'level', 'd.level',
'language', 'a.language',
];
}
parent::__construct($config, $factory);
}
/**
* 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 $this->getCurrentUser()->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 $this->getCurrentUser()->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)
{
$pks = (array) $pks;
$table = $this->getTable();
// Include the content plugins for the on delete events.
PluginHelper::importPlugin('content');
// Iterate the items to check if all of them exist.
foreach ($pks as $i => $pk) {
if (!$table->load($pk)) {
// Item is not in the table.
$this->setError($table->getError());
return false;
}
}
// 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 = Factory::getApplication()->triggerEvent('onContentBeforeDelete', [$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.
Factory::getApplication()->triggerEvent('onContentAfterDelete', [$context, $table]);
} else {
// Prune items that you can't change.
unset($pks[$i]);
$error = $this->getError();
if ($error) {
$this->setError($error);
} else {
$this->setError(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'));
}
}
}
}
// Clear the component's cache
$this->cleanCache();
return true;
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*
* @since 2.5
*/
protected function getListQuery()
{
$db = $this->getDatabase();
// Select all fields from the table.
$query = $db->getQuery(true)
->select('a.id, a.parent_id, a.lft, a.rgt, a.level, a.path, a.title, a.alias, a.state, a.access, a.language')
->from($db->quoteName('#__finder_taxonomy', 'a'))
->where('a.parent_id != 0');
// Join to get the branch title
$query->select([$db->quoteName('b.id', 'branch_id'), $db->quoteName('b.title', 'branch_title')])
->leftJoin($db->quoteName('#__finder_taxonomy', 'b') . ' ON b.level = 1 AND b.lft <= a.lft AND a.rgt <= b.rgt');
// 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);
}
// Add the list ordering clause.
$query->order($db->escape($this->getState('list.ordering', 'branch_title, a.lft')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));
return $query;
}
/**
* Returns a record count for the query.
*
* @param \Joomla\Database\DatabaseQuery|string
*
* @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->getDatabase()->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 Table 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 \Joomla\CMS\Table\Table A database object
*
* @since 2.5
*/
public function getTable($type = 'Map', $prefix = 'Administrator', $config = [])
{
return parent::getTable($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 = 'branch_title, a.lft', $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 = ComponentHelper::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)
{
$user = $this->getCurrentUser();
$table = $this->getTable();
$pks = (array) $pks;
// Include the content plugins for the change of state event.
PluginHelper::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(Text::_('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 = Factory::getApplication()->triggerEvent('onContentChangeState', [$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->getDatabase();
$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;
}
/**
* Manipulate the query to be used to evaluate if this is an Empty State to provide specific conditions for this extension.
*
* @return DatabaseQuery
*
* @since 4.0.0
*/
protected function getEmptyStateQuery()
{
$query = parent::getEmptyStateQuery();
$title = 'ROOT';
$query->where($this->getDatabase()->quoteName('title') . ' <> :title')
->bind(':title', $title);
return $query;
}
}

View File

@ -0,0 +1,173 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of search terms.
*
* @since 4.0.0
*/
class SearchesModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param ?MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 4.0.0
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'searchterm', 'a.searchterm',
'hits', 'a.hits',
];
}
parent::__construct($config, $factory);
}
/**
* Method to auto-populate the model state.
*
* Note. 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 4.0.0
*/
protected function populateState($ordering = 'a.hits', $direction = 'asc')
{
// Special state for toggle results button.
$this->setState('show_results', $this->getUserStateFromRequest($this->context . '.show_results', 'show_results', 1, 'int'));
// Load the parameters.
$params = ComponentHelper::getParams('com_finder');
$this->setState('params', $params);
// List state information.
parent::populateState($ordering, $direction);
}
/**
* 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.
*
* @return string A store id.
*
* @since 4.0.0
*/
protected function getStoreId($id = '')
{
// Compile the store id.
$id .= ':' . $this->getState('show_results');
$id .= ':' . $this->getState('filter.search');
return parent::getStoreId($id);
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*
* @since 4.0.0
*/
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',
'a.*'
)
);
$query->from($db->quoteName('#__finder_logging', 'a'));
// Filter by search in title
if ($search = $this->getState('filter.search')) {
$search = $db->quote('%' . str_replace(' ', '%', $db->escape(trim($search), true) . '%'));
$query->where($db->quoteName('a.searchterm') . ' LIKE ' . $search);
}
// Add the list ordering clause.
$query->order($db->escape($this->getState('list.ordering', 'a.hits')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));
return $query;
}
/**
* Override the parent getItems to inject optional data.
*
* @return mixed An array of objects on success, false on failure.
*
* @since 4.0.0
*/
public function getItems()
{
$items = parent::getItems();
foreach ($items as $item) {
if (\is_resource($item->query)) {
$item->query = unserialize(stream_get_contents($item->query));
} else {
$item->query = unserialize($item->query);
}
}
return $items;
}
/**
* Method to reset the search log table.
*
* @return boolean
*
* @since 4.0.0
*/
public function reset()
{
$db = $this->getDatabase();
try {
$db->truncateTable('#__finder_logging');
} catch (\RuntimeException $e) {
$this->setError($e->getMessage());
return false;
}
return true;
}
}

View File

@ -0,0 +1,87 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Statistics model class for Finder.
*
* @since 2.5
*/
class StatisticsModel extends BaseDatabaseModel
{
/**
* Method to get the component statistics
*
* @return CMSObject The component statistics
*
* @since 2.5
*/
public function getData()
{
// Initialise
$db = $this->getDatabase();
$query = $db->getQuery(true);
$data = new CMSObject();
$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 = Factory::getLanguage();
$plugins = PluginHelper::getPlugin('finder');
foreach ($plugins as $plugin) {
$lang->load('plg_finder_' . $plugin->name . '.sys', JPATH_ADMINISTRATOR)
|| $lang->load('plg_finder_' . $plugin->name . '.sys', JPATH_PLUGINS . '/finder/' . $plugin->name);
}
return $data;
}
}

View File

@ -0,0 +1,189 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Response;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Finder Indexer JSON Response Class
*
* @since 2.5
*/
class Response
{
/**
* The buffer
*
* @var string
* @since 4.3.0
*/
public $buffer;
/**
* The memory
*
* @var string
* @since 4.3.0
*/
public $memory;
/**
* If it has an error
*
* @var bool
* @since 4.3.0
*/
public $error;
/**
* The header
*
* @var string
* @since 4.3.0
*/
public $header;
/**
* The message
*
* @var string
* @since 4.3.0
*/
public $message;
/**
* The batch size
*
* @var int
* @since 4.3.0
*/
public $batchSize;
/**
* The batch offset
*
* @var int
* @since 4.3.0
*/
public $batchOffset;
/**
* The total items
*
* @var int
* @since 4.3.0
*/
public $totalItems;
/**
* The plugin state
*
* @var string
* @since 4.3.0
*/
public $pluginState;
/**
* The start time
*
* @var string
* @since 4.3.0
*/
public $startTime;
/**
* The end time
*
* @var string
* @since 4.3.0
*/
public $endTime;
/**
* The start
*
* @var int
* @since 4.3.0
*/
public $start;
/**
* The complete
*
* @var int
* @since 4.3.0
*/
public $complete;
/**
* Class Constructor
*
* @param mixed $state The processing state for the indexer
*
* @since 2.5
*/
public function __construct($state)
{
$params = ComponentHelper::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';
Log::addLogger($options);
}
// Check if we are dealing with an error.
if ($state instanceof \Exception) {
// Log the error
try {
Log::add($state->getMessage(), Log::ERROR);
} catch (\RuntimeException $exception) {
// Informational log only
}
// Prepare the error response.
$this->error = true;
$this->header = Text::_('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->pluginState = $state->pluginState;
$this->startTime = $state->startTime;
$this->endTime = Factory::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 = Text::_('COM_FINDER_INDEXER_HEADER_COMPLETE');
$this->message = Text::_('COM_FINDER_INDEXER_MESSAGE_COMPLETE');
} elseif ($this->totalItems <= 0) {
$this->header = Text::_('COM_FINDER_INDEXER_HEADER_OPTIMIZE');
$this->message = Text::_('COM_FINDER_INDEXER_MESSAGE_OPTIMIZE');
} else {
$this->header = Text::_('COM_FINDER_INDEXER_HEADER_RUNNING');
$this->message = Text::_('COM_FINDER_INDEXER_MESSAGE_RUNNING');
}
}
}
}

View File

@ -0,0 +1,496 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
use Joomla\Component\Finder\Administrator\Indexer\Query;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Filter\OutputFilter;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filter HTML Behaviors for Finder.
*
* @since 2.5
*/
class Filter
{
use DatabaseAwareTrait;
/**
* 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 function slider($options = [])
{
$db = $this->getDatabase();
$query = $db->getQuery(true);
$user = Factory::getUser();
$groups = implode(',', $user->getAuthorisedViewLevels());
$html = '';
$filter = null;
// Get the configuration options.
$filterId = $options['filter_id'] ?? null;
$activeNodes = \array_key_exists('selected_nodes', $options) ? $options['selected_nodes'] : [];
$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.title, c.parent_id')
->order('t.lft, 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 .= HTMLHelper::_('bootstrap.startAccordion', 'accordion', ['active' => 'accordion-' . $branch_keys[0]]);
// Load plugin language files.
LanguageHelper::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' && Multilanguage::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.lft > ' . (int) $bv->lft)
->where('t.rgt < ' . (int) $bv->rgt)
->where('t.state = 1')
->where('t.access IN (' . $groups . ')')
->order('t.lft, 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 = Factory::getLanguage();
foreach ($nodes as $nk => $nv) {
if (trim($nv->parent_title, '*') === 'Language') {
$title = LanguageHelper::branchLanguageTitle($nv->title);
} else {
$key = LanguageHelper::branchPlural($nv->title);
$title = $lang->hasKey($key) ? Text::_($key) : $nv->title;
}
$nodes[$nk]->title = $title;
}
// Adding slides
$html .= HTMLHelper::_(
'bootstrap.addSlide',
'accordion',
Text::sprintf(
'COM_FINDER_FILTER_BRANCH_LABEL',
Text::_(LanguageHelper::branchSingular($bv->title)) . ' - ' . \count($nodes)
),
'accordion-' . $bk
);
// Populate the toggle button.
$html .= '<button class="btn btn-secondary js-filter" type="button" data-id="tax-' . $bk . '"><span class="icon-square" aria-hidden="true"></span> '
. Text::_('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="form-check">';
$html .= '<label class="form-check-label">';
$html .= '<input type="checkbox" class="form-check-input selector filter-node' . $classSuffix
. ' tax-' . $bk . '" value="' . $nk . '" name="t[]"' . $checked . '> ' . str_repeat('&mdash;', $nv->level - 2) . $nv->title;
$html .= '</label>';
$html .= '</div>';
}
$html .= HTMLHelper::_('bootstrap.endSlide');
}
$html .= HTMLHelper::_('bootstrap.endAccordion');
return $html;
}
/**
* Method to generate filters using select box dropdown controls.
*
* @param Query $idxQuery A Query object.
* @param Registry $options An array of options.
*
* @return string|null A rendered HTML widget on success, null otherwise.
*
* @since 2.5
*/
public function select($idxQuery, $options)
{
$user = Factory::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 = Factory::getCache('com_finder', '');
$cacheId = 'filter_select_' . serialize([$idxQuery->filter, $options, $groups, Factory::getLanguage()->getTag()]);
// Check the cached results.
if ($cache->contains($cacheId)) {
$branches = $cache->get($cacheId);
} else {
$db = $this->getDatabase();
$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'))
->group($db->quoteName('t.parent_id'))
->group('t.title, t.state, t.access, t.lft')
->order('t.lft, 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' && Multilanguage::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.lft > :lft')
->where('t.rgt < :rgt')
->where('t.state = 1')
->whereIn('t.access', $user->getAuthorisedViewLevels())
->order('t.title')
->bind(':lft', $bv->lft, ParameterType::INTEGER)
->bind(':rgt', $bv->rgt, ParameterType::INTEGER);
// Apply multilanguage filter
if (Multilanguage::isEnabled()) {
$language = [Factory::getLanguage()->getTag(), '*'];
$query->whereIn($db->quoteName('t.language'), $language, ParameterType::STRING);
}
// 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->whereIn('t.id', explode(",", $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 = Factory::getLanguage();
foreach ($branches[$bk]->nodes as $node_id => $node) {
if (trim($node->parent_title, '*') === 'Language') {
$title = LanguageHelper::branchLanguageTitle($node->title);
} else {
$key = LanguageHelper::branchPlural($node->title);
$title = $language->hasKey($key) ? Text::_($key) : $node->title;
}
if ($node->level > 2) {
$branches[$bk]->nodes[$node_id]->title = str_repeat('-', $node->level - 2) . $title;
} else {
$branches[$bk]->nodes[$node_id]->title = $title;
}
}
// Add the Search All option to the branch.
array_unshift($branches[$bk]->nodes, ['id' => null, 'title' => Text::_('COM_FINDER_FILTER_SELECT_ALL_LABEL')]);
}
// Store the data in cache.
$cache->store($branches, $cacheId);
}
$html = '';
// Add the dates if enabled.
if ($showDates) {
$html .= HTMLHelper::_('filter.dates', $idxQuery, $options);
}
$html .= '<div class="filter-branch' . $classSuffix . '">';
// 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' && Multilanguage::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 = Factory::getApplication()->getInput()->request->get('t', [], '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="control-group">';
$html .= '<div class="control-label">';
$html .= '<label for="tax-' . OutputFilter::stringURLSafe($bv->title) . '">';
$html .= Text::sprintf('COM_FINDER_FILTER_BRANCH_LABEL', Text::_(LanguageHelper::branchSingular($bv->title)));
$html .= '</label>';
$html .= '</div>';
$html .= '<div class="controls">';
$html .= HTMLHelper::_(
'select.genericlist',
$branches[$bk]->nodes,
't[]',
'class="form-select advancedSelect"',
'id',
'title',
$active,
'tax-' . OutputFilter::stringURLSafe($bv->title)
);
$html .= '</div>';
$html .= '</div>';
}
$html .= '</div>';
return $html;
}
/**
* Method to generate fields for filtering dates
*
* @param Query $idxQuery A Query object.
* @param Registry $options An array of options.
*
* @return string A rendered HTML widget.
*
* @since 2.5
*/
public 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 = [];
$operators[] = HTMLHelper::_('select.option', 'before', Text::_('COM_FINDER_FILTER_DATE_BEFORE'));
$operators[] = HTMLHelper::_('select.option', 'exact', Text::_('COM_FINDER_FILTER_DATE_EXACTLY'));
$operators[] = HTMLHelper::_('select.option', 'after', Text::_('COM_FINDER_FILTER_DATE_AFTER'));
// Load the CSS/JS resources.
if ($loadMedia) {
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->useStyle('com_finder.dates');
}
// Open the widget.
$html .= '<ul id="finder-filter-select-dates">';
// Start date filter.
$attribs['class'] = 'input-medium';
$html .= '<li class="filter-date float-start' . $classSuffix . '">';
$html .= '<label for="filter_date1" class="hasTooltip" title ="' . Text::_('COM_FINDER_FILTER_DATE1_DESC') . '">';
$html .= Text::_('COM_FINDER_FILTER_DATE1');
$html .= '</label>';
$html .= '<br>';
$html .= '<label for="finder-filter-w1" class="visually-hidden">';
$html .= Text::_('COM_FINDER_FILTER_DATE1_OPERATOR');
$html .= '</label>';
$html .= HTMLHelper::_(
'select.genericlist',
$operators,
'w1',
'class="inputbox filter-date-operator advancedSelect form-select w-auto mb-2"',
'value',
'text',
$idxQuery->when1,
'finder-filter-w1'
);
$html .= HTMLHelper::_('calendar', $idxQuery->date1, 'd1', 'filter_date1', '%Y-%m-%d', $attribs);
$html .= '</li>';
// End date filter.
$html .= '<li class="filter-date float-end' . $classSuffix . '">';
$html .= '<label for="filter_date2" class="hasTooltip" title ="' . Text::_('COM_FINDER_FILTER_DATE2_DESC') . '">';
$html .= Text::_('COM_FINDER_FILTER_DATE2');
$html .= '</label>';
$html .= '<br>';
$html .= '<label for="finder-filter-w2" class="visually-hidden">';
$html .= Text::_('COM_FINDER_FILTER_DATE2_OPERATOR');
$html .= '</label>';
$html .= HTMLHelper::_(
'select.genericlist',
$operators,
'w2',
'class="inputbox filter-date-operator advancedSelect form-select w-auto mb-2"',
'value',
'text',
$idxQuery->when2,
'finder-filter-w2'
);
$html .= HTMLHelper::_('calendar', $idxQuery->date2, 'd2', 'filter_date2', '%Y-%m-%d', $attribs);
$html .= '</li>';
// Close the widget.
$html .= '</ul>';
}
return $html;
}
}

View File

@ -0,0 +1,131 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* HTML behavior class for Finder.
*
* @since 2.5
*/
class Finder
{
use DatabaseAwareTrait;
/**
* Creates a list of types to filter on.
*
* @return array An array containing the types that can be selected.
*
* @since 2.5
*/
public function typeslist()
{
// Load the finder types.
$db = $this->getDatabase();
$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 [];
}
// Compile the options.
$options = [];
$lang = Factory::getLanguage();
foreach ($rows as $row) {
$key = $lang->hasKey(LanguageHelper::branchPlural($row->text)) ? LanguageHelper::branchPlural($row->text) : $row->text;
$options[] = HTMLHelper::_('select.option', $row->value, Text::sprintf('COM_FINDER_ITEM_X_ONLY', Text::_($key)));
}
return $options;
}
/**
* Creates a list of maps.
*
* @return array An array containing the maps that can be selected.
*
* @since 2.5
*/
public function mapslist()
{
// Load the finder types.
$db = $this->getDatabase();
$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) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
}
// Translate.
$lang = Factory::getLanguage();
foreach ($branches as $branch) {
$key = LanguageHelper::branchPlural($branch->text);
$branch->translatedText = $lang->hasKey($key) ? Text::_($key) : $branch->text;
}
// Order by title.
$branches = ArrayHelper::sortObjects($branches, 'translatedText', 1, true, true);
// Compile the options.
$options = [];
$options[] = HTMLHelper::_('select.option', '', Text::_('COM_FINDER_MAPS_SELECT_BRANCH'));
// Convert the values to options.
foreach ($branches as $branch) {
$options[] = HTMLHelper::_('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 [
HTMLHelper::_('select.option', '1', Text::sprintf('COM_FINDER_ITEM_X_ONLY', Text::_('JPUBLISHED'))),
HTMLHelper::_('select.option', '0', Text::sprintf('COM_FINDER_ITEM_X_ONLY', Text::_('JUNPUBLISHED'))),
];
}
}

View File

@ -0,0 +1,156 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
use Joomla\Component\Finder\Administrator\Indexer\Query as IndexerQuery;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Query HTML behavior class for Finder.
*
* @since 2.5
*/
class Query
{
/**
* Method to get the explained (human-readable) search query.
*
* @param IndexerQuery $query A IndexerQuery object to explain.
*
* @return mixed String if there is data to explain, null otherwise.
*
* @since 2.5
*/
public static function explained(IndexerQuery $query)
{
$parts = [];
// Process the required tokens.
foreach ($query->included as $token) {
if ($token->required && (!isset($token->derived) || $token->derived == false)) {
$parts[] = '<span class="query-required">' . Text::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">' . Text::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">' . Text::sprintf('COM_FINDER_QUERY_TOKEN_EXCLUDED', $token->term) . '</span>';
}
}
// Process the start date.
if ($query->date1) {
$date = Factory::getDate($query->date1)->format(Text::_('DATE_FORMAT_LC'));
$datecondition = Text::_('COM_FINDER_QUERY_DATE_CONDITION_' . strtoupper($query->when1));
$parts[] = '<span class="query-start-date">' . Text::sprintf('COM_FINDER_QUERY_START_DATE', $datecondition, $date) . '</span>';
}
// Process the end date.
if ($query->date2) {
$date = Factory::getDate($query->date2)->format(Text::_('DATE_FORMAT_LC'));
$datecondition = Text::_('COM_FINDER_QUERY_DATE_CONDITION_' . strtoupper($query->when2));
$parts[] = '<span class="query-end-date">' . Text::sprintf('COM_FINDER_QUERY_END_DATE', $datecondition, $date) . '</span>';
}
// Process the taxonomy filters.
if (!empty($query->filters)) {
// Get the filters in the request.
$t = Factory::getApplication()->getInput()->request->get('t', [], 'array');
// Process the taxonomy branches.
foreach ($query->filters as $branch => $nodes) {
// Process the taxonomy nodes.
$lang = Factory::getLanguage();
foreach ($nodes as $title => $id) {
// Translate the title for Types
$key = LanguageHelper::branchPlural($title);
if ($lang->hasKey($key)) {
$title = Text::_($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">'
. Text::sprintf('COM_FINDER_QUERY_TAXONOMY_NODE', $title, Text::_(LanguageHelper::branchSingular($branch)))
. '</span>';
}
}
}
// Build the interpreted query.
return \count($parts) ? implode(Text::_('COM_FINDER_QUERY_TOKEN_GLUE'), $parts) : null;
}
/**
* Method to get the suggested search query.
*
* @param IndexerQuery $query A IndexerQuery object.
*
* @return mixed String if there is a suggestion, false otherwise.
*
* @since 2.5
*/
public static function suggested(IndexerQuery $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;
}
}

View File

@ -0,0 +1,173 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Table;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Table\Table;
use Joomla\CMS\User\CurrentUserInterface;
use Joomla\CMS\User\CurrentUserTrait;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filter table class for the Finder package.
*
* @since 2.5
*/
class FilterTable extends Table implements CurrentUserInterface
{
use CurrentUserTrait;
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since 4.0.0
*/
protected $_supportNullValue = true;
/**
* Ensure the params are json encoded in the bind method
*
* @var array
* @since 4.0.0
*/
protected $_jsonEncode = ['params'];
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 2.5
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__finder_filters', 'filter_id', $db, $dispatcher);
$this->setColumnAlias('published', 'state');
}
/**
* Method to perform sanity checks on the \Joomla\CMS\Table\Table 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()
{
try {
parent::check();
} catch (\Exception $e) {
$this->setError($e->getMessage());
return false;
}
if (trim($this->alias) === '') {
$this->alias = $this->title;
}
$this->alias = ApplicationHelper::stringURLSafe($this->alias);
if (trim(str_replace('-', '', $this->alias)) === '') {
$this->alias = Factory::getDate()->format('Y-m-d-H-i-s');
}
$params = new Registry($this->params);
$d1 = $params->get('d1', '');
$d2 = $params->get('d2', '');
// Check the end date is not earlier than the start date.
if (!empty($d1) && !empty($d2) && $d2 < $d1) {
// Swap the dates.
$params->set('d1', $d2);
$params->set('d2', $d1);
$this->params = (string) $params;
}
return true;
}
/**
* Method to store a row in the database from the \Joomla\CMS\Table\Table 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
* \Joomla\CMS\Table\Table 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 = true)
{
$date = Factory::getDate()->toSql();
$userId = $this->getCurrentUser()->id;
// Set created date if not set.
if (!(int) $this->created) {
$this->created = $date;
}
if ($this->filter_id) {
// Existing item
$this->modified_by = $userId;
$this->modified = $date;
} else {
if (empty($this->created_by)) {
$this->created_by = $userId;
}
if (!(int) $this->modified) {
$this->modified = $this->created;
}
if (empty($this->modified_by)) {
$this->modified_by = $this->created_by;
}
}
if (\is_array($this->data)) {
$this->map_count = \count($this->data);
$this->data = implode(',', $this->data);
} else {
$this->map_count = 0;
$this->data = implode(',', []);
}
// Verify that the alias is unique
$table = new self($this->getDbo(), $this->getDispatcher());
if ($table->load(['alias' => $this->alias]) && ($table->filter_id != $this->filter_id || $this->filter_id == 0)) {
$this->setError(Text::_('COM_FINDER_FILTER_ERROR_UNIQUE_ALIAS'));
return false;
}
return parent::store($updateNulls);
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Table;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Link table class for the Finder package.
*
* @since 2.5
*/
class LinkTable extends Table
{
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since 4.0.0
*/
protected $_supportNullValue = true;
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 2.5
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__finder_links', 'link_id', $db, $dispatcher);
}
/**
* Overloaded store function
*
* @param boolean $updateNulls True to update fields even if they are null.
*
* @return mixed False on failure, positive integer on success.
*
* @see Table::store()
* @since 4.0.0
*/
public function store($updateNulls = true)
{
return parent::store($updateNulls);
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\Table;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Table\Nested;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Map table class for the Finder package.
*
* @since 2.5
*/
class MapTable extends Nested
{
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 2.5
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__finder_taxonomy', 'id', $db, $dispatcher);
$this->setColumnAlias('published', 'state');
$this->access = (int) Factory::getApplication()->get('access');
}
/**
* Override check function
*
* @return boolean
*
* @see Table::check()
* @since 4.0.0
*/
public function check()
{
try {
parent::check();
} catch (\Exception $e) {
$this->setError($e->getMessage());
return false;
}
// Check for a title.
if (trim($this->title) == '') {
$this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_CATEGORY'));
return false;
}
$this->alias = ApplicationHelper::stringURLSafe($this->title, $this->language);
if (trim($this->alias) == '') {
$this->alias = md5(serialize($this->getProperties()));
}
return true;
}
}

View File

@ -0,0 +1,178 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Filter;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filter view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* The filter object
*
* @var \Joomla\Component\Finder\Administrator\Table\FilterTable
*
* @since 3.6.2
*/
protected $filter;
/**
* The Form object
*
* @var \Joomla\CMS\Form\Form
*
* @since 3.6.2
*/
protected $form;
/**
* The active item
*
* @var CMSObject|boolean
*
* @since 3.6.2
*/
protected $item;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*
* @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 void
*
* @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 GenericDataException(implode("\n", $errors), 500);
}
// Configure the toolbar.
$this->addToolbar();
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 2.5
*/
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
$isNew = ($this->item->filter_id == 0);
$checkedOut = !(\is_null($this->item->checked_out) || $this->item->checked_out == $this->getCurrentUser()->id);
$canDo = ContentHelper::getActions('com_finder');
$toolbar = Toolbar::getInstance();
// Configure the toolbar.
ToolbarHelper::title(
$isNew ? Text::_('COM_FINDER_FILTER_NEW_TOOLBAR_TITLE') : Text::_('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')) {
$toolbar->apply('filter.apply');
$saveGroup = $toolbar->dropdownButton('save-group');
$saveGroup->configure(
function (Toolbar $childBar) {
$childBar->save('filter.save');
$childBar->save2new('filter.save2new');
}
);
}
$toolbar->cancel('filter.cancel', 'JTOOLBAR_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')) {
$toolbar->apply('filter.apply');
}
$saveGroup = $toolbar->dropdownButton('save-group');
$saveGroup->configure(
function (Toolbar $childBar) use ($checkedOut, $canDo) {
// 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')) {
$childBar->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')) {
$childBar->save2new('filter.save2new');
}
}
// If an existing item, can save as a copy
if ($canDo->get('core.create')) {
$childBar->save2copy('filter.save2copy');
}
}
);
$toolbar->cancel('filter.cancel');
}
$toolbar->divider();
$toolbar->help('Smart_Search:_New_or_Edit_Filter');
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Filters;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Filters view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*
* @since 3.6.1
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*
* @since 3.6.1
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*
* @since 3.6.1
*/
protected $state;
/**
* The total number of items
*
* @var integer
*
* @since 3.6.1
*/
protected $total;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*
* @since 4.0.0
*/
public $activeFilters;
/**
* @var boolean
*
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @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');
if (\count($this->items) === 0 && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Configure the toolbar.
$this->addToolbar();
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 2.5
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_finder');
$toolbar = Toolbar::getInstance();
ToolbarHelper::title(Text::_('COM_FINDER_FILTERS_TOOLBAR_TITLE'), 'search-plus finder');
if ($canDo->get('core.create')) {
$toolbar->addNew('filter.add');
$toolbar->divider();
}
if ($this->isEmptyState === false) {
if ($canDo->get('core.edit.state')) {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('status-group', 'JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('filters.publish')->listCheck(true);
$childBar->unpublish('filters.unpublish')->listCheck(true);
$childBar->checkin('filters.checkin')->listCheck(true);
}
if ($canDo->get('core.delete')) {
$toolbar->standardButton('delete', 'JTOOLBAR_DELETE', 'filters.delete')
->listCheck(true);
$toolbar->divider();
}
$toolbar->divider();
$toolbar->popupButton('bars', 'COM_FINDER_STATISTICS')
->url('index.php?option=com_finder&view=statistics&tmpl=component')
->iframeWidth(550)
->iframeHeight(350)
->title(Text::_('COM_FINDER_STATISTICS_TITLE'))
->icon('icon-bars');
$toolbar->divider();
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_finder');
}
$toolbar->help('Smart_Search:_Search_Filters');
}
}

View File

@ -0,0 +1,264 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Index;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\Component\Finder\Administrator\Helper\FinderHelper;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Index view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*
* @since 3.6.1
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*
* @since 3.6.1
*/
protected $pagination;
/**
* The state of core Smart Search plugins
*
* @var array
*
* @since 3.6.1
*/
protected $pluginState;
/**
* The id of the content - finder plugin in mysql
*
* @var integer
*
* @since 4.0.0
*/
protected $finderPluginId = 0;
/**
* The model state
*
* @var mixed
*
* @since 3.6.1
*/
protected $state;
/**
* The total number of items
*
* @var integer
*
* @since 3.6.1
*/
protected $total;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*
* @since 4.0.0
*/
public $activeFilters;
/**
* @var mixed
*
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @since 2.5
*/
public function display($tpl = null)
{
// Load plugin language files.
LanguageHelper::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');
if ($this->get('TotalIndexed') === 0 && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
// We do not need to filter by language when multilingual is disabled
if (!Multilanguage::isEnabled()) {
unset($this->activeFilters['language']);
$this->filterForm->removeField('language', 'filter');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Check that the content - finder plugin is enabled
if (!PluginHelper::isEnabled('content', 'finder')) {
$this->finderPluginId = FinderHelper::getFinderPluginId();
}
// Configure the toolbar.
$this->addToolbar();
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 2.5
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_finder');
$toolbar = Toolbar::getInstance();
ToolbarHelper::title(Text::_('COM_FINDER_INDEX_TOOLBAR_TITLE'), 'search-plus finder');
if (JDEBUG) {
$dropdown = $toolbar->dropdownButton('indexing-group');
$dropdown->text('COM_FINDER_INDEX')
->toggleSplit(false)
->icon('icon-archive')
->buttonClass('btn btn-action');
$childBar = $dropdown->getChildToolbar();
$childBar->popupButton('index', 'COM_FINDER_INDEX')
->popupType('iframe')
->textHeader(Text::_('COM_FINDER_HEADING_INDEXER'))
->url('index.php?option=com_finder&view=indexer&tmpl=component')
->modalWidth('800px')
->modalHeight('400px')
->icon('icon-archive')
->title(Text::_('COM_FINDER_HEADING_INDEXER'));
$childBar->linkButton('indexdebug', 'COM_FINDER_INDEX_TOOLBAR_INDEX_DEBUGGING')
->url('index.php?option=com_finder&view=indexer&layout=debug')
->icon('icon-tools');
} else {
$toolbar->popupButton('index', 'COM_FINDER_INDEX')
->popupType('iframe')
->textHeader(Text::_('COM_FINDER_HEADING_INDEXER'))
->url('index.php?option=com_finder&view=indexer&tmpl=component')
->modalWidth('800px')
->modalHeight('400px')
->icon('icon-archive')
->title(Text::_('COM_FINDER_HEADING_INDEXER'));
}
if (!$this->isEmptyState) {
if ($canDo->get('core.edit.state')) {
$dropdown = $toolbar->dropdownButton('status-group')
->text('JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('index.publish')->listCheck(true);
$childBar->unpublish('index.unpublish')->listCheck(true);
}
if ($canDo->get('core.delete')) {
$toolbar->confirmButton('delete', 'JTOOLBAR_DELETE', 'index.delete')
->message('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT')
->icon('icon-delete')
->listCheck(true);
$toolbar->divider();
}
if ($canDo->get('core.edit.state')) {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('maintenance-group', 'COM_FINDER_INDEX_TOOLBAR_MAINTENANCE')
->toggleSplit(false)
->icon('icon-wrench')
->buttonClass('btn btn-action');
$childBar = $dropdown->getChildToolbar();
$childBar->standardButton('cog', 'COM_FINDER_INDEX_TOOLBAR_OPTIMISE', 'index.optimise');
$childBar->confirmButton('index-purge', 'COM_FINDER_INDEX_TOOLBAR_PURGE', 'index.purge')
->message('COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT')
->icon('icon-trash');
}
$toolbar->popupButton('statistics', 'COM_FINDER_STATISTICS')
->popupType('iframe')
->textHeader(Text::_('COM_FINDER_STATISTICS_TITLE'))
->url('index.php?option=com_finder&view=statistics&tmpl=component')
->modalWidth('800px')
->modalHeight('500px')
->title(Text::_('COM_FINDER_STATISTICS_TITLE'))
->icon('icon-bars');
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_finder');
}
$toolbar->help('Smart_Search:_Indexed_Content');
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Indexer;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Indexer view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* @var Form $form
*
* @since 5.0.0
*/
public $form;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @since 5.0.0
*/
public function display($tpl = null)
{
if ($this->getLayout() == 'debug') {
$this->form = $this->get('Form');
$this->addToolbar();
}
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 5.0.0
*/
protected function addToolbar()
{
/** @var Toolbar $toolbar */
$toolbar = $this->getDocument()->getToolbar();
ToolbarHelper::title(Text::_('COM_FINDER_INDEXER_TOOLBAR_TITLE'), 'search-plus finder');
$toolbar->linkButton('back', 'JTOOLBAR_BACK')
->icon('icon-arrow-' . ($this->getLanguage()->isRtl() ? 'right' : 'left'))
->url(Route::_('index.php?option=com_finder&view=index'));
$toolbar->standardButton('index', 'COM_FINDER_INDEX')
->icon('icon-play')
->onclick('Joomla.debugIndexing();');
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Finder\Administrator\View\Item;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
/**
* Index view class for Finder.
*
* @since 5.0.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The indexed item
*
* @var object
*
* @since 5.0.0
*/
protected $item;
/**
* The associated terms
*
* @var object[]
*
* @since 5.0.0
*/
protected $terms;
/**
* The associated taxonomies
*
* @var object[]
*
* @since 5.0.0
*/
protected $taxonomies;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @since 5.0.0
*/
public function display($tpl = null)
{
$this->item = $this->get('Item');
$this->terms = $this->get('Terms');
$this->taxonomies = $this->get('Taxonomies');
// Configure the toolbar.
$this->addToolbar();
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 5.0.0
*/
protected function addToolbar()
{
/** @var Toolbar $toolbar */
$toolbar = $this->getDocument()->getToolbar();
ToolbarHelper::title(Text::_('COM_FINDER_INDEX_TOOLBAR_TITLE'), 'search-plus finder');
$toolbar->linkButton('back', 'JTOOLBAR_BACK')
->icon('icon-arrow-' . ($this->getLanguage()->isRtl() ? 'right' : 'left'))
->url(Route::_('index.php?option=com_finder&view=index'));
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Maps;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Groups view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*
* @since 3.6.1
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*
* @since 3.6.1
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*
* @since 3.6.1
*/
protected $state;
/**
* The total number of items
*
* @var integer
*
* @since 3.6.1
*/
protected $total;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*
* @since 4.0.0
*/
public $activeFilters;
/**
* @var boolean
*
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @since 2.5
*/
public function display($tpl = null)
{
// Load plugin language files.
LanguageHelper::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');
if ($this->total === 0 && $this->isEmptyState = $this->get('isEmptyState')) {
$this->setLayout('emptystate');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Prepare the view.
$this->addToolbar();
parent::display($tpl);
}
/**
* Method to configure the toolbar for this view.
*
* @return void
*
* @since 2.5
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_finder');
$toolbar = Toolbar::getInstance();
ToolbarHelper::title(Text::_('COM_FINDER_MAPS_TOOLBAR_TITLE'), 'search-plus finder');
if (!$this->isEmptyState) {
if ($canDo->get('core.edit.state')) {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('status-group', 'JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('maps.publish')->listCheck(true);
$childBar->unpublish('maps.unpublish')->listCheck(true);
}
if ($canDo->get('core.delete')) {
$toolbar->standardButton('delete', 'JTOOLBAR_DELETE', 'maps.delete')
->listCheck(true);
$toolbar->divider();
}
$toolbar->divider();
$toolbar->popupButton('bars', 'COM_FINDER_STATISTICS')
->popupType('iframe')
->textHeader(Text::_('COM_FINDER_STATISTICS_TITLE'))
->url('index.php?option=com_finder&view=statistics&tmpl=component')
->modalWidth('800px')
->modalHeight('500px')
->title(Text::_('COM_FINDER_STATISTICS_TITLE'))
->icon('icon-bars');
$toolbar->divider();
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_finder');
}
$toolbar->help('Smart_Search:_Content_Maps');
}
}

View File

@ -0,0 +1,173 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Searches;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
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\Router\Route;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\CMS\Uri\Uri;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View class for a list of search terms.
*
* @since 4.0.0
*/
class HtmlView extends BaseHtmlView
{
/**
* True if gathering search statistics is enabled
*
* @var boolean
*/
protected $enabled;
/**
* An array of items
*
* @var array
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*
* @since 4.0.0
*/
public $activeFilters;
/**
* The actions the user is authorised to perform
*
* @var \Joomla\Registry\Registry
*
* @since 4.0.0
*/
protected $canDo;
/**
* @var boolean
*
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Display the view.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
$app = Factory::getApplication();
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->enabled = $this->state->params->get('gather_search_statistics', 0);
$this->canDo = ContentHelper::getActions('com_finder');
$uri = Uri::getInstance();
$link = 'index.php?option=com_config&view=component&component=com_finder&return=' . base64_encode($uri);
$output = HTMLHelper::_('link', Route::_($link), Text::_('JOPTIONS'));
if (!\count($this->items) && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Check if component is enabled
if (!$this->enabled) {
// Check if the user has access to the component options
if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) {
$app->enqueueMessage(Text::sprintf('COM_FINDER_LOGGING_DISABLED', $output), 'warning');
} else {
$app->enqueueMessage(Text::_('COM_FINDER_LOGGING_DISABLED_NO_AUTH'), 'warning');
}
}
// Prepare the view.
$this->addToolbar();
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*/
protected function addToolbar()
{
$canDo = $this->canDo;
$toolbar = Toolbar::getInstance();
ToolbarHelper::title(Text::_('COM_FINDER_MANAGER_SEARCHES'), 'search');
if (!$this->isEmptyState) {
if ($canDo->get('core.edit.state')) {
$toolbar->standardButton('reset', 'JSEARCH_RESET', 'searches.reset')
->icon('icon-refresh')
->listCheck(false);
}
$toolbar->divider();
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_finder');
}
$toolbar->help('Smart_Search:_Search_Term_Analysis');
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @package Joomla.Administrator
* @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\Administrator\View\Statistics;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Statistics view class for Finder.
*
* @since 2.5
*/
class HtmlView extends BaseHtmlView
{
/**
* The index statistics
*
* @var \Joomla\CMS\Object\CMSObject
*
* @since 3.6.1
*/
protected $data;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @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 GenericDataException(implode("\n", $errors), 500);
}
parent::display($tpl);
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* @package Joomla.Administrator
* @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\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
Text::script('COM_FINDER_FILTER_SHOW_ALL', true);
Text::script('COM_FINDER_FILTER_HIDE_ALL', true);
$this->ignore_fieldsets = ['jbasic'];
$this->useCoreUI = true;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate')
->useScript('com_finder.finder-edit');
?>
<form action="<?php echo Route::_('index.php?option=com_finder&view=filter&layout=edit&filter_id=' . (int) $this->item->filter_id); ?>" method="post" name="adminForm" id="adminForm" aria-label="<?php echo Text::_('COM_FINDER_FILTER_FORM_TITLE_' . ((int) $this->item->filter_id === 0 ? 'NEW' : 'EDIT'), true); ?>" class="form-validate">
<?php echo LayoutHelper::render('joomla.edit.title_alias', $this); ?>
<div class="main-card">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'details', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', Text::_('COM_FINDER_EDIT_FILTER')); ?>
<div class="row">
<div class="col-lg-9">
<?php if ($this->total > 0) : ?>
<div class="well">
<?php echo $this->form->renderField('map_count'); ?>
</div>
<button class="btn btn-secondary filter-toggle-all" type="button">
<span class="icon-square" aria-hidden="true"></span> <?php echo Text::_('JGLOBAL_SELECTION_INVERT'); ?></button>
<button class="btn btn-secondary float-end" type="button" id="expandAccordion"><?php echo Text::_('COM_FINDER_FILTER_SHOW_ALL'); ?></button>
<hr>
<?php endif; ?>
<?php echo HTMLHelper::_('filter.slider', ['selected_nodes' => $this->filter->data]); ?>
</div>
<div class="col-lg-3">
<?php echo LayoutHelper::render('joomla.edit.global', $this); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'publishing', Text::_('JGLOBAL_FIELDSET_OPTIONS')); ?>
<div class="row">
<div class="col-md-6">
<fieldset id="fieldset-publishingdata" class="options-form">
<legend><?php echo Text::_('JGLOBAL_FIELDSET_PUBLISHING'); ?></legend>
<div>
<?php echo LayoutHelper::render('joomla.edit.publishingdata', $this); ?>
</div>
</fieldset>
</div>
<div class="col-md-6">
<fieldset id="fieldset-filter" class="options-form">
<legend><?php echo Text::_('COM_FINDER_FILTER_FIELDSET_PARAMS'); ?></legend>
<div>
<?php echo $this->form->renderFieldset('jbasic'); ?>
</div>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo LayoutHelper::render('joomla.edit.params', $this); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<input type="hidden" name="task" value="">
<input type="hidden" name="return" value="<?php echo Factory::getApplication()->getInput()->get('return', '', 'BASE64'); ?>">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</form>

View File

@ -0,0 +1,131 @@
<?php
/**
* @package Joomla.Administrator
* @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\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
$user = $this->getCurrentUser();
$userId = $user->get('id');
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
Text::script('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT');
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('com_finder.filters')
->useScript('table.columns')
->useScript('multiselect');
?>
<form action="<?php echo Route::_('index.php?option=com_finder&view=filters'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]); ?>
<?php if (empty($this->items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_FINDER_NO_RESULTS_OR_FILTERS'); ?>
</div>
<?php else : ?>
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_FILTERS_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<th scope="col" class="w-1 text-center">
<?php echo HTMLHelper::_('searchtools.sort', 'JSTATUS', 'a.state', $listDirn, $listOrder); ?>
</th>
<th scope="col">
<?php echo HTMLHelper::_('searchtools.sort', 'JGLOBAL_TITLE', 'a.title', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_HEADING_CREATED_BY', 'a.created_by_alias', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_HEADING_CREATED_ON', 'a.created', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-5 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_HEADING_MAP_COUNT', 'a.map_count', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-1 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_ID', 'a.filter_id', $listDirn, $listOrder); ?>
</th>
</tr>
</thead>
<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->id;
foreach ($this->items as $i => $item) :
$canCheckIn = $userAuthoriseCoreManage || $item->checked_out == $userId || is_null($item->checked_out);
$canChange = $userAuthoriseCoreEditState && $canCheckIn;
$escapedTitle = $this->escape($item->title);
?>
<tr class="row<?php echo $i % 2; ?>">
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->filter_id, false, 'cid', 'cb', $item->title); ?>
</td>
<td class="text-center">
<?php echo HTMLHelper::_('jgrid.published', $item->state, $i, 'filters.', $canChange); ?>
</td>
<th scope="row">
<?php if ($item->checked_out) : ?>
<?php echo HTMLHelper::_('jgrid.checkedout', $i, $item->editor, $item->checked_out_time, 'filters.', $canCheckIn); ?>
<?php endif; ?>
<?php if ($canEdit) : ?>
<a href="<?php echo Route::_('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; ?>
</th>
<td class="d-none d-md-table-cell">
<?php echo $item->created_by_alias ?: $item->user_name; ?>
</td>
<td class="d-none d-md-table-cell">
<?php echo HTMLHelper::_('date', $item->created, Text::_('DATE_FORMAT_LC4')); ?>
</td>
<td class="d-none d-md-table-cell">
<?php echo $item->map_count; ?>
</td>
<td class="d-none d-md-table-cell">
<?php echo (int) $item->filter_id; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php echo $this->pagination->getListFooter(); ?>
<?php endif; ?>
<input type="hidden" name="task" value="">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_FINDER_FILTERS_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_FINDER_FILTERS_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,30 @@
<?php
/**
* @package Joomla.Administrator
* @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\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
$displayData = [
'textPrefix' => 'COM_FINDER',
'formURL' => 'index.php?option=com_finder&view=filters',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Smart_Search_quickstart_guide',
'icon' => 'icon-search-plus finder',
'btnadd' => Text::_('COM_FINDER_FILTERS_EMPTYSTATE_BUTTON_ADD'),
'content' => Text::_('COM_FINDER_FILTERS_EMPTYSTATE_CONTENT'),
'title' => Text::_('COM_FINDER_FILTERS_TOOLBAR_TITLE'),
];
if ($this->getCurrentUser()->authorise('core.create', 'com_finder')) {
$displayData['createURL'] = "index.php?option=com_finder&task=filter.add";
}
echo LayoutHelper::render('joomla.content.emptystate', $displayData);

View File

@ -0,0 +1,169 @@
<?php
/**
* @package Joomla.Administrator
* @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\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$lang = $this->getLanguage();
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('multiselect')
->useScript('table.columns');
?>
<form action="<?php echo Route::_('index.php?option=com_finder&view=index'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]); ?>
<?php if ($this->finderPluginId) : ?>
<?php $link = Route::_('index.php?option=com_plugins&client_id=0&task=plugin.edit&extension_id=' . $this->finderPluginId . '&tmpl=component&layout=modal'); ?>
<?php echo HTMLHelper::_(
'bootstrap.renderModal',
'plugin' . $this->finderPluginId . 'Modal',
[
'url' => $link,
'title' => Text::_('COM_FINDER_EDIT_PLUGIN_SETTINGS'),
'height' => '400px',
'width' => '800px',
'bodyHeight' => '70',
'modalWidth' => '80',
'closeButton' => false,
'backdrop' => 'static',
'keyboard' => false,
'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"'
. ' onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#closeBtn\'})">'
. Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>'
. '<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#saveBtn\'})">'
. Text::_("JSAVE") . '</button>'
. '<button type="button" class="btn btn-success" onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#applyBtn\'}); return false;">'
. Text::_("JAPPLY") . '</button>'
]
); ?>
<?php endif; ?>
<?php if (empty($this->items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
</div>
<?php else : ?>
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_INDEX_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<th scope="col" class="w-1 text-center">
<?php echo HTMLHelper::_('searchtools.sort', 'JSTATUS', 'l.published', $listDirn, $listOrder); ?>
</th>
<th scope="col">
<?php echo HTMLHelper::_('searchtools.sort', 'JGLOBAL_TITLE', 'l.title', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_INDEX_HEADING_INDEX_TYPE', 't.title', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell text-center">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_INDEX_HEADING_INDEX_DATE', 'l.indexdate', $listDirn, $listOrder); ?>
</th>
<?php if (Multilanguage::isEnabled()) : ?>
<th scope="col" class="w-10 nowrap d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_LANGUAGE', 'l.language', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
<th scope="col" class="w-15 d-none d-md-table-cell text-center">
<?php echo Text::_('COM_FINDER_INDEX_HEADING_DETAILS'); ?>
</th>
<th scope="col" class="w-30 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_INDEX_HEADING_LINK_URL', 'l.url', $listDirn, $listOrder); ?>
</th>
</tr>
</thead>
<tbody>
<?php $canChange = $this->getCurrentUser()->authorise('core.manage', 'com_finder'); ?>
<?php foreach ($this->items as $i => $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->link_id, false, 'cid', 'cb', $item->title); ?>
</td>
<td class="text-center">
<?php echo HTMLHelper::_('jgrid.published', $item->published, $i, 'index.', $canChange, 'cb'); ?>
</td>
<th scope="row">
<?php if (JDEBUG) : ?>
<a href="index.php?option=com_finder&view=item&id=<?php echo $item->link_id; ?>">
<?php echo $this->escape($item->title); ?>
</a>
<?php else : ?>
<?php echo $this->escape($item->title); ?>
<?php endif; ?>
</th>
<td class="small d-none d-md-table-cell">
<?php
$key = LanguageHelper::branchSingular($item->t_title);
echo $lang->hasKey($key) ? Text::_($key) : $item->t_title;
?>
</td>
<td class="small d-none d-md-table-cell text-center">
<?php echo HTMLHelper::_('date', $item->indexdate, Text::_('DATE_FORMAT_LC4')); ?>
</td>
<?php if (Multilanguage::isEnabled()) : ?>
<td class="small d-none d-md-table-cell">
<?php echo LayoutHelper::render('joomla.content.language', $item); ?>
</td>
<?php endif; ?>
<td class="text-center d-none d-md-table-cell text-center">
<?php if ((int) $item->publish_start_date or (int) $item->publish_end_date or (int) $item->start_date or (int) $item->end_date) : ?>
<span tabindex="0">
<span class="icon-calendar" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('COM_FINDER_INDEX_DATE_INFO_TITLE'); ?></span>
</span>
<div role="tooltip" id="tip<?php echo $i; ?>">
<?php
$publishStartDate = $item->publish_start_date !== null ? HTMLHelper::_('date', $item->publish_start_date, Text::_('DATE_FORMAT_LC5'), 'UTC') : '';
$publishEndDate = $item->publish_end_date !== null ? HTMLHelper::_('date', $item->publish_end_date, Text::_('DATE_FORMAT_LC5'), 'UTC') : '';
$startDate = $item->start_date !== null ? HTMLHelper::_('date', $item->start_date, Text::_('DATE_FORMAT_LC5'), 'UTC') : '';
$endDate = $item->end_date !== null ? HTMLHelper::_('date', $item->end_date, Text::_('DATE_FORMAT_LC5'), 'UTC') : '';
?>
<?php echo Text::sprintf('COM_FINDER_INDEX_DATE_INFO', $publishStartDate, $publishEndDate, $startDate, $endDate); ?>
</div>
<?php endif; ?>
</td>
<td class="small break-word d-none d-md-table-cell">
<?php echo (strlen($item->url) > 80) ? substr($item->url, 0, 70) . '...' : $item->url; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php echo $this->pagination->getListFooter(); ?>
<?php endif; ?>
<input type="hidden" name="task" value="display">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_FINDER_INDEX_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_FINDER_INDEX_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,54 @@
<?php
/**
* @package Joomla.Administrator
* @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\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
$displayData = [
'textPrefix' => 'COM_FINDER',
'formURL' => 'index.php?option=com_finder&view=index',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Smart_Search_quickstart_guide',
'icon' => 'icon-search-plus finder',
'content' => Text::_('COM_FINDER_INDEX_NO_DATA') . '<br>' . Text::_('COM_FINDER_INDEX_TIP'),
'title' => Text::_('COM_FINDER_HEADING_INDEXER'),
'createURL' => "javascript:document.getElementsByClassName('button-index')[0].click();",
];
echo LayoutHelper::render('joomla.content.emptystate', $displayData);
if ($this->finderPluginId) : ?>
<?php $link = Route::_('index.php?option=com_plugins&client_id=0&task=plugin.edit&extension_id=' . $this->finderPluginId . '&tmpl=component&layout=modal'); ?>
<?php echo HTMLHelper::_(
'bootstrap.renderModal',
'plugin' . $this->finderPluginId . 'Modal',
[
'url' => $link,
'title' => Text::_('COM_FINDER_EDIT_PLUGIN_SETTINGS'),
'height' => '400px',
'width' => '800px',
'bodyHeight' => '70',
'modalWidth' => '80',
'closeButton' => false,
'backdrop' => 'static',
'keyboard' => false,
'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"'
. ' onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#closeBtn\'})">'
. Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>'
. '<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#saveBtn\'})">'
. Text::_("JSAVE") . '</button>'
. '<button type="button" class="btn btn-success" onclick="Joomla.iframeButtonClick({iframeSelector: \'#plugin' . $this->finderPluginId . 'Modal\', buttonSelector: \'#applyBtn\'}); return false;">'
. Text::_("JAPPLY") . '</button>'
]
); ?>
<?php endif;

View File

@ -0,0 +1,61 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 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;
/** @var Joomla\Component\Finder\Administrator\View\Indexer\HtmlView $this */
Text::script('COM_FINDER_INDEXER_MESSAGE_COMPLETE', true);
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('com_finder.debug');
?>
<form action="<?php echo Route::_('index.php?option=com_finder&layout=debug'); ?>" method="post" name="adminForm" id="debug-form">
<div class="form-horizontal">
<div class="card mt-3">
<div class="card-body">
<fieldset class="adminform p-4">
<div class="alert alert-info">
<h2 class="alert-heading"><?php echo Text::_('COM_FINDER_INDEXER_MSG_DEBUGGING_INDEXING'); ?></h2>
<?php echo Text::_('COM_FINDER_INDEXER_MSG_DEBUGGING_INDEXING_TEXT'); ?>
</div>
<?php echo $this->form->renderField('plugin'); ?>
<?php echo $this->form->renderField('id'); ?>
<input id="finder-indexer-token" type="hidden" name="<?php echo Factory::getSession()->getFormToken(); ?>" value="1">
</fieldset>
</div>
</div>
</div>
</form>
<div class="form-horizontal">
<div class="card mt-3">
<div class="card-body">
<fieldset class="adminform">
<legend><?php echo Text::_('COM_FINDER_INDEXER_OUTPUT_AREA_TITLE'); ?></legend>
<div id="indexer-output" class="border p-3" style="min-height:200px;">
</div>
</fieldset>
</div>
</div>
</div>

View File

@ -0,0 +1,41 @@
<?php
/**
* @package Joomla.Administrator
* @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;
Text::script('COM_FINDER_INDEXER_MESSAGE_COMPLETE');
Text::script('COM_FINDER_AN_ERROR_HAS_OCCURRED');
Text::script('COM_FINDER_MESSAGE_RETURNED');
Text::script('JLIB_JS_AJAX_ERROR_OTHER');
Text::script('JLIB_JS_AJAX_ERROR_PARSE');
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useStyle('com_finder.indexer')
->useScript('com_finder.indexer');
?>
<div class="text-center">
<h1 id="finder-progress-header" class="m-t-2" aria-live="assertive"><?php echo Text::_('COM_FINDER_INDEXER_HEADER_INIT'); ?></h1>
<p id="finder-progress-message" aria-live="polite"><?php echo Text::_('COM_FINDER_INDEXER_MESSAGE_INIT'); ?></p>
<div id="progress" class="progress">
<div id="progress-bar" class="progress-bar bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<?php if (JDEBUG) : ?>
<dl id="finder-debug-data" class="row">
</dl>
<?php endif; ?>
<input id="finder-indexer-token" type="hidden" name="<?php echo Factory::getSession()->getFormToken(); ?>" value="1">
</div>

View File

@ -0,0 +1,100 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_finder
*
* @copyright (C) 2022 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\Language\Text;
?>
<div role="main">
<h1 class="mb-3"><?php echo $this->item->title; ?></h1>
<div class="card mb-3">
<div class="card-header"><h2><?php echo Text::_('COM_FINDER_ITEM_FIELDSET_ITEM_TITLE'); ?></h2></div>
<div class="card-body">
<dl class="row">
<?php foreach ($this->item as $key => $value) : ?>
<dt class="col-sm-3"><?php echo $key; ?></dt>
<dd class="col-sm-9<?php echo $key == 'object' ? ' text-break' : '';?>"><?php echo $value; ?></dd>
<?php endforeach; ?>
</dl>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h2><?php echo Text::_('COM_FINDER_ITEM_FIELDSET_TERMS_TITLE'); ?></h2></div>
<div class="card-body">
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_ITEM_TERMS_TABLE_CAPTION'); ?>,
</caption>
<thead>
<tr>
<th scope="col">id</th>
<th scope="col">term</th>
<th scope="col">stem</th>
<th scope="col">common</th>
<th scope="col">phrase</th>
<th scope="col">weight</th>
<th scope="col">links</th>
<th scope="col">language</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->terms as $term) : ?>
<tr>
<th scope="row"><?php echo $term->term_id; ?></th>
<td><?php echo $term->term; ?></td>
<td><?php echo $term->stem; ?></td>
<td><?php echo $term->common; ?></td>
<td><?php echo $term->phrase; ?></td>
<td><?php echo $term->weight; ?></td>
<td><?php echo $term->links; ?></td>
<td><?php echo $term->language; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="card mb-3">
<div class="card-header"><h2><?php echo Text::_('COM_FINDER_ITEM_FIELDSET_TAXONOMIES_TITLE'); ?></h2></div>
<div class="card-body">
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_ITEM_TAXONOMIES_TABLE_CAPTION'); ?>,
</caption>
<thead>
<tr>
<th scope="col">id</th>
<th scope="col">title</th>
<th scope="col">alias</th>
<th scope="col">lft</th>
<th scope="col">path</th>
<th scope="col">state</th>
<th scope="col">access</th>
<th scope="col">language</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->taxonomies as $taxonomy) : ?>
<tr>
<th scope="row"><?php echo $taxonomy->id; ?></th>
<td><?php echo $taxonomy->title; ?></td>
<td><?php echo $taxonomy->alias; ?></td>
<td><?php echo $taxonomy->lft; ?></td>
<td><?php echo $taxonomy->path; ?></td>
<td><?php echo $taxonomy->state; ?></td>
<td><?php echo $taxonomy->access; ?></td>
<td><?php echo $taxonomy->language; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,174 @@
<?php
/**
* @package Joomla.Administrator
* @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\HTML\HTMLHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Finder\Administrator\Helper\LanguageHelper;
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$lang = Factory::getLanguage();
$branchFilter = $this->escape($this->state->get('filter.branch'));
Text::script('COM_FINDER_MAPS_CONFIRM_DELETE_PROMPT');
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('com_finder.maps')
->useScript('table.columns')
->useScript('multiselect');
?>
<form action="<?php echo Route::_('index.php?option=com_finder&view=maps'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]); ?>
<?php if (empty($this->items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_FINDER_MAPS_NO_CONTENT'); ?>
</div>
<?php else : ?>
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_MAPS_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<th scope="col" class="w-1 text-center">
<?php echo HTMLHelper::_('searchtools.sort', 'JSTATUS', 'a.state', $listDirn, $listOrder); ?>
</th>
<th scope="col">
<?php echo HTMLHelper::_('searchtools.sort', 'JGLOBAL_TITLE', 'branch_title, a.lft', $listDirn, $listOrder); ?>
</th>
<?php if (!$branchFilter) : ?>
<th scope="col" class="w-1 text-center">
<?php echo Text::_('COM_FINDER_HEADING_CHILDREN'); ?>
</th>
<?php endif; ?>
<th scope="col" class="w-1 text-center">
<span class="icon-check" aria-hidden="true"></span>
<span class="d-none d-md-inline"><?php echo Text::_('COM_FINDER_MAPS_COUNT_PUBLISHED_ITEMS'); ?></span>
</th>
<th scope="col" class="w-1 text-center">
<span class="icon-times" aria-hidden="true"></span>
<span class="d-none d-md-inline"><?php echo Text::_('COM_FINDER_MAPS_COUNT_UNPUBLISHED_ITEMS'); ?></span>
</th>
<?php if (Multilanguage::isEnabled()) : ?>
<th scope="col" class="w-10 nowrap d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_LANGUAGE', 'a.language', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php $canChange = $this->getCurrentUser()->authorise('core.manage', 'com_finder'); ?>
<?php foreach ($this->items as $i => $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->id, false, 'cid', 'cb', $item->title); ?>
</td>
<td class="text-center">
<?php echo HTMLHelper::_('jgrid.published', $item->state, $i, 'maps.', $canChange, 'cb'); ?>
</td>
<th scope="row">
<?php
if (trim($item->branch_title, '*') === 'Language') {
$title = LanguageHelper::branchLanguageTitle($item->title);
} else {
$key = LanguageHelper::branchSingular($item->title);
$title = $lang->hasKey($key) ? Text::_($key) : $item->title;
}
?>
<?php echo str_repeat('<span class="gi">&mdash;</span>', $item->level - 1); ?>
<?php echo $this->escape($title); ?>
<?php if ($this->escape(trim($title, '*')) === 'Language' && Multilanguage::isEnabled()) : ?>
<div class="small">
<strong><?php echo Text::_('COM_FINDER_MAPS_MULTILANG'); ?></strong>
</div>
<?php endif; ?>
</th>
<?php if (!$branchFilter) : ?>
<td class="text-center btns itemnumber">
<?php if ($item->rgt - $item->lft > 1) : ?>
<a href="<?php echo Route::_('index.php?option=com_finder&view=maps&filter[branch]=' . $item->id); ?>"
aria-describedby="tip-map<?php echo $i; ?>">
<span class="btn btn-info"><?php echo floor(($item->rgt - $item->lft) / 2); ?></span>
</a>
<div role="tooltip" id="tip-map<?php echo $i; ?>">
<?php echo Text::_('COM_FINDER_HEADING_CHILDREN'); ?>
</div>
<?php else : ?>
-
<?php endif; ?>
</td>
<?php endif; ?>
<td class="text-center btns itemnumber">
<?php if ($item->level > 1) : ?>
<a class="btn <?php echo ((int) $item->count_published > 0) ? 'btn-success' : 'btn-secondary'; ?>"
href="<?php echo Route::_('index.php?option=com_finder&view=index&filter[state]=1&filter[content_map]=' . $item->id); ?>"
aria-describedby="tip-publish<?php echo $i; ?>">
<?php echo (int) $item->count_published; ?>
</a>
<div role="tooltip" id="tip-publish<?php echo $i; ?>">
<?php echo Text::_('COM_FINDER_MAPS_COUNT_PUBLISHED_ITEMS'); ?>
</div>
<?php else : ?>
-
<?php endif; ?>
</td>
<td class="text-center btns itemnumber">
<?php if ($item->level > 1) : ?>
<a class="btn <?php echo ((int) $item->count_unpublished > 0) ? 'btn-danger' : 'btn-secondary'; ?>"
href="<?php echo Route::_('index.php?option=com_finder&view=index&filter[state]=0&filter[content_map]=' . $item->id); ?>"
aria-describedby="tip-unpublish<?php echo $i; ?>">
<?php echo (int) $item->count_unpublished; ?>
</a>
<div role="tooltip" id="tip-unpublish<?php echo $i; ?>">
<?php echo Text::_('COM_FINDER_MAPS_COUNT_UNPUBLISHED_ITEMS'); ?>
</div>
<?php else : ?>
-
<?php endif; ?>
</td>
<?php if (Multilanguage::isEnabled()) : ?>
<td class="small d-none d-md-table-cell">
<?php echo $item->language; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php echo $this->pagination->getListFooter(); ?>
<?php endif; ?>
</div>
<input type="hidden" name="task" value="display">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</div>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_FINDER_MAPS_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_FINDER_MAPS_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,24 @@
<?php
/**
* @package Joomla.Administrator
* @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\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
$displayData = [
'textPrefix' => 'COM_FINDER',
'formURL' => 'index.php?option=com_finder&view=maps',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Help4.x:Smart_Search:_Content_Maps',
'icon' => 'icon-search-plus finder',
'title' => Text::_('COM_FINDER_MAPS_TOOLBAR_TITLE')
];
echo LayoutHelper::render('joomla.content.emptystate', $displayData);

View File

@ -0,0 +1,83 @@
<?php
/**
* @package Joomla.Administrator
* @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
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('multiselect');
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>
<form action="<?php echo Route::_('index.php?option=com_finder&view=searches'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this, 'options' => ['filterButton' => false]]); ?>
<?php if (empty($this->items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
</div>
<?php else : ?>
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_FINDER_SEARCHES_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<th scope="col">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_FINDER_HEADING_PHRASE', 'a.searchterm', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-15">
<?php echo HTMLHelper::_('searchtools.sort', 'JGLOBAL_HITS', 'a.hits', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-1 text-center">
<?php echo Text::_('COM_FINDER_HEADING_RESULTS'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->items as $i => $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<th scope="row" class="break-word">
<?php echo $this->escape($item->searchterm); ?>
</th>
<td>
<?php echo (int) $item->hits; ?>
</td>
<td class="text-center btns">
<?php echo (int) $item->results; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php echo $this->pagination->getListFooter(); ?>
<?php endif; ?>
<input type="hidden" name="task" value="">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_FINDER_SEARCH_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_FINDER_SEARCH_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,25 @@
<?php
/**
* @package Joomla.Administrator
* @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\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
$displayData = [
'textPrefix' => 'COM_FINDER',
'formURL' => 'index.php?option=com_finder&view=searches',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Help4.x:Smart_Search:_Search_Term_Analysis',
'icon' => 'icon-search',
'title' => Text::_('COM_FINDER_MANAGER_SEARCHES'),
'content' => Text::_('COM_FINDER_EMPTYSTATE_SEARCHES_CONTENT'),
];
echo LayoutHelper::render('joomla.content.emptystate', $displayData);

View File

@ -0,0 +1,54 @@
<?php
/**
* @package Joomla.Administrator
* @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\Language\Text;
?>
<div class="container-popup">
<table class="table table-sm">
<caption class="caption-top"><?php echo Text::sprintf('COM_FINDER_STATISTICS_STATS_DESCRIPTION', number_format($this->data->term_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR')), number_format($this->data->link_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR')), number_format($this->data->taxonomy_node_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR')), number_format($this->data->taxonomy_branch_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR'))); ?></caption>
<thead>
<tr>
<th scope="col">
<?php echo Text::_('COM_FINDER_STATISTICS_LINK_TYPE_HEADING'); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_FINDER_STATISTICS_LINK_TYPE_COUNT'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->data->type_list as $type) : ?>
<tr>
<th scope="row">
<?php
$lang_key = 'PLG_FINDER_STATISTICS_' . str_replace(' ', '_', $type->type_title);
$lang_string = Text::_($lang_key);
echo $lang_string === $lang_key ? $type->type_title : $lang_string;
?>
</th>
<td>
<span class="badge bg-info"><?php echo number_format($type->link_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR')); ?></span>
</td>
</tr>
<?php endforeach; ?>
<tr>
<td>
<strong><?php echo Text::_('COM_FINDER_STATISTICS_LINK_TYPE_TOTAL'); ?></strong>
</td>
<td>
<span class="badge bg-info"><?php echo number_format($this->data->link_count, 0, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR')); ?></span>
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_FINDER_STATS_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_FINDER_STATS_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>