first commit
This commit is contained in:
72
plugins/content/confirmconsent/confirmconsent.xml
Normal file
72
plugins/content/confirmconsent/confirmconsent.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_confirmconsent</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2018-05</creationDate>
|
||||
<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.9.0</version>
|
||||
<description>PLG_CONTENT_CONFIRMCONSENT_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\ConfirmConsent</namespace>
|
||||
<files>
|
||||
<folder plugin="confirmconsent">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_confirmconsent.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_confirmconsent.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic" addfieldprefix="Joomla\Component\Content\Administrator\Field">
|
||||
<field
|
||||
name="consentbox_text"
|
||||
type="textarea"
|
||||
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_LABEL"
|
||||
description="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DESC"
|
||||
hint="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT"
|
||||
rows="7"
|
||||
cols="20"
|
||||
filter="html"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="privacy_type"
|
||||
type="list"
|
||||
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_LABEL"
|
||||
default="article"
|
||||
validate="options"
|
||||
>
|
||||
<option value="article">PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_ARTICLE</option>
|
||||
<option value="menu_item">PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_MENU_ITEM</option>
|
||||
</field>
|
||||
<field
|
||||
name="privacy_article"
|
||||
type="modal_article"
|
||||
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_LABEL"
|
||||
description="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_DESC"
|
||||
select="true"
|
||||
new="true"
|
||||
edit="true"
|
||||
clear="true"
|
||||
filter="integer"
|
||||
showon="privacy_type:article"
|
||||
/>
|
||||
<field
|
||||
addfieldprefix="Joomla\Component\Menus\Administrator\Field"
|
||||
name="privacy_menu_item"
|
||||
type="modal_menu"
|
||||
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_MENU_ITEM_LABEL"
|
||||
select="true"
|
||||
new="true"
|
||||
edit="true"
|
||||
clear="true"
|
||||
filter="integer"
|
||||
showon="privacy_type:menu_item"
|
||||
/>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
46
plugins/content/confirmconsent/services/provider.php
Normal file
46
plugins/content/confirmconsent/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.confirmconsent
|
||||
*
|
||||
* @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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\ConfirmConsent\Extension\ConfirmConsent;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new ConfirmConsent(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'confirmconsent')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.confirmconsent
|
||||
*
|
||||
* @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\Plugin\Content\ConfirmConsent\Extension;
|
||||
|
||||
use Joomla\CMS\Form\Form;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* The Joomla Core confirm consent plugin
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
final class ConfirmConsent extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Load the language file on instantiation.
|
||||
*
|
||||
* @var boolean
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* The supported form contexts
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $supportedContext = [
|
||||
'com_contact.contact',
|
||||
'com_privacy.request',
|
||||
];
|
||||
|
||||
/**
|
||||
* Add additional fields to the supported forms
|
||||
*
|
||||
* @param Form $form The form to be altered.
|
||||
* @param mixed $data The associated data for the form.
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function onContentPrepareForm(Form $form, $data)
|
||||
{
|
||||
if ($this->getApplication()->isClient('administrator') || !\in_array($form->getName(), $this->supportedContext)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the consent box Text & the selected privacyarticle
|
||||
$consentboxText = (string) $this->params->get(
|
||||
'consentbox_text',
|
||||
$this->getApplication()->getLanguage()->_('PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT')
|
||||
);
|
||||
$privacyArticle = $this->params->get('privacy_article', false);
|
||||
$privacyType = $this->params->get('privacy_type', 'article');
|
||||
$privacyMenuItem = $this->params->get('privacy_menu_item', false);
|
||||
|
||||
$form->load('
|
||||
<form>
|
||||
<fieldset name="default" addfieldprefix="Joomla\\Plugin\\Content\\ConfirmConsent\\Field">
|
||||
<field
|
||||
name="consentbox"
|
||||
type="ConsentBox"
|
||||
articleid="' . $privacyArticle . '"
|
||||
menu_item_id="' . $privacyMenuItem . '"
|
||||
privacy_type="' . $privacyType . '"
|
||||
label="PLG_CONTENT_CONFIRMCONSENT_CONSENTBOX_LABEL"
|
||||
required="true"
|
||||
>
|
||||
<option value="0">' . htmlspecialchars($consentboxText, ENT_COMPAT, 'UTF-8') . '</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
339
plugins/content/confirmconsent/src/Field/ConsentBoxField.php
Normal file
339
plugins/content/confirmconsent/src/Field/ConsentBoxField.php
Normal file
@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.confirmconsent
|
||||
*
|
||||
* @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\Plugin\Content\ConfirmConsent\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\Field\CheckboxesField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Associations;
|
||||
use Joomla\CMS\Language\Multilanguage;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Content\Site\Helper\RouteHelper;
|
||||
use Joomla\Database\Exception\ExecutionFailureException;
|
||||
use Joomla\Database\ParameterType;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Consentbox Field class for the Confirm Consent Plugin.
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
class ConsentBoxField extends CheckboxesField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.1
|
||||
*/
|
||||
protected $type = 'ConsentBox';
|
||||
|
||||
/**
|
||||
* Flag to tell the field to always be in multiple values mode.
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.9.1
|
||||
*/
|
||||
protected $forceMultiple = false;
|
||||
|
||||
/**
|
||||
* The article ID.
|
||||
*
|
||||
* @var integer
|
||||
* @since 3.9.1
|
||||
*/
|
||||
protected $articleid;
|
||||
|
||||
/**
|
||||
* The menu item ID.
|
||||
*
|
||||
* @var integer
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $menuItemId;
|
||||
|
||||
/**
|
||||
* Type of the privacy policy.
|
||||
*
|
||||
* @var string
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $privacyType;
|
||||
|
||||
/**
|
||||
* Method to set certain otherwise inaccessible properties of the form field object.
|
||||
*
|
||||
* @param string $name The property name for which to set the value.
|
||||
* @param mixed $value The value of the property.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'articleid':
|
||||
$this->articleid = (int) $value;
|
||||
break;
|
||||
|
||||
default:
|
||||
parent::__set($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get certain otherwise inaccessible properties from the form field object.
|
||||
*
|
||||
* @param string $name The property name for which to get the value.
|
||||
*
|
||||
* @return mixed The property value or null.
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name == 'articleid') {
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
return parent::__get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to attach a JForm object to the field.
|
||||
*
|
||||
* @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object.
|
||||
* @param mixed $value The form field value to validate.
|
||||
* @param string $group The field name group control value. This acts as an array container for the field.
|
||||
* For example if the field has name="foo" and the group value is set to "bar" then the
|
||||
* full field name would end up being "bar[foo]".
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*
|
||||
* @see \Joomla\CMS\Form\FormField::setup()
|
||||
* @since 3.9.1
|
||||
*/
|
||||
public function setup(\SimpleXMLElement $element, $value, $group = null)
|
||||
{
|
||||
$return = parent::setup($element, $value, $group);
|
||||
|
||||
if ($return) {
|
||||
$this->articleid = (int) $this->element['articleid'];
|
||||
$this->menuItemId = (int) $this->element['menu_item_id'];
|
||||
$this->privacyType = (string) $this->element['privacy_type'];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the field label markup.
|
||||
*
|
||||
* @return string The field label markup.
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
protected function getLabel()
|
||||
{
|
||||
if ($this->hidden) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$data = $this->getLayoutData();
|
||||
|
||||
// Forcing the Alias field to display the tip below
|
||||
$position = $this->element['name'] == 'alias' ? ' data-bs-placement="bottom" ' : '';
|
||||
|
||||
// When we have an article let's add the modal and make the title clickable
|
||||
$hasLink = ($data['privacyType'] === 'article' && $data['articleid'])
|
||||
|| ($data['privacyType'] === 'menu_item' && $data['menuItemId']);
|
||||
|
||||
if ($hasLink) {
|
||||
$attribs['data-bs-toggle'] = 'modal';
|
||||
|
||||
$data['label'] = HTMLHelper::_(
|
||||
'link',
|
||||
'#modal-' . $this->id,
|
||||
$data['label'],
|
||||
$attribs
|
||||
);
|
||||
}
|
||||
|
||||
// Here mainly for B/C with old layouts. This can be done in the layouts directly
|
||||
$extraData = [
|
||||
'text' => $data['label'],
|
||||
'for' => $this->id,
|
||||
'classes' => explode(' ', $data['labelclass']),
|
||||
'position' => $position,
|
||||
];
|
||||
|
||||
return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the field input markup.
|
||||
*
|
||||
* @return string The field input markup.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected function getInput()
|
||||
{
|
||||
$modalHtml = '';
|
||||
$layoutData = $this->getLayoutData();
|
||||
|
||||
$hasLink = ($this->privacyType === 'article' && $this->articleid)
|
||||
|| ($this->privacyType === 'menu_item' && $this->menuItemId);
|
||||
|
||||
if ($hasLink) {
|
||||
$modalParams['title'] = $layoutData['label'];
|
||||
$modalParams['url'] = ($this->privacyType === 'menu_item') ? $this->getAssignedMenuItemUrl() : $this->getAssignedArticleUrl();
|
||||
$modalParams['height'] = '100%';
|
||||
$modalParams['width'] = '100%';
|
||||
$modalParams['bodyHeight'] = 70;
|
||||
$modalParams['modalWidth'] = 80;
|
||||
$modalHtml = HTMLHelper::_('bootstrap.renderModal', 'modal-' . $this->id, $modalParams);
|
||||
}
|
||||
|
||||
return $modalHtml . parent::getInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the data to be passed to the layout for rendering.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
protected function getLayoutData()
|
||||
{
|
||||
$data = parent::getLayoutData();
|
||||
|
||||
$extraData = [
|
||||
'articleid' => (int) $this->articleid,
|
||||
'menuItemId' => (int) $this->menuItemId,
|
||||
'privacyType' => (string) $this->privacyType,
|
||||
];
|
||||
|
||||
return array_merge($data, $extraData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the url of the assigned article based on the current user language
|
||||
*
|
||||
* @return string Returns the link to the article
|
||||
*
|
||||
* @since 3.9.1
|
||||
*/
|
||||
private function getAssignedArticleUrl()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
|
||||
// Get the info from the article
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName(['id', 'catid', 'language']))
|
||||
->from($db->quoteName('#__content'))
|
||||
->where($db->quoteName('id') . ' = ' . (int) $this->articleid);
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$article = $db->loadObject();
|
||||
} catch (ExecutionFailureException $e) {
|
||||
// Something at the database layer went wrong
|
||||
return Route::_(
|
||||
'index.php?option=com_content&view=article&id='
|
||||
. $this->articleid . '&tmpl=component'
|
||||
);
|
||||
}
|
||||
|
||||
if (!\is_object($article)) {
|
||||
// We have not found the article object lets show a 404 to the user
|
||||
return Route::_(
|
||||
'index.php?option=com_content&view=article&id='
|
||||
. $this->articleid . '&tmpl=component'
|
||||
);
|
||||
}
|
||||
|
||||
if (!Associations::isEnabled()) {
|
||||
return Route::_(
|
||||
RouteHelper::getArticleRoute(
|
||||
$article->id,
|
||||
$article->catid,
|
||||
$article->language
|
||||
) . '&tmpl=component'
|
||||
);
|
||||
}
|
||||
|
||||
$associatedArticles = Associations::getAssociations('com_content', '#__content', 'com_content.item', $article->id);
|
||||
$currentLang = Factory::getLanguage()->getTag();
|
||||
|
||||
if (isset($associatedArticles) && $currentLang !== $article->language && \array_key_exists($currentLang, $associatedArticles)) {
|
||||
return Route::_(
|
||||
RouteHelper::getArticleRoute(
|
||||
$associatedArticles[$currentLang]->id,
|
||||
$associatedArticles[$currentLang]->catid,
|
||||
$associatedArticles[$currentLang]->language
|
||||
) . '&tmpl=component'
|
||||
);
|
||||
}
|
||||
|
||||
// Association is enabled but this article is not associated
|
||||
return Route::_(
|
||||
'index.php?option=com_content&view=article&id='
|
||||
. $article->id . '&catid=' . $article->catid
|
||||
. '&tmpl=component&lang=' . $article->language
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get privacy menu item URL. If the site is a multilingual website and there is associated menu item for the
|
||||
* current language, the URL of the associated menu item will be returned.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
private function getAssignedMenuItemUrl()
|
||||
{
|
||||
$itemId = $this->menuItemId;
|
||||
$languageSuffix = '';
|
||||
|
||||
if ($itemId > 0 && Associations::isEnabled()) {
|
||||
$privacyAssociated = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $itemId, 'id', '', '');
|
||||
$currentLang = Factory::getLanguage()->getTag();
|
||||
|
||||
if (isset($privacyAssociated[$currentLang])) {
|
||||
$itemId = $privacyAssociated[$currentLang]->id;
|
||||
}
|
||||
|
||||
if (Multilanguage::isEnabled()) {
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName(['id', 'language']))
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('id') . ' = :id')
|
||||
->bind(':id', $itemId, ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$menuItem = $db->loadObject();
|
||||
|
||||
$languageSuffix = '&lang=' . $menuItem->language;
|
||||
}
|
||||
}
|
||||
|
||||
return Route::_(
|
||||
'index.php?Itemid=' . (int) $itemId . '&tmpl=component' . $languageSuffix
|
||||
);
|
||||
}
|
||||
}
|
||||
52
plugins/content/contact/contact.xml
Normal file
52
plugins/content/contact/contact.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_contact</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2014-01</creationDate>
|
||||
<copyright>(C) 2014 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.2.2</version>
|
||||
<description>PLG_CONTENT_CONTACT_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\Contact</namespace>
|
||||
<files>
|
||||
<folder plugin="contact">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_contact.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_contact.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="url"
|
||||
type="list"
|
||||
label="PLG_CONTENT_CONTACT_PARAM_URL_LABEL"
|
||||
description="PLG_CONTENT_CONTACT_PARAM_URL_DESCRIPTION"
|
||||
default="url"
|
||||
validate="options"
|
||||
>
|
||||
<option value="url">PLG_CONTENT_CONTACT_PARAM_URL_URL</option>
|
||||
<option value="webpage">PLG_CONTENT_CONTACT_PARAM_URL_WEBPAGE</option>
|
||||
<option value="email">PLG_CONTENT_CONTACT_PARAM_URL_EMAIL</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="link_to_alias"
|
||||
type="radio"
|
||||
label="PLG_CONTENT_CONTACT_PARAM_ALIAS_LABEL"
|
||||
description="PLG_CONTENT_CONTACT_PARAM_ALIAS_DESCRIPTION"
|
||||
default="0"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
48
plugins/content/contact/services/provider.php
Normal file
48
plugins/content/contact/services/provider.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.contact
|
||||
*
|
||||
* @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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\Contact\Extension\Contact;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Contact(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'contact')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
$plugin->setDatabase($container->get(DatabaseInterface::class));
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
146
plugins/content/contact/src/Extension/Contact.php
Normal file
146
plugins/content/contact/src/Extension/Contact.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.contact
|
||||
*
|
||||
* @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\Contact\Extension;
|
||||
|
||||
use Joomla\CMS\Language\Multilanguage;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Contact\Site\Helper\RouteHelper;
|
||||
use Joomla\Database\DatabaseAwareTrait;
|
||||
use Joomla\Database\ParameterType;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Contact Plugin
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
final class Contact extends CMSPlugin
|
||||
{
|
||||
use DatabaseAwareTrait;
|
||||
|
||||
/**
|
||||
* Plugin that retrieves contact information for contact
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin.
|
||||
* @param mixed &$row An object with a "text" property
|
||||
* @param mixed $params Additional parameters. See {@see PlgContentContent()}.
|
||||
* @param integer $page Optional page number. Unused. Defaults to zero.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onContentPrepare($context, &$row, $params, $page = 0)
|
||||
{
|
||||
$allowed_contexts = ['com_content.category', 'com_content.article', 'com_content.featured'];
|
||||
|
||||
if (!\in_array($context, $allowed_contexts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if we don't have valid params or don't link the author
|
||||
if (!($params instanceof Registry) || !$params->get('link_author')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if an alias is used
|
||||
if ((int) $this->params->get('link_to_alias', 0) === 0 && $row->created_by_alias != '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if we don't have a valid article id
|
||||
if (!isset($row->id) || !(int) $row->id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = $this->getContactData($row->created_by);
|
||||
|
||||
if ($contact === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$row->contactid = $contact->contactid;
|
||||
$row->webpage = $contact->webpage;
|
||||
$row->email = $contact->email_to;
|
||||
$url = $this->params->get('url', 'url');
|
||||
|
||||
if ($row->contactid && $url === 'url') {
|
||||
$row->contact_link = Route::_(RouteHelper::getContactRoute($contact->contactid . ':' . $contact->alias, $contact->catid));
|
||||
} elseif ($row->webpage && $url === 'webpage') {
|
||||
$row->contact_link = $row->webpage;
|
||||
} elseif ($row->email && $url === 'email') {
|
||||
$row->contact_link = 'mailto:' . $row->email;
|
||||
} else {
|
||||
$row->contact_link = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Contact
|
||||
*
|
||||
* @param int $userId Id of the user who created the article
|
||||
*
|
||||
* @return stdClass|null Object containing contact details or null if not found
|
||||
*/
|
||||
private function getContactData($userId)
|
||||
{
|
||||
static $contacts = [];
|
||||
|
||||
// Note: don't use isset() because value could be null.
|
||||
if (\array_key_exists($userId, $contacts)) {
|
||||
return $contacts[$userId];
|
||||
}
|
||||
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true);
|
||||
$userId = (int) $userId;
|
||||
|
||||
$query->select($db->quoteName('contact.id', 'contactid'))
|
||||
->select(
|
||||
$db->quoteName(
|
||||
[
|
||||
'contact.alias',
|
||||
'contact.catid',
|
||||
'contact.webpage',
|
||||
'contact.email_to',
|
||||
]
|
||||
)
|
||||
)
|
||||
->from($db->quoteName('#__contact_details', 'contact'))
|
||||
->where(
|
||||
[
|
||||
$db->quoteName('contact.published') . ' = 1',
|
||||
$db->quoteName('contact.user_id') . ' = :createdby',
|
||||
]
|
||||
)
|
||||
->bind(':createdby', $userId, ParameterType::INTEGER);
|
||||
|
||||
if (Multilanguage::isEnabled() === true) {
|
||||
$query->where(
|
||||
'(' . $db->quoteName('contact.language') . ' IN ('
|
||||
. implode(',', $query->bindArray([$this->getApplication()->getLanguage()->getTag(), '*'], ParameterType::STRING))
|
||||
. ') OR ' . $db->quoteName('contact.language') . ' IS NULL)'
|
||||
);
|
||||
}
|
||||
|
||||
$query->order($db->quoteName('contact.id') . ' DESC')
|
||||
->setLimit(1);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
$contacts[$userId] = $db->loadObject();
|
||||
|
||||
return $contacts[$userId];
|
||||
}
|
||||
}
|
||||
38
plugins/content/emailcloak/emailcloak.xml
Normal file
38
plugins/content/emailcloak/emailcloak.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_emailcloak</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2005-11</creationDate>
|
||||
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_CONTENT_EMAILCLOAK_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\EmailCloak</namespace>
|
||||
<files>
|
||||
<folder plugin="emailcloak">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_emailcloak.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_emailcloak.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="mode"
|
||||
type="list"
|
||||
label="PLG_CONTENT_EMAILCLOAK_MODE_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
validate="options"
|
||||
>
|
||||
<option value="0">PLG_CONTENT_EMAILCLOAK_NONLINKABLE</option>
|
||||
<option value="1">PLG_CONTENT_EMAILCLOAK_LINKABLE</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
46
plugins/content/emailcloak/services/provider.php
Normal file
46
plugins/content/emailcloak/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.emailcloak
|
||||
*
|
||||
* @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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\EmailCloak\Extension\EmailCloak;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new EmailCloak(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'emailcloak')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
494
plugins/content/emailcloak/src/Extension/EmailCloak.php
Normal file
494
plugins/content/emailcloak/src/Extension/EmailCloak.php
Normal file
@ -0,0 +1,494 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.emailcloak
|
||||
*
|
||||
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\EmailCloak\Extension;
|
||||
|
||||
use Joomla\CMS\Event\Content\ContentPrepareEvent;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\Event\SubscriberInterface;
|
||||
use Joomla\String\StringHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Email cloak plugin class.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final class EmailCloak extends CMSPlugin implements SubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array of events this subscriber will listen to.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['onContentPrepare' => 'onContentPrepare'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin that cloaks all emails in content from spambots via Javascript.
|
||||
*
|
||||
* @param ContentPrepareEvent $event Event instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onContentPrepare(ContentPrepareEvent $event)
|
||||
{
|
||||
// Don't run if in the API Application
|
||||
// Don't run this plugin when the content is being indexed
|
||||
if ($this->getApplication()->isClient('api') || $event->getContext() === 'com_finder.indexer') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get content item
|
||||
$item = $event->getItem();
|
||||
|
||||
// If the item does not have a text property there is nothing to do
|
||||
if (!property_exists($item, 'text')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$text = $this->cloak($item->text);
|
||||
|
||||
if ($text) {
|
||||
$item->text = $text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a search pattern based on link and text.
|
||||
*
|
||||
* @param string $link The target of an email link.
|
||||
* @param string $text The text enclosed by the link.
|
||||
*
|
||||
* @return string A regular expression that matches a link containing the parameters.
|
||||
*/
|
||||
private function getPattern($link, $text)
|
||||
{
|
||||
$pattern = '~(?:<a ([^>]*)href\s*=\s*"mailto:' . $link . '"([^>]*))>' . $text . '</a>~i';
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloak all emails in text from spambots via Javascript.
|
||||
*
|
||||
* @param string $text The string to be cloaked.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function cloak($text)
|
||||
{
|
||||
/*
|
||||
* Check for presence of {emailcloak=off} which is explicits disables this
|
||||
* bot for the item.
|
||||
*/
|
||||
if (StringHelper::strpos($text, '{emailcloak=off}') !== false) {
|
||||
return StringHelper::str_ireplace('{emailcloak=off}', '', $text);
|
||||
}
|
||||
|
||||
// Simple performance check to determine whether bot should process further.
|
||||
if (StringHelper::strpos($text, '@') === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$mode = (int) $this->params->def('mode', 1);
|
||||
$mode = $mode === 1;
|
||||
|
||||
// Example: any@example.org
|
||||
$searchEmail = '([\w\.\'\-\+]+\@(?:[a-z0-9\.\-]+\.)+(?:[a-zA-Z0-9\-]{2,24}))';
|
||||
|
||||
// Example: any@example.org?subject=anyText
|
||||
$searchEmailLink = $searchEmail . '([?&][\x20-\x7f][^"<>]+)';
|
||||
|
||||
// Any Text
|
||||
$searchText = '((?:[\x20-\x7f]|[\xA1-\xFF]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF4][\x80-\xBF]{3})[^<>]+)';
|
||||
|
||||
// Any Image link
|
||||
$searchImage = '(<img[^>]+>)';
|
||||
|
||||
// Any Text with <span or <strong
|
||||
$searchTextSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchText . '(</span>|</strong>|</span></strong>)';
|
||||
|
||||
// Any address with <span or <strong
|
||||
$searchEmailSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchEmail . '(</span>|</strong>|</span></strong>)';
|
||||
|
||||
/*
|
||||
* Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org"
|
||||
* >email@example.org</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add
|
||||
* the mailto: prefix...
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchEmail);
|
||||
$pattern = str_replace('"mailto:', '"([\x20-\x7f][^<>]+/)', $pattern);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[3][0];
|
||||
$mailText = $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org"
|
||||
* >anytext</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add
|
||||
* the mailto: prefix...
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchText);
|
||||
$pattern = str_replace('"mailto:', '"([\x20-\x7f][^<>]+/)', $pattern);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[3][0];
|
||||
$mailText = $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org"
|
||||
* >email@example.org</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchEmail);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@amail.com"
|
||||
* ><anyspan >email@amail.com</anyspan></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchEmailSpan);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0] . $regs[5][0] . $regs[6][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@amail.com">
|
||||
* <anyspan >anytext</anyspan></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchTextSpan);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0] . $regs[5][0] . $regs[6][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org">
|
||||
* anytext</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchText);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org">
|
||||
* <img anything></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchImage);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org">
|
||||
* <img anything>email@example.org</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchImage . $searchEmail);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0] . $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org">
|
||||
* <img anything>any text</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmail, $searchImage . $searchText);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0];
|
||||
$mailText = $regs[4][0] . $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[3][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org?
|
||||
* subject=Text">email@example.org</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchEmail);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Needed for handling of Body parameter
|
||||
$mail = str_replace('&', '&', $mail);
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@example.org?
|
||||
* subject=Text">anytext</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchText);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Needed for handling of Body parameter
|
||||
$mail = str_replace('&', '&', $mail);
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text"
|
||||
* ><anyspan >email@amail.com</anyspan></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchEmailSpan);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0] . $regs[6][0] . $regs[7][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text">
|
||||
* <anyspan >anytext</anyspan></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchTextSpan);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0] . $regs[6][0] . $regs[7][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code
|
||||
* <a href="mailto:email@amail.com?subject=Text"><img anything></a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchImage);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Needed for handling of Body parameter
|
||||
$mail = str_replace('&', '&', $mail);
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code
|
||||
* <a href="mailto:email@amail.com?subject=Text"><img anything>email@amail.com</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchImage . $searchEmail);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0] . $regs[6][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Needed for handling of Body parameter
|
||||
$mail = str_replace('&', '&', $mail);
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for derivatives of link code
|
||||
* <a href="mailto:email@amail.com?subject=Text"><img anything>any text</a>
|
||||
*/
|
||||
$pattern = $this->getPattern($searchEmailLink, $searchImage . $searchText);
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[2][0] . $regs[3][0];
|
||||
$mailText = $regs[5][0] . $regs[6][0];
|
||||
$attribsBefore = $regs[1][0];
|
||||
$attribsAfter = $regs[4][0];
|
||||
|
||||
// Needed for handling of Body parameter
|
||||
$mail = str_replace('&', '&', $mail);
|
||||
|
||||
// Check to see if mail text is different from mail addy
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for plain text email addresses, such as email@example.org but within HTML tags:
|
||||
* <img src="..." title="email@example.org"> or <input type="text" placeholder="email@example.org">
|
||||
* The '<[^<]*>(*SKIP)(*F)|' trick is used to exclude this kind of occurrences
|
||||
*/
|
||||
$pattern = '~<[^<]*(?<!\/)>(*SKIP)(*F)|<[^>]+?(\w*=\"' . $searchEmail . '\")[^>]*\/>~i';
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[0][0];
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, 0, $mail);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($mail));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for plain text email addresses, such as email@example.org but within HTML attributes:
|
||||
* <a title="email@example.org" href="#">email</a> or <li title="email@example.org">email</li>
|
||||
*/
|
||||
$pattern = '(<[^>]+?(\w*=\"' . $searchEmail . '")[^>]*>[^<]+<[^<]+>)';
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[0][0];
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, 0, $mail);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($mail));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for plain text email addresses, such as email@example.org but not within HTML tags:
|
||||
* <p>email@example.org</p>
|
||||
* The '<[^<]*>(*SKIP)(*F)|' trick is used to exclude this kind of occurrences
|
||||
* The '<[^<]*(?<!\/(?:src))>(*SKIP)(*F)|' exclude image files with @ in filename
|
||||
*/
|
||||
|
||||
$pattern = '~<[^<]*(?<!\/(?:src))>(*SKIP)(*F)|' . $searchEmail . '~i';
|
||||
|
||||
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
|
||||
$mail = $regs[1][0];
|
||||
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mail);
|
||||
|
||||
// Replace the found address with the js cloaked email
|
||||
$text = substr_replace($text, $replacement, $regs[1][1], \strlen($mail));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
27
plugins/content/fields/fields.xml
Normal file
27
plugins/content/fields/fields.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_fields</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2017-02</creationDate>
|
||||
<copyright>(C) 2017 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.7.0</version>
|
||||
<description>PLG_CONTENT_FIELDS_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\Fields</namespace>
|
||||
<files>
|
||||
<folder plugin="fields">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_fields.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_fields.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
44
plugins/content/fields/services/provider.php
Normal file
44
plugins/content/fields/services/provider.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.fields
|
||||
*
|
||||
* @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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\Fields\Extension\Fields;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Fields(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'fields')
|
||||
);
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
155
plugins/content/fields/src/Extension/Fields.php
Normal file
155
plugins/content/fields/src/Extension/Fields.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.fields
|
||||
*
|
||||
* @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\Plugin\Content\Fields\Extension;
|
||||
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Plug-in to show a custom field in eg an article
|
||||
* This uses the {fields ID} syntax
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
final class Fields extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Plugin that shows a custom field
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin.
|
||||
* @param object &$item The item object. Note $article->text is also available
|
||||
* @param object &$params The article params
|
||||
* @param int $page The 'page' number
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function onContentPrepare($context, &$item, &$params, $page = 0)
|
||||
{
|
||||
// If the item has a context, overwrite the existing one
|
||||
if ($context === 'com_finder.indexer' && !empty($item->context)) {
|
||||
$context = $item->context;
|
||||
} elseif ($context === 'com_finder.indexer') {
|
||||
// Don't run this plugin when the content is being indexed and we have no real context
|
||||
return;
|
||||
}
|
||||
|
||||
// This plugin only works if $item is an object
|
||||
if (!\is_object($item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't run if there is no text property (in case of bad calls) or it is empty
|
||||
if (!property_exists($item, 'text') || empty($item->text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the text
|
||||
if (property_exists($item, 'text') && strpos($item->text, 'field') !== false) {
|
||||
$item->text = $this->prepare($item->text, $context, $item);
|
||||
}
|
||||
|
||||
// Prepare the intro text
|
||||
if (property_exists($item, 'introtext') && \is_string($item->introtext) && strpos($item->introtext, 'field') !== false) {
|
||||
$item->introtext = $this->prepare($item->introtext, $context, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the given string by parsing {field} and {fieldgroup} groups and replacing them.
|
||||
*
|
||||
* @param string $string The text to prepare
|
||||
* @param string $context The context of the content
|
||||
* @param object $item The item object
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.8.1
|
||||
*/
|
||||
private function prepare($string, $context, $item)
|
||||
{
|
||||
// Search for {field ID} or {fieldgroup ID} tags and put the results into $matches.
|
||||
$regex = '/{(field|fieldgroup)\s+(.*?)}/i';
|
||||
preg_match_all($regex, $string, $matches, PREG_SET_ORDER);
|
||||
|
||||
if (!$matches) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$parts = FieldsHelper::extract($context);
|
||||
|
||||
if (!$parts || \count($parts) < 2) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$context = $parts[0] . '.' . $parts[1];
|
||||
$fields = FieldsHelper::getFields($context, $item, true);
|
||||
$fieldsById = [];
|
||||
$groups = [];
|
||||
|
||||
// Rearranging fields in arrays for easier lookup later.
|
||||
foreach ($fields as $field) {
|
||||
$fieldsById[$field->id] = $field;
|
||||
$groups[$field->group_id][] = $field;
|
||||
}
|
||||
|
||||
foreach ($matches as $i => $match) {
|
||||
// $match[0] is the full pattern match, $match[1] is the type (field or fieldgroup) and $match[2] the ID and optional the layout
|
||||
$explode = explode(',', $match[2]);
|
||||
$id = (int) $explode[0];
|
||||
$output = '';
|
||||
|
||||
if ($match[1] === 'field' && $id) {
|
||||
if (isset($fieldsById[$id])) {
|
||||
$layout = !empty($explode[1]) ? trim($explode[1]) : $fieldsById[$id]->params->get('layout', 'render');
|
||||
$output = FieldsHelper::render(
|
||||
$context,
|
||||
'field.' . $layout,
|
||||
[
|
||||
'item' => $item,
|
||||
'context' => $context,
|
||||
'field' => $fieldsById[$id],
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ($match[2] === '*') {
|
||||
$match[0] = str_replace('*', '\*', $match[0]);
|
||||
$renderFields = $fields;
|
||||
} else {
|
||||
$renderFields = $groups[$id] ?? '';
|
||||
}
|
||||
|
||||
if ($renderFields) {
|
||||
$layout = !empty($explode[1]) ? trim($explode[1]) : 'render';
|
||||
$output = FieldsHelper::render(
|
||||
$context,
|
||||
'fields.' . $layout,
|
||||
[
|
||||
'item' => $item,
|
||||
'context' => $context,
|
||||
'fields' => $renderFields,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$string = preg_replace("|$match[0]|", addcslashes($output, '\\$'), $string, 1);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
25
plugins/content/finder/finder.xml
Normal file
25
plugins/content/finder/finder.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_finder</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2011-12</creationDate>
|
||||
<copyright>(C) 2011 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_CONTENT_FINDER_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\Finder</namespace>
|
||||
<files>
|
||||
<folder plugin="finder">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_finder.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_finder.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
46
plugins/content/finder/services/provider.php
Normal file
46
plugins/content/finder/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.finder
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\Finder\Extension\Finder;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Finder(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'finder')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
176
plugins/content/finder/src/Extension/Finder.php
Normal file
176
plugins/content/finder/src/Extension/Finder.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.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\Plugin\Content\Finder\Extension;
|
||||
|
||||
use Joomla\CMS\Event\Finder as FinderEvent;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Smart Search Content Plugin
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
final class Finder extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Flag to check whether finder plugins already imported.
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
protected $pluginsImported = false;
|
||||
|
||||
/**
|
||||
* Smart Search after save content method.
|
||||
* Content is passed by reference, but after the save, so no changes will be saved.
|
||||
* Method is called right after the content is saved.
|
||||
*
|
||||
* @param string $context The context of the content passed to the plugin (added in 1.6)
|
||||
* @param object $article A \Joomla\CMS\Table\Table\ object
|
||||
* @param bool $isNew If the content has just been created
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public function onContentAfterSave($context, $article, $isNew): void
|
||||
{
|
||||
$this->importFinderPlugins();
|
||||
|
||||
// Trigger the onFinderAfterSave event.
|
||||
$this->getDispatcher()->dispatch('onFinderAfterSave', new FinderEvent\AfterSaveEvent('onFinderAfterSave', [
|
||||
'context' => $context,
|
||||
'subject' => $article,
|
||||
'isNew' => $isNew,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart Search before save content method.
|
||||
* Content is passed by reference. Method is called before the content is saved.
|
||||
*
|
||||
* @param string $context The context of the content passed to the plugin (added in 1.6).
|
||||
* @param object $article A \Joomla\CMS\Table\Table\ object.
|
||||
* @param bool $isNew If the content is just about to be created.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public function onContentBeforeSave($context, $article, $isNew)
|
||||
{
|
||||
$this->importFinderPlugins();
|
||||
|
||||
// Trigger the onFinderBeforeSave event.
|
||||
$this->getDispatcher()->dispatch('onFinderBeforeSave', new FinderEvent\BeforeSaveEvent('onFinderBeforeSave', [
|
||||
'context' => $context,
|
||||
'subject' => $article,
|
||||
'isNew' => $isNew,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart Search after delete content method.
|
||||
* Content is passed by reference, but after the deletion.
|
||||
*
|
||||
* @param string $context The context of the content passed to the plugin (added in 1.6).
|
||||
* @param object $article A \Joomla\CMS\Table\Table object.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public function onContentAfterDelete($context, $article): void
|
||||
{
|
||||
$this->importFinderPlugins();
|
||||
|
||||
// Trigger the onFinderAfterDelete event.
|
||||
$this->getDispatcher()->dispatch('onFinderAfterDelete', new FinderEvent\AfterDeleteEvent('onFinderAfterDelete', [
|
||||
'context' => $context,
|
||||
'subject' => $article,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart Search content state change method.
|
||||
* Method to update the link information for items that have been changed
|
||||
* from outside the edit screen. This is fired when the item is published,
|
||||
* unpublished, archived, or unarchived from the list view.
|
||||
*
|
||||
* @param string $context The context for the content passed to the plugin.
|
||||
* @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
|
||||
*/
|
||||
public function onContentChangeState($context, $pks, $value)
|
||||
{
|
||||
$this->importFinderPlugins();
|
||||
|
||||
// Trigger the onFinderChangeState event.
|
||||
$this->getDispatcher()->dispatch('onFinderChangeState', new FinderEvent\AfterChangeStateEvent('onFinderChangeState', [
|
||||
'context' => $context,
|
||||
'subject' => $pks,
|
||||
'value' => $value,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart Search change category state content method.
|
||||
* Method is called when the state of the category to which the
|
||||
* content item belongs is changed.
|
||||
*
|
||||
* @param string $extension The extension whose category has been updated.
|
||||
* @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
|
||||
*/
|
||||
public function onCategoryChangeState($extension, $pks, $value)
|
||||
{
|
||||
$this->importFinderPlugins();
|
||||
|
||||
// Trigger the onFinderCategoryChangeState event.
|
||||
$this->getDispatcher()->dispatch('onFinderCategoryChangeState', new FinderEvent\AfterCategoryChangeStateEvent('onFinderCategoryChangeState', [
|
||||
'context' => $extension,
|
||||
'subject' => $pks,
|
||||
'value' => $value,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to import finder plugins.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
protected function importFinderPlugins()
|
||||
{
|
||||
if ($this->pluginsImported) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pluginsImported = true;
|
||||
|
||||
PluginHelper::importPlugin('finder', null, true, $this->getDispatcher());
|
||||
}
|
||||
}
|
||||
79
plugins/content/joomla/joomla.xml
Normal file
79
plugins/content/joomla/joomla.xml
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_joomla</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2010-11</creationDate>
|
||||
<copyright>(C) 2010 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_CONTENT_JOOMLA_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\Joomla</namespace>
|
||||
<files>
|
||||
<folder plugin="joomla">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_joomla.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_joomla.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="check_categories"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_LABEL"
|
||||
description="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_DESC"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="email_new_fe"
|
||||
type="radio"
|
||||
label="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_LABEL"
|
||||
description="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_DESC"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<field
|
||||
name="schema_content"
|
||||
type="radio"
|
||||
label="PLG_CONTENT_JOOMLA_SCHEMA_CONTENT_LABEL"
|
||||
description="PLG_CONTENT_JOOMLA_SCHEMA_CONTENT_DESC"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
filter="integer"
|
||||
required="true"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<field
|
||||
name="schema_contact"
|
||||
type="radio"
|
||||
label="PLG_CONTENT_JOOMLA_SCHEMA_CONTACT_LABEL"
|
||||
description="PLG_CONTENT_JOOMLA_SCHEMA_CONTACT_DESC"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
filter="integer"
|
||||
required="true"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
|
||||
</extension>
|
||||
50
plugins/content/joomla/services/provider.php
Normal file
50
plugins/content/joomla/services/provider.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.joomla
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\CMS\User\UserFactoryInterface;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\Joomla\Extension\Joomla;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Joomla(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'joomla')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
$plugin->setDatabase($container->get(DatabaseInterface::class));
|
||||
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
1022
plugins/content/joomla/src/Extension/Joomla.php
Normal file
1022
plugins/content/joomla/src/Extension/Joomla.php
Normal file
File diff suppressed because it is too large
Load Diff
41
plugins/content/loadmodule/loadmodule.xml
Normal file
41
plugins/content/loadmodule/loadmodule.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_loadmodule</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2005-11</creationDate>
|
||||
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_LOADMODULE_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\LoadModule</namespace>
|
||||
<files>
|
||||
<folder plugin="loadmodule">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_loadmodule.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_loadmodule.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="style"
|
||||
type="list"
|
||||
label="PLG_LOADMODULE_FIELD_STYLE_LABEL"
|
||||
default="none"
|
||||
validate="options"
|
||||
>
|
||||
<option value="none">PLG_LOADMODULE_FIELD_VALUE_RAW</option>
|
||||
<option value="html5">PLG_LOADMODULE_FIELD_VALUE_DIVS</option>
|
||||
<option value="table">PLG_LOADMODULE_FIELD_VALUE_TABLE</option>
|
||||
<!-- @TODO: The following styles don't exist in default installation and can be removed in Joomla 5 -->
|
||||
<option value="horz">PLG_LOADMODULE_FIELD_VALUE_HORIZONTAL</option>
|
||||
<option value="rounded">PLG_LOADMODULE_FIELD_VALUE_MULTIPLEDIVS</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
46
plugins/content/loadmodule/services/provider.php
Normal file
46
plugins/content/loadmodule/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.loadmodule
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\LoadModule\Extension\LoadModule;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new LoadModule(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'loadmodule')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
250
plugins/content/loadmodule/src/Extension/LoadModule.php
Normal file
250
plugins/content/loadmodule/src/Extension/LoadModule.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.loadmodule
|
||||
*
|
||||
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\LoadModule\Extension;
|
||||
|
||||
use Joomla\CMS\Helper\ModuleHelper;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Plugin to enable loading modules into content (e.g. articles)
|
||||
* This uses the {loadmodule} syntax
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final class LoadModule extends CMSPlugin
|
||||
{
|
||||
protected static $modules = [];
|
||||
|
||||
protected static $mods = [];
|
||||
|
||||
/**
|
||||
* Plugin that loads module positions within content
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin.
|
||||
* @param object &$article The article object. Note $article->text is also available
|
||||
* @param mixed &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public function onContentPrepare($context, &$article, &$params, $page = 0)
|
||||
{
|
||||
// Only execute if $article is an object and has a text property
|
||||
if (!\is_object($article) || !property_exists($article, 'text') || \is_null($article->text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaultStyle = $this->params->get('style', 'none');
|
||||
|
||||
// Fallback xhtml (used in Joomla 3) to html5
|
||||
if ($defaultStyle === 'xhtml') {
|
||||
$defaultStyle = 'html5';
|
||||
}
|
||||
|
||||
// Expression to search for (positions)
|
||||
$regex = '/{loadposition\s(.*?)}/i';
|
||||
|
||||
// Expression to search for(modules)
|
||||
$regexmod = '/{loadmodule\s(.*?)}/i';
|
||||
|
||||
// Expression to search for(id)
|
||||
$regexmodid = '/{loadmoduleid\s([1-9][0-9]*)}/i';
|
||||
|
||||
// Remove macros and don't run this plugin when the content is being indexed
|
||||
if ($context === 'com_finder.indexer') {
|
||||
if (str_contains($article->text, 'loadposition')) {
|
||||
$article->text = preg_replace($regex, '', $article->text);
|
||||
}
|
||||
|
||||
if (str_contains($article->text, 'loadmoduleid')) {
|
||||
$article->text = preg_replace($regexmodid, '', $article->text);
|
||||
}
|
||||
|
||||
if (str_contains($article->text, 'loadmodule')) {
|
||||
$article->text = preg_replace($regexmod, '', $article->text);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (str_contains($article->text, '{loadposition ')) {
|
||||
// Find all instances of plugin and put in $matches for loadposition
|
||||
// $matches[0] is full pattern match, $matches[1] is the position
|
||||
preg_match_all($regex, $article->text, $matches, PREG_SET_ORDER);
|
||||
|
||||
// No matches, skip this
|
||||
if ($matches) {
|
||||
foreach ($matches as $match) {
|
||||
$matcheslist = explode(',', $match[1]);
|
||||
|
||||
// We may not have a module style so fall back to the plugin default.
|
||||
if (!\array_key_exists(1, $matcheslist)) {
|
||||
$matcheslist[1] = $defaultStyle;
|
||||
}
|
||||
|
||||
$position = trim($matcheslist[0]);
|
||||
$style = trim($matcheslist[1]);
|
||||
|
||||
$output = $this->load($position, $style);
|
||||
|
||||
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
|
||||
if (($start = strpos($article->text, $match[0])) !== false) {
|
||||
$article->text = substr_replace($article->text, $output, $start, \strlen($match[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($article->text, '{loadmodule ')) {
|
||||
// Find all instances of plugin and put in $matchesmod for loadmodule
|
||||
preg_match_all($regexmod, $article->text, $matchesmod, PREG_SET_ORDER);
|
||||
|
||||
// If no matches, skip this
|
||||
if ($matchesmod) {
|
||||
foreach ($matchesmod as $matchmod) {
|
||||
$matchesmodlist = explode(',', $matchmod[1]);
|
||||
|
||||
// First parameter is the module, will be prefixed with mod_ later
|
||||
$module = trim($matchesmodlist[0]);
|
||||
|
||||
// Second parameter is the title
|
||||
$title = '';
|
||||
|
||||
if (\array_key_exists(1, $matchesmodlist)) {
|
||||
$title = htmlspecialchars_decode(trim($matchesmodlist[1]));
|
||||
}
|
||||
|
||||
// Third parameter is the module style, (fallback is the plugin default set earlier).
|
||||
$stylemod = $defaultStyle;
|
||||
|
||||
if (\array_key_exists(2, $matchesmodlist)) {
|
||||
$stylemod = trim($matchesmodlist[2]);
|
||||
}
|
||||
|
||||
$output = $this->loadModule($module, $title, $stylemod);
|
||||
|
||||
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
|
||||
if (($start = strpos($article->text, $matchmod[0])) !== false) {
|
||||
$article->text = substr_replace($article->text, $output, $start, \strlen($matchmod[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($article->text, '{loadmoduleid ')) {
|
||||
// Find all instances of plugin and put in $matchesmodid for loadmoduleid
|
||||
preg_match_all($regexmodid, $article->text, $matchesmodid, PREG_SET_ORDER);
|
||||
|
||||
// If no matches, skip this
|
||||
if ($matchesmodid) {
|
||||
foreach ($matchesmodid as $match) {
|
||||
$id = trim($match[1]);
|
||||
$output = $this->loadID($id);
|
||||
|
||||
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
|
||||
if (($start = strpos($article->text, $match[0])) !== false) {
|
||||
$article->text = substr_replace($article->text, $output, $start, \strlen($match[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and renders the module
|
||||
*
|
||||
* @param string $position The position assigned to the module
|
||||
* @param string $style The style assigned to the module
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
private function load($position, $style = 'none')
|
||||
{
|
||||
$document = $this->getApplication()->getDocument();
|
||||
$renderer = $document->loadRenderer('module');
|
||||
$modules = ModuleHelper::getModules($position);
|
||||
$params = ['style' => $style];
|
||||
ob_start();
|
||||
|
||||
foreach ($modules as $module) {
|
||||
echo $renderer->render($module, $params);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is always going to get the first instance of the module type unless
|
||||
* there is a title.
|
||||
*
|
||||
* @param string $module The module title
|
||||
* @param string $title The title of the module
|
||||
* @param string $style The style of the module
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
private function loadModule($module, $title, $style = 'none')
|
||||
{
|
||||
$document = $this->getApplication()->getDocument();
|
||||
$renderer = $document->loadRenderer('module');
|
||||
$mod = ModuleHelper::getModule($module, $title);
|
||||
|
||||
// If the module without the mod_ isn't found, try it with mod_.
|
||||
// This allows people to enter it either way in the content
|
||||
if (!isset($mod)) {
|
||||
$name = 'mod_' . $module;
|
||||
$mod = ModuleHelper::getModule($name, $title);
|
||||
}
|
||||
|
||||
$params = ['style' => $style];
|
||||
ob_start();
|
||||
|
||||
if ($mod->id) {
|
||||
echo $renderer->render($mod, $params);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and renders the module
|
||||
*
|
||||
* @param string $id The id of the module
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
private function loadID($id)
|
||||
{
|
||||
$document = $this->getApplication()->getDocument();
|
||||
$renderer = $document->loadRenderer('module');
|
||||
$modules = ModuleHelper::getModuleById($id);
|
||||
$params = ['style' => 'none'];
|
||||
ob_start();
|
||||
|
||||
if ($modules->id > 0) {
|
||||
echo $renderer->render($modules, $params);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
96
plugins/content/pagebreak/pagebreak.xml
Normal file
96
plugins/content/pagebreak/pagebreak.xml
Normal file
@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_pagebreak</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2005-11</creationDate>
|
||||
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_CONTENT_PAGEBREAK_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\PageBreak</namespace>
|
||||
<files>
|
||||
<folder plugin="pagebreak">services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>tmpl</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_pagebreak.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_pagebreak.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="title"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CONTENT_PAGEBREAK_SITE_TITLE_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="article_index"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEX_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="article_index_text"
|
||||
type="text"
|
||||
label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEXTEXT"
|
||||
showon="article_index:1"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="multipage_toc"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CONTENT_PAGEBREAK_TOC_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="showall"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CONTENT_PAGEBREAK_SHOW_ALL_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="style"
|
||||
type="list"
|
||||
label="PLG_CONTENT_PAGEBREAK_STYLE_LABEL"
|
||||
default="pages"
|
||||
validate="options"
|
||||
>
|
||||
<option value="pages">PLG_CONTENT_PAGEBREAK_PAGES</option>
|
||||
<option value="sliders">PLG_CONTENT_PAGEBREAK_SLIDERS</option>
|
||||
<option value="tabs">PLG_CONTENT_PAGEBREAK_TABS</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
46
plugins/content/pagebreak/services/provider.php
Normal file
46
plugins/content/pagebreak/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagebreak
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\PageBreak\Extension\PageBreak;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new PageBreak(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'pagebreak')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
358
plugins/content/pagebreak/src/Extension/PageBreak.php
Normal file
358
plugins/content/pagebreak/src/Extension/PageBreak.php
Normal file
@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagebreak
|
||||
*
|
||||
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\PageBreak\Extension;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Pagination\Pagination;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\CMS\Utility\Utility;
|
||||
use Joomla\Component\Content\Site\Helper\RouteHelper;
|
||||
use Joomla\String\StringHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Page break plugin
|
||||
*
|
||||
* <strong>Usage:</strong>
|
||||
* <code><hr class="system-pagebreak" /></code>
|
||||
* <code><hr class="system-pagebreak" title="The page title" /></code>
|
||||
* or
|
||||
* <code><hr class="system-pagebreak" alt="The first page" /></code>
|
||||
* or
|
||||
* <code><hr class="system-pagebreak" title="The page title" alt="The first page" /></code>
|
||||
* or
|
||||
* <code><hr class="system-pagebreak" alt="The first page" title="The page title" /></code>
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
final class PageBreak extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* The navigation list with all page objects if parameter 'multipage_toc' is active.
|
||||
*
|
||||
* @var array
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $list = [];
|
||||
|
||||
/**
|
||||
* Plugin that adds a pagebreak into the text and truncates text at that point
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin.
|
||||
* @param object &$row The article object. Note $article->text is also available
|
||||
* @param mixed &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public function onContentPrepare($context, &$row, &$params, $page = 0)
|
||||
{
|
||||
$canProceed = $context === 'com_content.article';
|
||||
|
||||
if (!$canProceed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$style = $this->params->get('style', 'pages');
|
||||
|
||||
// Expression to search for.
|
||||
$regex = '#<hr(.*)class="system-pagebreak"(.*)\/?>#iU';
|
||||
|
||||
$input = $this->getApplication()->getInput();
|
||||
|
||||
$print = $input->getBool('print');
|
||||
$showall = $input->getBool('showall');
|
||||
|
||||
if (!$this->params->get('enabled', 1)) {
|
||||
$print = true;
|
||||
}
|
||||
|
||||
if ($print) {
|
||||
$row->text = preg_replace($regex, '<br>', $row->text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple performance check to determine whether bot should process further.
|
||||
if (StringHelper::strpos($row->text, 'class="system-pagebreak') === false) {
|
||||
if ($page > 0) {
|
||||
throw new \Exception($this->getApplication()->getLanguage()->_('JERROR_PAGE_NOT_FOUND'), 404);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$view = $input->getString('view');
|
||||
$full = $input->getBool('fullview');
|
||||
|
||||
if (!$page) {
|
||||
$page = 0;
|
||||
}
|
||||
|
||||
if ($full || $view !== 'article' || $params->get('intro_only') || $params->get('popup')) {
|
||||
$row->text = preg_replace($regex, '', $row->text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Load plugin language files only when needed (ex: not needed if no system-pagebreak class exists).
|
||||
$this->loadLanguage();
|
||||
|
||||
// Find all instances of plugin and put in $matches.
|
||||
$matches = [];
|
||||
preg_match_all($regex, $row->text, $matches, PREG_SET_ORDER);
|
||||
|
||||
if ($showall && $this->params->get('showall', 1)) {
|
||||
$hasToc = $this->params->get('multipage_toc', 1);
|
||||
|
||||
if ($hasToc) {
|
||||
// Display TOC.
|
||||
$page = 1;
|
||||
$this->createToc($row, $matches, $page);
|
||||
} else {
|
||||
$row->toc = '';
|
||||
}
|
||||
|
||||
$row->text = preg_replace($regex, '<br>', $row->text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the text around the plugin.
|
||||
$text = preg_split($regex, $row->text);
|
||||
|
||||
if (!isset($text[$page])) {
|
||||
throw new \Exception($this->getApplication()->getLanguage()->_('JERROR_PAGE_NOT_FOUND'), 404);
|
||||
}
|
||||
|
||||
// Count the number of pages.
|
||||
$n = \count($text);
|
||||
|
||||
// We have found at least one plugin, therefore at least 2 pages.
|
||||
if ($n > 1) {
|
||||
$title = $this->params->get('title', 1);
|
||||
$hasToc = $this->params->get('multipage_toc', 1);
|
||||
|
||||
// Adds heading or title to <site> Title.
|
||||
if ($title && $page && isset($matches[$page - 1][0])) {
|
||||
$attrs = Utility::parseAttributes($matches[$page - 1][0]);
|
||||
|
||||
if (isset($attrs['title'])) {
|
||||
$row->page_title = $attrs['title'];
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the text, we already hold it in the $text array.
|
||||
$row->text = '';
|
||||
|
||||
if ($style === 'pages') {
|
||||
// Display TOC.
|
||||
if ($hasToc) {
|
||||
$this->createToc($row, $matches, $page);
|
||||
} else {
|
||||
$row->toc = '';
|
||||
}
|
||||
|
||||
// Traditional mos page navigation
|
||||
$pageNav = new Pagination($n, $page, 1);
|
||||
|
||||
// Flag indicates to not add limitstart=0 to URL
|
||||
$pageNav->hideEmptyLimitstart = true;
|
||||
|
||||
// Page counter.
|
||||
$row->text .= '<div class="pagenavcounter">';
|
||||
$row->text .= $pageNav->getPagesCounter();
|
||||
$row->text .= '</div>';
|
||||
|
||||
// Page text.
|
||||
$text[$page] = str_replace('<hr id="system-readmore" />', '', $text[$page]);
|
||||
$row->text .= $text[$page];
|
||||
|
||||
// $row->text .= '<br>';
|
||||
$row->text .= '<div class="pager">';
|
||||
|
||||
// Adds navigation between pages to bottom of text.
|
||||
if ($hasToc) {
|
||||
$this->createNavigation($row, $page, $n);
|
||||
}
|
||||
|
||||
// Page links shown at bottom of page if TOC disabled.
|
||||
if (!$hasToc) {
|
||||
$row->text .= $pageNav->getPagesLinks();
|
||||
}
|
||||
|
||||
$row->text .= '</div>';
|
||||
} else {
|
||||
$t[] = $text[0];
|
||||
|
||||
if ($style === 'tabs') {
|
||||
$t[] = (string) HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'article' . $row->id . '-' . $style . '0', 'view' => 'tabs']);
|
||||
} else {
|
||||
$t[] = (string) HTMLHelper::_('bootstrap.startAccordion', 'myAccordion', ['active' => 'article' . $row->id . '-' . $style . '0']);
|
||||
}
|
||||
|
||||
foreach ($text as $key => $subtext) {
|
||||
$index = 'article' . $row->id . '-' . $style . $key;
|
||||
|
||||
if ($key >= 1) {
|
||||
$match = $matches[$key - 1];
|
||||
$match = (array) Utility::parseAttributes($match[0]);
|
||||
|
||||
if (isset($match['alt'])) {
|
||||
$title = stripslashes($match['alt']);
|
||||
} elseif (isset($match['title'])) {
|
||||
$title = stripslashes($match['title']);
|
||||
} else {
|
||||
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $key + 1);
|
||||
}
|
||||
|
||||
if ($style === 'tabs') {
|
||||
$t[] = (string) HTMLHelper::_('uitab.addTab', 'myTab', $index, $title);
|
||||
} else {
|
||||
$t[] = (string) HTMLHelper::_('bootstrap.addSlide', 'myAccordion', $title, $index);
|
||||
}
|
||||
|
||||
$t[] = (string) $subtext;
|
||||
|
||||
if ($style === 'tabs') {
|
||||
$t[] = (string) HTMLHelper::_('uitab.endTab');
|
||||
} else {
|
||||
$t[] = (string) HTMLHelper::_('bootstrap.endSlide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($style === 'tabs') {
|
||||
$t[] = (string) HTMLHelper::_('uitab.endTabSet');
|
||||
} else {
|
||||
$t[] = (string) HTMLHelper::_('bootstrap.endAccordion');
|
||||
}
|
||||
|
||||
$row->text = implode(' ', $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Table of Contents for the pagebreak
|
||||
*
|
||||
* @param object &$row The article object. Note $article->text is also available
|
||||
* @param array &$matches Array of matches of a regex in onContentPrepare
|
||||
* @param integer &$page The 'page' number
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
private function createToc(&$row, &$matches, &$page)
|
||||
{
|
||||
$heading = $row->title ?? $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_NO_TITLE');
|
||||
$input = $this->getApplication()->getInput();
|
||||
$limitstart = $input->getUint('limitstart', 0);
|
||||
$showall = $input->getInt('showall', 0);
|
||||
$headingtext = '';
|
||||
|
||||
if ($this->params->get('article_index', 1) == 1) {
|
||||
$headingtext = $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_ARTICLE_INDEX');
|
||||
|
||||
if ($this->params->get('article_index_text')) {
|
||||
$headingtext = htmlspecialchars($this->params->get('article_index_text'), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
// TOC first Page link.
|
||||
$this->list[1] = new \stdClass();
|
||||
$this->list[1]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language);
|
||||
$this->list[1]->title = $heading;
|
||||
$this->list[1]->active = ($limitstart === 0 && $showall === 0);
|
||||
|
||||
$i = 2;
|
||||
|
||||
foreach ($matches as $bot) {
|
||||
if (@$bot[0]) {
|
||||
$attrs2 = Utility::parseAttributes($bot[0]);
|
||||
|
||||
if (@$attrs2['alt']) {
|
||||
$title = stripslashes($attrs2['alt']);
|
||||
} elseif (@$attrs2['title']) {
|
||||
$title = stripslashes($attrs2['title']);
|
||||
} else {
|
||||
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i);
|
||||
}
|
||||
} else {
|
||||
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i);
|
||||
}
|
||||
|
||||
$this->list[$i] = new \stdClass();
|
||||
$this->list[$i]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($i - 1);
|
||||
$this->list[$i]->title = $title;
|
||||
$this->list[$i]->active = ($limitstart === $i - 1);
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($this->params->get('showall')) {
|
||||
$this->list[$i] = new \stdClass();
|
||||
$this->list[$i]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&showall=1';
|
||||
$this->list[$i]->title = $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_ALL_PAGES');
|
||||
$this->list[$i]->active = ($limitstart === $i - 1);
|
||||
}
|
||||
|
||||
$list = $this->list;
|
||||
$path = PluginHelper::getLayoutPath('content', 'pagebreak', 'toc');
|
||||
ob_start();
|
||||
include $path;
|
||||
$row->toc = ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the navigation for the item
|
||||
*
|
||||
* @param object &$row The article object. Note $article->text is also available
|
||||
* @param int $page The page number
|
||||
* @param int $n The total number of pages
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
private function createNavigation(&$row, $page, $n)
|
||||
{
|
||||
$links = [
|
||||
'next' => '',
|
||||
'previous' => '',
|
||||
];
|
||||
|
||||
if ($page < $n - 1) {
|
||||
$links['next'] = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($page + 1);
|
||||
}
|
||||
|
||||
if ($page > 0) {
|
||||
$links['previous'] = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language);
|
||||
|
||||
if ($page > 1) {
|
||||
$links['previous'] .= '&limitstart=' . ($page - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$path = PluginHelper::getLayoutPath('content', 'pagebreak', 'navigation');
|
||||
ob_start();
|
||||
include $path;
|
||||
$row->text .= ob_get_clean();
|
||||
}
|
||||
}
|
||||
46
plugins/content/pagebreak/tmpl/navigation.php
Normal file
46
plugins/content/pagebreak/tmpl/navigation.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagebreak
|
||||
*
|
||||
* @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\Language\Text;
|
||||
use Joomla\CMS\Router\Route;
|
||||
|
||||
/**
|
||||
* @var $links array Array with keys 'previous' and 'next' with non-SEO links to the previous and next pages
|
||||
* @var $page integer The page number
|
||||
*/
|
||||
|
||||
$lang = $this->getApplication()->getLanguage();
|
||||
?>
|
||||
<ul class="pagination">
|
||||
<li class="previous page-item">
|
||||
<?php if ($links['previous']) :
|
||||
$direction = $lang->isRtl() ? 'right' : 'left';
|
||||
$title = htmlspecialchars($this->list[$page]->title, ENT_QUOTES, 'UTF-8');
|
||||
$ariaLabel = Text::_('JPREVIOUS') . ': ' . $title . ' (' . Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $page, $n) . ')';
|
||||
?>
|
||||
<a class="page-link" href="<?php echo Route::_($links['previous']); ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="prev">
|
||||
<?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> ' . Text::_('JPREV'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<li class="next page-item">
|
||||
<?php if ($links['next']) :
|
||||
$direction = $lang->isRtl() ? 'left' : 'right';
|
||||
$title = htmlspecialchars($this->list[$page + 2]->title, ENT_QUOTES, 'UTF-8');
|
||||
$ariaLabel = Text::_('JNEXT') . ': ' . $title . ' (' . Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', ($page + 2), $n) . ')';
|
||||
?>
|
||||
<a class="page-link" href="<?php echo Route::_($links['next']); ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="next">
|
||||
<?php echo Text::_('JNEXT') . ' <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
</ul>
|
||||
34
plugins/content/pagebreak/tmpl/toc.php
Normal file
34
plugins/content/pagebreak/tmpl/toc.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagebreak
|
||||
*
|
||||
* @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\Router\Route;
|
||||
|
||||
?>
|
||||
<div class="card float-end article-index ms-3 mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
<?php if ($headingtext) : ?>
|
||||
<h3><?php echo $headingtext; ?></h3>
|
||||
<?php endif; ?>
|
||||
|
||||
<ul class="nav flex-column">
|
||||
<?php foreach ($list as $listItem) : ?>
|
||||
<?php $class = $listItem->active ? ' active' : ''; ?>
|
||||
<li class="py-1">
|
||||
<a href="<?php echo Route::_($listItem->link); ?>" class="toclink<?php echo $class; ?>">
|
||||
<?php echo $listItem->title; ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
65
plugins/content/pagenavigation/pagenavigation.xml
Normal file
65
plugins/content/pagenavigation/pagenavigation.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_pagenavigation</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2006-01</creationDate>
|
||||
<copyright>(C) 2006 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_PAGENAVIGATION_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\PageNavigation</namespace>
|
||||
<files>
|
||||
<folder plugin="pagenavigation">services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>tmpl</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_pagenavigation.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_pagenavigation.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="position"
|
||||
type="list"
|
||||
label="PLG_PAGENAVIGATION_FIELD_POSITION_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
validate="options"
|
||||
>
|
||||
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_BELOW</option>
|
||||
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_ABOVE</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="relative"
|
||||
type="list"
|
||||
label="PLG_PAGENAVIGATION_FIELD_RELATIVE_LABEL"
|
||||
default="1"
|
||||
filter="integer"
|
||||
validate="options"
|
||||
>
|
||||
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_ARTICLE</option>
|
||||
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_TEXT</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="display"
|
||||
type="list"
|
||||
label="PLG_PAGENAVIGATION_FIELD_DISPLAY_LABEL"
|
||||
default="0"
|
||||
filter="integer"
|
||||
validate="options"
|
||||
>
|
||||
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_NEXTPREV</option>
|
||||
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_TITLE</option>
|
||||
</field>
|
||||
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
48
plugins/content/pagenavigation/services/provider.php
Normal file
48
plugins/content/pagenavigation/services/provider.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagenavigation
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\PageNavigation\Extension\PageNavigation;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new PageNavigation(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'pagenavigation')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
$plugin->setDatabase($container->get(DatabaseInterface::class));
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
247
plugins/content/pagenavigation/src/Extension/PageNavigation.php
Normal file
247
plugins/content/pagenavigation/src/Extension/PageNavigation.php
Normal file
@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagenavigation
|
||||
*
|
||||
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\PageNavigation\Extension;
|
||||
|
||||
use Joomla\CMS\Access\Access;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Component\Content\Site\Helper\RouteHelper;
|
||||
use Joomla\Database\DatabaseAwareTrait;
|
||||
use Joomla\Database\ParameterType;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Pagenavigation plugin class.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final class PageNavigation extends CMSPlugin
|
||||
{
|
||||
use DatabaseAwareTrait;
|
||||
|
||||
/**
|
||||
* If in the article view and the parameter is enabled shows the page navigation
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin
|
||||
* @param object &$row The article object
|
||||
* @param mixed &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public function onContentBeforeDisplay($context, &$row, &$params, $page = 0)
|
||||
{
|
||||
$app = $this->getApplication();
|
||||
$view = $app->getInput()->get('view');
|
||||
$print = $app->getInput()->getBool('print');
|
||||
|
||||
if ($print) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($context === 'com_content.article' && $view === 'article' && $params->get('show_item_navigation')) {
|
||||
$db = $this->getDatabase();
|
||||
$user = $app->getIdentity();
|
||||
$lang = $app->getLanguage();
|
||||
$now = Factory::getDate()->toSql();
|
||||
$query = $db->getQuery(true);
|
||||
$uid = $row->id;
|
||||
$option = 'com_content';
|
||||
$canPublish = $user->authorise('core.edit.state', $option . '.article.' . $row->id);
|
||||
|
||||
/**
|
||||
* The following is needed as different menu items types utilise a different param to control ordering.
|
||||
* For Blogs the `orderby_sec` param is the order controlling param.
|
||||
* For Table and List views it is the `orderby` param.
|
||||
*/
|
||||
$params_list = $params->toArray();
|
||||
|
||||
if (\array_key_exists('orderby_sec', $params_list)) {
|
||||
$order_method = $params->get('orderby_sec', '');
|
||||
} else {
|
||||
$order_method = $params->get('orderby', '');
|
||||
}
|
||||
|
||||
// Additional check for invalid sort ordering.
|
||||
if ($order_method === 'front') {
|
||||
$order_method = '';
|
||||
}
|
||||
|
||||
if (\in_array($order_method, ['date', 'rdate'])) {
|
||||
// Get the order code
|
||||
$orderDate = $params->get('order_date');
|
||||
|
||||
switch ($orderDate) {
|
||||
// Use created if modified is not set
|
||||
case 'modified':
|
||||
$orderby = 'CASE WHEN ' . $db->quoteName('a.modified') . ' IS NULL THEN ' .
|
||||
$db->quoteName('a.created') . ' ELSE ' . $db->quoteName('a.modified') . ' END';
|
||||
break;
|
||||
|
||||
// Use created if publish_up is not set
|
||||
case 'published':
|
||||
$orderby = 'CASE WHEN ' . $db->quoteName('a.publish_up') . ' IS NULL THEN ' .
|
||||
$db->quoteName('a.created') . ' ELSE ' . $db->quoteName('a.publish_up') . ' END';
|
||||
break;
|
||||
|
||||
// Use created as default
|
||||
default:
|
||||
$orderby = $db->quoteName('a.created');
|
||||
break;
|
||||
}
|
||||
|
||||
if ($order_method === 'rdate') {
|
||||
$orderby .= ' DESC';
|
||||
}
|
||||
} else {
|
||||
// Determine sort order.
|
||||
switch ($order_method) {
|
||||
case 'alpha':
|
||||
$orderby = $db->quoteName('a.title');
|
||||
break;
|
||||
case 'ralpha':
|
||||
$orderby = $db->quoteName('a.title') . ' DESC';
|
||||
break;
|
||||
case 'hits':
|
||||
$orderby = $db->quoteName('a.hits');
|
||||
break;
|
||||
case 'rhits':
|
||||
$orderby = $db->quoteName('a.hits') . ' DESC';
|
||||
break;
|
||||
case 'author':
|
||||
$orderby = $db->quoteName(['a.created_by_alias', 'u.name']);
|
||||
break;
|
||||
case 'rauthor':
|
||||
$orderby = $db->quoteName('a.created_by_alias') . ' DESC, ' .
|
||||
$db->quoteName('u.name') . ' DESC';
|
||||
break;
|
||||
case 'front':
|
||||
$orderby = $db->quoteName('f.ordering');
|
||||
break;
|
||||
default:
|
||||
$orderby = $db->quoteName('a.ordering');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$query->order($orderby);
|
||||
|
||||
$case_when = ' CASE WHEN ' . $query->charLength($db->quoteName('a.alias'), '!=', '0')
|
||||
. ' THEN ' . $query->concatenate([$query->castAsChar($db->quoteName('a.id')), $db->quoteName('a.alias')], ':')
|
||||
. ' ELSE ' . $query->castAsChar('a.id') . ' END AS ' . $db->quoteName('slug');
|
||||
|
||||
$case_when1 = ' CASE WHEN ' . $query->charLength($db->quoteName('cc.alias'), '!=', '0')
|
||||
. ' THEN ' . $query->concatenate([$query->castAsChar($db->quoteName('cc.id')), $db->quoteName('cc.alias')], ':')
|
||||
. ' ELSE ' . $query->castAsChar('cc.id') . ' END AS ' . $db->quoteName('catslug');
|
||||
|
||||
$query->select($db->quoteName(['a.id', 'a.title', 'a.catid', 'a.language']))
|
||||
->select([$case_when, $case_when1])
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__categories', 'cc'), $db->quoteName('cc.id') . ' = ' . $db->quoteName('a.catid'));
|
||||
|
||||
if ($order_method === 'author' || $order_method === 'rauthor') {
|
||||
$query->select($db->quoteName(['a.created_by', 'u.name']));
|
||||
$query->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'));
|
||||
}
|
||||
|
||||
$query->where(
|
||||
[
|
||||
$db->quoteName('a.catid') . ' = :catid',
|
||||
$db->quoteName('a.state') . ' = :state',
|
||||
]
|
||||
)
|
||||
->bind(':catid', $row->catid, ParameterType::INTEGER)
|
||||
->bind(':state', $row->state, ParameterType::INTEGER);
|
||||
|
||||
if (!$canPublish) {
|
||||
$query->whereIn($db->quoteName('a.access'), Access::getAuthorisedViewLevels($user->id));
|
||||
}
|
||||
|
||||
$query->where(
|
||||
[
|
||||
'(' . $db->quoteName('publish_up') . ' IS NULL OR ' . $db->quoteName('publish_up') . ' <= :nowDate1)',
|
||||
'(' . $db->quoteName('publish_down') . ' IS NULL OR ' . $db->quoteName('publish_down') . ' >= :nowDate2)',
|
||||
]
|
||||
)
|
||||
->bind(':nowDate1', $now)
|
||||
->bind(':nowDate2', $now);
|
||||
|
||||
if ($app->isClient('site') && $app->getLanguageFilter()) {
|
||||
$query->whereIn($db->quoteName('a.language'), [$lang->getTag(), '*'], ParameterType::STRING);
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
$list = $db->loadObjectList('id');
|
||||
|
||||
// This check needed if incorrect Itemid is given resulting in an incorrect result.
|
||||
if (!\is_array($list)) {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
reset($list);
|
||||
|
||||
// Location of current content item in array list.
|
||||
$location = array_search($uid, array_keys($list));
|
||||
$rows = array_values($list);
|
||||
|
||||
$row->prev = null;
|
||||
$row->next = null;
|
||||
|
||||
if ($location - 1 >= 0) {
|
||||
// The previous content item cannot be in the array position -1.
|
||||
$row->prev = $rows[$location - 1];
|
||||
}
|
||||
|
||||
if (($location + 1) < \count($rows)) {
|
||||
// The next content item cannot be in an array position greater than the number of array positions.
|
||||
$row->next = $rows[$location + 1];
|
||||
}
|
||||
|
||||
if ($row->prev) {
|
||||
$row->prev_label = ($this->params->get('display', 0) == 0) ? $lang->_('JPREV') : $row->prev->title;
|
||||
$row->prev = RouteHelper::getArticleRoute($row->prev->slug, $row->prev->catid, $row->prev->language);
|
||||
} else {
|
||||
$row->prev_label = '';
|
||||
$row->prev = '';
|
||||
}
|
||||
|
||||
if ($row->next) {
|
||||
$row->next_label = ($this->params->get('display', 0) == 0) ? $lang->_('JNEXT') : $row->next->title;
|
||||
$row->next = RouteHelper::getArticleRoute($row->next->slug, $row->next->catid, $row->next->language);
|
||||
} else {
|
||||
$row->next_label = '';
|
||||
$row->next = '';
|
||||
}
|
||||
|
||||
// Output.
|
||||
if ($row->prev || $row->next) {
|
||||
// Get the path for the layout file
|
||||
$path = PluginHelper::getLayoutPath('content', 'pagenavigation');
|
||||
|
||||
// Render the pagenav
|
||||
ob_start();
|
||||
include $path;
|
||||
$row->pagination = ob_get_clean();
|
||||
|
||||
$row->paginationposition = $this->params->get('position', 1);
|
||||
|
||||
// This will default to the 1.5 and 1.6-1.7 behavior.
|
||||
$row->paginationrelative = $this->params->get('relative', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
plugins/content/pagenavigation/tmpl/default.php
Normal file
40
plugins/content/pagenavigation/tmpl/default.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.pagenavigation
|
||||
*
|
||||
* @copyright (C) 2013 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\Router\Route;
|
||||
|
||||
$lang = $this->getLanguage();
|
||||
?>
|
||||
|
||||
<nav class="pagenavigation">
|
||||
<span class="pagination ms-0">
|
||||
<?php if ($row->prev) :
|
||||
$direction = $lang->isRtl() ? 'right' : 'left'; ?>
|
||||
<a class="btn btn-sm btn-secondary previous" href="<?php echo Route::_($row->prev); ?>" rel="prev">
|
||||
<span class="visually-hidden">
|
||||
<?php echo Text::sprintf('JPREVIOUS_TITLE', htmlspecialchars($rows[$location - 1]->title)); ?>
|
||||
</span>
|
||||
<?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> <span aria-hidden="true">' . $row->prev_label . '</span>'; ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($row->next) :
|
||||
$direction = $lang->isRtl() ? 'left' : 'right'; ?>
|
||||
<a class="btn btn-sm btn-secondary next" href="<?php echo Route::_($row->next); ?>" rel="next">
|
||||
<span class="visually-hidden">
|
||||
<?php echo Text::sprintf('JNEXT_TITLE', htmlspecialchars($rows[$location + 1]->title)); ?>
|
||||
</span>
|
||||
<?php echo '<span aria-hidden="true">' . $row->next_label . '</span> <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</nav>
|
||||
46
plugins/content/vote/services/provider.php
Normal file
46
plugins/content/vote/services/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.vote
|
||||
*
|
||||
* @copyright (C) 2023 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\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\Content\Vote\Extension\Vote;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Vote(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('content', 'vote')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
126
plugins/content/vote/src/Extension/Vote.php
Normal file
126
plugins/content/vote/src/Extension/Vote.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.vote
|
||||
*
|
||||
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\Content\Vote\Extension;
|
||||
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Vote plugin.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final class Vote extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* @var \Joomla\CMS\Application\CMSApplication
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @deprecated 4.4.0 will be removed in 6.0 as it is there only for layout overrides
|
||||
* Use getApplication() instead
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Displays the voting area when viewing an article and the voting section is displayed before the article
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin
|
||||
* @param object &$row The article object
|
||||
* @param object &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return string|boolean HTML string containing code for the votes if in com_content else boolean false
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public function onContentBeforeDisplay($context, &$row, &$params, $page = 0)
|
||||
{
|
||||
if ($this->params->get('position', 'top') !== 'top') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->displayVotingData($context, $row, $params, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the voting area when viewing an article and the voting section is displayed after the article
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin
|
||||
* @param object &$row The article object
|
||||
* @param object &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return string|boolean HTML string containing code for the votes if in com_content else boolean false
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function onContentAfterDisplay($context, &$row, &$params, $page = 0)
|
||||
{
|
||||
if ($this->params->get('position', 'top') !== 'bottom') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->displayVotingData($context, $row, $params, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the voting area
|
||||
*
|
||||
* @param string $context The context of the content being passed to the plugin
|
||||
* @param object &$row The article object
|
||||
* @param object &$params The article params
|
||||
* @param integer $page The 'page' number
|
||||
*
|
||||
* @return string HTML string containing code for the votes if in com_content else empty string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private function displayVotingData($context, &$row, &$params, $page)
|
||||
{
|
||||
$parts = explode('.', $context);
|
||||
|
||||
if ($parts[0] !== 'com_content') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (empty($params) || !$params->get('show_vote', null)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Load plugin language files only when needed (ex: they are not needed if show_vote is not active).
|
||||
$this->loadLanguage();
|
||||
|
||||
// Get the path for the rating summary layout file
|
||||
$path = PluginHelper::getLayoutPath('content', 'vote', 'rating');
|
||||
|
||||
// Render the layout
|
||||
ob_start();
|
||||
include $path;
|
||||
$html = ob_get_clean();
|
||||
|
||||
if ($this->getApplication()->getInput()->getString('view', '') === 'article' && $row->state == 1) {
|
||||
// Get the path for the voting form layout file
|
||||
$path = PluginHelper::getLayoutPath('content', 'vote', 'vote');
|
||||
|
||||
// Render the layout
|
||||
ob_start();
|
||||
include $path;
|
||||
$html .= ob_get_clean();
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
94
plugins/content/vote/tmpl/rating.php
Normal file
94
plugins/content/vote/tmpl/rating.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.vote
|
||||
*
|
||||
* @copyright (C) 2016 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\Uri\Uri;
|
||||
|
||||
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
|
||||
$wa = $this->getApplication()->getDocument()->getWebAssetManager();
|
||||
$wa->registerAndUseStyle('plg_content_vote', 'plg_content_vote/rating.css');
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var string $context The context of the content being passed to the plugin
|
||||
* @var object &$row The article object
|
||||
* @var object &$params The article params
|
||||
* @var integer $page The 'page' number
|
||||
* @var array $parts The context segments
|
||||
* @var string $path Path to this file
|
||||
*/
|
||||
|
||||
if ($context === 'com_content.categories') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the icons
|
||||
$iconStar = HTMLHelper::_('image', 'plg_content_vote/vote-star.svg', '', '', true, true);
|
||||
$iconHalfstar = HTMLHelper::_('image', 'plg_content_vote/vote-star-half.svg', '', '', true, true);
|
||||
|
||||
// If you can't find the icons then skip it
|
||||
if ($iconStar === null || $iconHalfstar === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get paths to icons
|
||||
$pathStar = JPATH_ROOT . substr($iconStar, strlen(Uri::root(true)));
|
||||
$pathHalfstar = JPATH_ROOT . substr($iconHalfstar, strlen(Uri::root(true)));
|
||||
|
||||
// Write inline '<svg>' elements
|
||||
$star = file_exists($pathStar) ? file_get_contents($pathStar) : '';
|
||||
$halfstar = file_exists($pathHalfstar) ? file_get_contents($pathHalfstar) : '';
|
||||
|
||||
// Get rating
|
||||
$rating = (float) $row->rating;
|
||||
$rcount = (int) $row->rating_count;
|
||||
|
||||
// Round to 0.5
|
||||
$rating = round($rating / 0.5) * 0.5;
|
||||
|
||||
// Determine number of stars
|
||||
$stars = $rating;
|
||||
$img = '';
|
||||
|
||||
for ($i = 0; $i < floor($stars); $i++) {
|
||||
$img .= '<li class="vote-star">' . $star . '</li>';
|
||||
}
|
||||
|
||||
if (($stars - floor($stars)) >= 0.5) {
|
||||
$img .= '<li class="vote-star-empty">' . $star . '</li>';
|
||||
$img .= '<li class="vote-star-half">' . $halfstar . '</li>';
|
||||
|
||||
++$stars;
|
||||
}
|
||||
|
||||
for ($i = $stars; $i < 5; $i++) {
|
||||
$img .= '<li class="vote-star-empty">' . $star . '</li>';
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="content_rating" role="img" aria-label="<?php echo Text::sprintf('PLG_VOTE_STAR_RATING', $rating); ?>">
|
||||
<?php if ($rcount) : ?>
|
||||
<p class="visually-hidden" itemprop="aggregateRating" itemscope itemtype="https://schema.org/AggregateRating">
|
||||
<?php echo Text::sprintf('PLG_VOTE_USER_RATING', '<span itemprop="ratingValue">' . $rating . '</span>', '<span itemprop="bestRating">5</span>'); ?>
|
||||
<meta itemprop="ratingCount" content="<?php echo $rcount; ?>">
|
||||
<meta itemprop="worstRating" content="1">
|
||||
</p>
|
||||
<?php if ($this->params->get('show_total_votes', 0)) : ?>
|
||||
<?php echo Text::sprintf('PLG_VOTE_TOTAL_VOTES', $rcount); ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<ul>
|
||||
<?php echo $img; ?>
|
||||
</ul>
|
||||
</div>
|
||||
48
plugins/content/vote/tmpl/vote.php
Normal file
48
plugins/content/vote/tmpl/vote.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage Content.vote
|
||||
*
|
||||
* @copyright (C) 2016 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\Uri\Uri;
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var string $context The context of the content being passed to the plugin
|
||||
* @var object &$row The article object
|
||||
* @var object &$params The article params
|
||||
* @var integer $page The 'page' number
|
||||
* @var array $parts The context segments
|
||||
* @var string $path Path to this file
|
||||
*/
|
||||
|
||||
$uri = clone Uri::getInstance();
|
||||
|
||||
// Create option list for voting select box
|
||||
$options = [];
|
||||
|
||||
for ($i = 1; $i < 6; $i++) {
|
||||
$options[] = HTMLHelper::_('select.option', $i, Text::sprintf('PLG_VOTE_VOTE', $i));
|
||||
}
|
||||
|
||||
?>
|
||||
<form method="post" action="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>" class="form-inline mb-2">
|
||||
<span class="content_vote">
|
||||
<label class="visually-hidden" for="content_vote_<?php echo (int) $row->id; ?>"><?php echo Text::_('PLG_VOTE_LABEL'); ?></label>
|
||||
<?php echo HTMLHelper::_('select.genericlist', $options, 'user_rating', 'class="form-select form-select-sm w-auto"', 'value', 'text', '5', 'content_vote_' . (int) $row->id); ?>
|
||||
<input class="btn btn-sm btn-primary align-baseline" type="submit" name="submit_vote" value="<?php echo Text::_('PLG_VOTE_RATE'); ?>">
|
||||
<input type="hidden" name="task" value="article.vote">
|
||||
<input type="hidden" name="hitcount" value="0">
|
||||
<input type="hidden" name="url" value="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>">
|
||||
<?php echo HTMLHelper::_('form.token'); ?>
|
||||
</span>
|
||||
</form>
|
||||
49
plugins/content/vote/vote.xml
Normal file
49
plugins/content/vote/vote.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="content" method="upgrade">
|
||||
<name>plg_content_vote</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2005-11</creationDate>
|
||||
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_VOTE_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\Content\Vote</namespace>
|
||||
<files>
|
||||
<folder plugin="vote">services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>tmpl</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_content_vote.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_content_vote.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="position"
|
||||
type="list"
|
||||
label="PLG_VOTE_POSITION_LABEL"
|
||||
default="top"
|
||||
validate="options"
|
||||
>
|
||||
<option value="top">PLG_VOTE_TOP</option>
|
||||
<option value="bottom">PLG_VOTE_BOTTOM</option>
|
||||
</field>
|
||||
<field
|
||||
name="show_total_votes"
|
||||
type="radio"
|
||||
label="PLG_VOTE_TOTAL_VOTES_LABEL"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="0"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
Reference in New Issue
Block a user