primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,85 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Controller;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Response\JsonResponse;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Table\Table;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* The article controller for ajax requests
*
* @since 3.9.0
*/
class AjaxController extends BaseController
{
/**
* Method to fetch associations of an article
*
* The method assumes that the following http parameters are passed in an Ajax Get request:
* token: the form token
* assocId: the id of the article whose associations are to be returned
* excludeLang: the association for this language is to be excluded
*
* @return null
*
* @since 3.9.0
*/
public function fetchAssociations()
{
if (!Session::checkToken('get')) {
echo new JsonResponse(null, Text::_('JINVALID_TOKEN'), true);
} else {
$assocId = $this->input->getInt('assocId', 0);
if ($assocId == 0) {
echo new JsonResponse(null, Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', 'assocId'), true);
return;
}
$excludeLang = $this->input->get('excludeLang', '', 'STRING');
$associations = Associations::getAssociations('com_content', '#__content', 'com_content.item', (int) $assocId);
unset($associations[$excludeLang]);
// Add the title to each of the associated records
$contentTable = Table::getInstance('Content', '\\Joomla\\CMS\\Table\\');
foreach ($associations as $association) {
$contentTable->load($association->id);
$association->title = $contentTable->title;
}
$countContentLanguages = \count(LanguageHelper::getContentLanguages([0, 1], false));
if (\count($associations) == 0) {
$message = Text::_('JGLOBAL_ASSOCIATIONS_PROPAGATE_MESSAGE_NONE');
} elseif ($countContentLanguages > \count($associations) + 2) {
$tags = implode(', ', array_keys($associations));
$message = Text::sprintf('JGLOBAL_ASSOCIATIONS_PROPAGATE_MESSAGE_SOME', $tags);
} else {
$message = Text::_('JGLOBAL_ASSOCIATIONS_PROPAGATE_MESSAGE_ALL');
}
echo new JsonResponse($associations, $message);
}
}
}

View File

@ -0,0 +1,213 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Controller;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Versioning\VersionableControllerTrait;
use Joomla\Input\Input;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* The article controller
*
* @since 1.6
*/
class ArticleController extends FormController
{
use VersionableControllerTrait;
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* Recognized key values include 'name', 'default_task', 'model_path', and
* 'view_path' (this list is not meant to be comprehensive).
* @param ?MVCFactoryInterface $factory The factory.
* @param ?CMSApplication $app The Application for the dispatcher
* @param ?Input $input Input
*
* @since 3.0
*/
public function __construct($config = [], ?MVCFactoryInterface $factory = null, $app = null, $input = null)
{
parent::__construct($config, $factory, $app, $input);
// An article edit form can come from the articles or featured view.
// Adjust the redirect view on the value of 'return' in the request.
if ($this->input->get('return') == 'featured') {
$this->view_list = 'featured';
$this->view_item = 'article&return=featured';
}
}
/**
* Method to cancel an edit.
*
* @param string $key The name of the primary key of the URL variable.
*
* @return boolean True if access level checks pass, false otherwise.
*
* @since 5.0.0
*/
public function cancel($key = null)
{
$result = parent::cancel($key);
// When editing in modal then redirect to modalreturn layout
if ($result && $this->input->get('layout') === 'modal') {
$id = $this->input->get('id');
$return = 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($id)
. '&layout=modalreturn&from-task=cancel';
$this->setRedirect(Route::_($return, false));
}
return $result;
}
/**
* Function that allows child controller access to model data
* after the data has been saved.
*
* @param BaseDatabaseModel $model The data model object.
* @param array $validData The validated data.
*
* @return void
*
* @since 4.0.0
*/
protected function postSaveHook(BaseDatabaseModel $model, $validData = [])
{
if ($this->getTask() === 'save2menu') {
$editState = [];
$id = $model->getState('article.id');
$link = 'index.php?option=com_content&view=article';
$type = 'component';
$editState['id'] = $id;
$editState['link'] = $link;
$editState['title'] = $model->getItem($id)->title;
$editState['type'] = $type;
$editState['request']['id'] = $id;
$this->app->setUserState('com_menus.edit.item', [
'data' => $editState,
'type' => $type,
'link' => $link,
]);
$this->setRedirect(Route::_('index.php?option=com_menus&view=item&client_id=0&menutype=mainmenu&layout=edit', false));
} elseif ($this->input->get('layout') === 'modal' && $this->task === 'save') {
// When editing in modal then redirect to modalreturn layout
$id = $model->getState('article.id', '');
$return = 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($id)
. '&layout=modalreturn&from-task=save';
$this->setRedirect(Route::_($return, false));
}
}
/**
* Method override to check if you can add a new record.
*
* @param array $data An array of input data.
*
* @return boolean
*
* @since 1.6
*/
protected function allowAdd($data = [])
{
$categoryId = ArrayHelper::getValue($data, 'catid', $this->input->getInt('filter_category_id'), 'int');
if ($categoryId) {
// If the category has been passed in the data or URL check it.
return $this->app->getIdentity()->authorise('core.create', 'com_content.category.' . $categoryId);
}
// In the absence of better information, revert to the component permissions.
return parent::allowAdd();
}
/**
* Method override to check if you can edit an existing record.
*
* @param array $data An array of input data.
* @param string $key The name of the key for the primary key.
*
* @return boolean
*
* @since 1.6
*/
protected function allowEdit($data = [], $key = 'id')
{
$recordId = (int) isset($data[$key]) ? $data[$key] : 0;
$user = $this->app->getIdentity();
// Zero record (id:0), return component edit permission by calling parent controller method
if (!$recordId) {
return parent::allowEdit($data, $key);
}
// Check edit on the record asset (explicit or inherited)
if ($user->authorise('core.edit', 'com_content.article.' . $recordId)) {
return true;
}
// Check edit own on the record asset (explicit or inherited)
if ($user->authorise('core.edit.own', 'com_content.article.' . $recordId)) {
// Existing record already has an owner, get it
$record = $this->getModel()->getItem($recordId);
if (empty($record)) {
return false;
}
// Grant if current user is owner of the record
return $user->id == $record->created_by;
}
return false;
}
/**
* Method to run batch operations.
*
* @param object $model The model.
*
* @return boolean True if successful, false otherwise and internal error is set.
*
* @since 1.6
*/
public function batch($model = null)
{
$this->checkToken();
// Set the model
/** @var \Joomla\Component\Content\Administrator\Model\ArticleModel $model */
$model = $this->getModel('Article', 'Administrator', []);
// Preset the redirect
$this->setRedirect(Route::_('index.php?option=com_content&view=articles' . $this->getRedirectToListAppend(), false));
return parent::batch($model);
}
}

View File

@ -0,0 +1,160 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Controller;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Response\JsonResponse;
use Joomla\CMS\Router\Route;
use Joomla\Input\Input;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Articles list controller class.
*
* @since 1.6
*/
class ArticlesController extends AdminController
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* Recognized key values include 'name', 'default_task', 'model_path', and
* 'view_path' (this list is not meant to be comprehensive).
* @param ?MVCFactoryInterface $factory The factory.
* @param ?CMSApplication $app The Application for the dispatcher
* @param ?Input $input Input
*
* @since 3.0
*/
public function __construct($config = [], ?MVCFactoryInterface $factory = null, $app = null, $input = null)
{
parent::__construct($config, $factory, $app, $input);
// Articles default form can come from the articles or featured view.
// Adjust the redirect view on the value of 'view' in the request.
if ($this->input->get('view') == 'featured') {
$this->view_list = 'featured';
}
$this->registerTask('unfeatured', 'featured');
}
/**
* Method to toggle the featured setting of a list of articles.
*
* @return void
*
* @since 1.6
*/
public function featured()
{
// Check for request forgeries
$this->checkToken();
$user = $this->app->getIdentity();
$ids = (array) $this->input->get('cid', [], 'int');
$values = ['featured' => 1, 'unfeatured' => 0];
$task = $this->getTask();
$value = ArrayHelper::getValue($values, $task, 0, 'int');
$redirectUrl = 'index.php?option=com_content&view=' . $this->view_list . $this->getRedirectToListAppend();
// Access checks.
foreach ($ids as $i => $id) {
// Remove zero value resulting from input filter
if ($id === 0) {
unset($ids[$i]);
continue;
}
if (!$user->authorise('core.edit.state', 'com_content.article.' . (int) $id)) {
// Prune items that you can't change.
unset($ids[$i]);
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), 'notice');
}
}
if (empty($ids)) {
$this->app->enqueueMessage(Text::_('JERROR_NO_ITEMS_SELECTED'), 'error');
$this->setRedirect(Route::_($redirectUrl, false));
return;
}
// Get the model.
/** @var \Joomla\Component\Content\Administrator\Model\ArticleModel $model */
$model = $this->getModel();
// Publish the items.
if (!$model->featured($ids, $value)) {
$this->setRedirect(Route::_($redirectUrl, false), $model->getError(), 'error');
return;
}
if ($value == 1) {
$message = Text::plural('COM_CONTENT_N_ITEMS_FEATURED', \count($ids));
} else {
$message = Text::plural('COM_CONTENT_N_ITEMS_UNFEATURED', \count($ids));
}
$this->setRedirect(Route::_($redirectUrl, false), $message);
}
/**
* Proxy for getModel.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config The array of possible config values. Optional.
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel
*
* @since 1.6
*/
public function getModel($name = 'Article', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
/**
* Method to get the JSON-encoded amount of published articles
*
* @return void
*
* @since 4.0.0
*/
public function getQuickiconContent()
{
$model = $this->getModel('articles');
$model->setState('filter.published', 1);
$amount = (int) $model->getTotal();
$result = [];
$result['amount'] = $amount;
$result['sronly'] = Text::plural('COM_CONTENT_N_QUICKICON_SRONLY', $amount);
$result['name'] = Text::plural('COM_CONTENT_N_QUICKICON', $amount);
echo new JsonResponse($result);
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @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\Component\Content\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component Controller
*
* @since 1.5
*/
class DisplayController extends BaseController
{
/**
* The default view.
*
* @var string
* @since 1.6
*/
protected $default_view = 'articles';
/**
* Method to display a view.
*
* @param boolean $cachable If true, the view output will be cached
* @param array $urlparams An array of safe URL parameters and their variable types.
* @see \Joomla\CMS\Filter\InputFilter::clean() for valid values.
*
* @return BaseController|boolean This object to support chaining.
*
* @since 1.5
*/
public function display($cachable = false, $urlparams = [])
{
$view = $this->input->get('view', 'articles');
$layout = $this->input->get('layout', 'articles');
$id = $this->input->getInt('id');
// Check for edit form.
if ($view == 'article' && $layout == 'edit' && !$this->checkEditId('com_content.edit.article', $id)) {
// Somehow the person just went to the form - we don't allow that.
if (!\count($this->app->getMessageQueue())) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id), 'error');
}
$this->setRedirect(Route::_('index.php?option=com_content&view=articles', false));
return false;
}
return parent::display();
}
}

View File

@ -0,0 +1,127 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Response\JsonResponse;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Featured content controller class.
*
* @since 1.6
*/
class FeaturedController extends ArticlesController
{
/**
* Removes an item.
*
* @return void
*
* @since 1.6
*/
public function delete()
{
// Check for request forgeries
$this->checkToken();
$user = $this->app->getIdentity();
$ids = (array) $this->input->get('cid', [], 'int');
// Access checks.
foreach ($ids as $i => $id) {
// Remove zero value resulting from input filter
if ($id === 0) {
unset($ids[$i]);
continue;
}
if (!$user->authorise('core.delete', 'com_content.article.' . (int) $id)) {
// Prune items that you can't delete.
unset($ids[$i]);
$this->app->enqueueMessage(Text::_('JERROR_CORE_DELETE_NOT_PERMITTED'), 'notice');
}
}
if (empty($ids)) {
$this->app->enqueueMessage(Text::_('JERROR_NO_ITEMS_SELECTED'), 'error');
} else {
/** @var \Joomla\Component\Content\Administrator\Model\FeatureModel $model */
$model = $this->getModel();
// Remove the items.
if (!$model->featured($ids, 0)) {
$this->app->enqueueMessage($model->getError(), 'error');
}
}
$this->setRedirect('index.php?option=com_content&view=featured');
}
/**
* Method to publish a list of articles.
*
* @return void
*
* @since 1.0
*/
public function publish()
{
parent::publish();
$this->setRedirect('index.php?option=com_content&view=featured');
}
/**
* Method to get a model object, loading it if required.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since 1.6
*/
public function getModel($name = 'Feature', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
/**
* Method to get the number of published featured articles for quickicons
*
* @return void
*
* @since 4.3.0
*/
public function getQuickiconContent()
{
$model = $this->getModel('articles');
$model->setState('filter.published', 1);
$model->setState('filter.featured', 1);
$amount = (int) $model->getTotal();
$result = [];
$result['amount'] = $amount;
$result['sronly'] = Text::plural('COM_CONTENT_FEATURED_N_QUICKICON_SRONLY', $amount);
$result['name'] = Text::plural('COM_CONTENT_FEATURED_N_QUICKICON', $amount);
echo new JsonResponse($result);
}
}

View File

@ -0,0 +1,87 @@
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Event\Model;
use Joomla\CMS\Event\AbstractImmutableEvent;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Event class for WebAsset events
*
* @since 4.0.0
*/
class FeatureEvent extends AbstractImmutableEvent
{
/**
* Constructor.
*
* @param string $name The event name.
* @param array $arguments The event arguments.
*
* @throws \BadMethodCallException
*
* @since 4.0.0
*/
public function __construct($name, array $arguments = [])
{
if (!isset($arguments['extension'])) {
throw new \BadMethodCallException("Argument 'extension' of event $this->name is required but has not been provided");
}
if (!isset($arguments['extension']) || !\is_string($arguments['extension'])) {
throw new \BadMethodCallException("Argument 'extension' of event $this->name is not of type 'string'");
}
if (strpos($arguments['extension'], '.') === false) {
throw new \BadMethodCallException("Argument 'extension' of event $this->name has wrong format. Valid format: 'component.section'");
}
if (!\array_key_exists('extensionName', $arguments) || !\array_key_exists('section', $arguments)) {
$parts = explode('.', $arguments['extension']);
$arguments['extensionName'] = $arguments['extensionName'] ?? $parts[0];
$arguments['section'] = $arguments['section'] ?? $parts[1];
}
if (!isset($arguments['pks']) || !\is_array($arguments['pks'])) {
throw new \BadMethodCallException("Argument 'pks' of event $this->name is not of type 'array'");
}
if (!isset($arguments['value']) || !is_numeric($arguments['value'])) {
throw new \BadMethodCallException("Argument 'value' of event $this->name is not of type 'numeric'");
}
$arguments['value'] = (int) $arguments['value'];
if ($arguments['value'] !== 0 && $arguments['value'] !== 1) {
throw new \BadMethodCallException("Argument 'value' of event $this->name is not 0 or 1");
}
parent::__construct($name, $arguments);
}
/**
* Set abort and reason parameters.
*
* @param string $reason Abort reason
*
* @return void
*
* @since 4.0.0
*/
public function setAbort(string $reason)
{
$this->arguments['abort'] = true;
$this->arguments['abortReason'] = $reason;
}
}

View File

@ -0,0 +1,377 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Extension;
use Joomla\CMS\Association\AssociationServiceInterface;
use Joomla\CMS\Association\AssociationServiceTrait;
use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Categories\CategoryServiceTrait;
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\CMS\Component\Router\RouterServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\Factory;
use Joomla\CMS\Fields\FieldsFormServiceInterface;
use Joomla\CMS\Fields\FieldsServiceTrait;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Helper\ContentHelper as LibraryContentHelper;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Schemaorg\SchemaorgServiceInterface;
use Joomla\CMS\Schemaorg\SchemaorgServiceTrait;
use Joomla\CMS\Tag\TagServiceInterface;
use Joomla\CMS\Tag\TagServiceTrait;
use Joomla\CMS\Workflow\WorkflowServiceInterface;
use Joomla\CMS\Workflow\WorkflowServiceTrait;
use Joomla\Component\Content\Administrator\Helper\ContentHelper;
use Joomla\Component\Content\Administrator\Service\HTML\AdministratorService;
use Joomla\Component\Content\Administrator\Service\HTML\Icon;
use Psr\Container\ContainerInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component class for com_content
*
* @since 4.0.0
*/
class ContentComponent extends MVCComponent implements
BootableExtensionInterface,
CategoryServiceInterface,
FieldsFormServiceInterface,
AssociationServiceInterface,
SchemaorgServiceInterface,
WorkflowServiceInterface,
RouterServiceInterface,
TagServiceInterface
{
use AssociationServiceTrait;
use RouterServiceTrait;
use HTMLRegistryAwareTrait;
use WorkflowServiceTrait;
use SchemaorgServiceTrait;
use CategoryServiceTrait, TagServiceTrait, FieldsServiceTrait {
CategoryServiceTrait::getTableNameForSection insteadof TagServiceTrait;
CategoryServiceTrait::getStateColumnForSection insteadof TagServiceTrait;
CategoryServiceTrait::prepareForm insteadof FieldsServiceTrait;
}
/** @var array Supported functionality */
protected $supportedFunctionality = [
'core.featured' => true,
'core.state' => true,
];
/**
* The trashed condition
*
* @since 4.0.0
*/
public const CONDITION_NAMES = [
self::CONDITION_PUBLISHED => 'JPUBLISHED',
self::CONDITION_UNPUBLISHED => 'JUNPUBLISHED',
self::CONDITION_ARCHIVED => 'JARCHIVED',
self::CONDITION_TRASHED => 'JTRASHED',
];
/**
* The archived condition
*
* @since 4.0.0
*/
public const CONDITION_ARCHIVED = 2;
/**
* The published condition
*
* @since 4.0.0
*/
public const CONDITION_PUBLISHED = 1;
/**
* The unpublished condition
*
* @since 4.0.0
*/
public const CONDITION_UNPUBLISHED = 0;
/**
* The trashed condition
*
* @since 4.0.0
*/
public const CONDITION_TRASHED = -2;
/**
* Booting the extension. This is the function to set up the environment of the extension like
* registering new class loaders, etc.
*
* If required, some initial set up can be done from services of the container, eg.
* registering HTML services.
*
* @param ContainerInterface $container The container
*
* @return void
*
* @since 4.0.0
*/
public function boot(ContainerInterface $container)
{
$this->getRegistry()->register('contentadministrator', new AdministratorService());
$this->getRegistry()->register('contenticon', new Icon());
// The layout joomla.content.icons does need a general icon service
$this->getRegistry()->register('icon', $this->getRegistry()->getService('contenticon'));
}
/**
* Returns a valid section for the given section. If it is not valid then null
* is returned.
*
* @param string $section The section to get the mapping for
* @param object $item The item
*
* @return string|null The new section
*
* @since 4.0.0
*/
public function validateSection($section, $item = null)
{
if (Factory::getApplication()->isClient('site')) {
// On the front end we need to map some sections
switch ($section) {
// Editing an article
case 'form':
// Category list view
case 'featured':
case 'category':
$section = 'article';
}
}
if ($section != 'article') {
// We don't know other sections
return null;
}
return $section;
}
/**
* Returns valid contexts
*
* @return array
*
* @since 4.0.0
*/
public function getContexts(): array
{
Factory::getLanguage()->load('com_content', JPATH_ADMINISTRATOR);
$contexts = [
'com_content.article' => Text::_('COM_CONTENT'),
'com_content.categories' => Text::_('JCATEGORY'),
];
return $contexts;
}
/**
* Returns valid contexts for schemaorg
*
* @return array
*
* @since 5.0.0
*/
public function getSchemaorgContexts(): array
{
Factory::getLanguage()->load('com_content', JPATH_ADMINISTRATOR);
$contexts = [
'com_content.article' => Text::_('COM_CONTENT'),
];
return $contexts;
}
/**
* Returns valid contexts
*
* @return array
*
* @since 4.0.0
*/
public function getWorkflowContexts(): array
{
Factory::getLanguage()->load('com_content', JPATH_ADMINISTRATOR);
$contexts = [
'com_content.article' => Text::_('COM_CONTENT'),
];
return $contexts;
}
/**
* Returns the workflow context based on the given category section
*
* @param ?string $section The section
*
* @return string|null
*
* @since 4.0.0
*/
public function getCategoryWorkflowContext(?string $section = null): string
{
$context = $this->getWorkflowContexts();
return array_key_first($context);
}
/**
* Returns the table for the count items functions for the given section.
*
* @param ?string $section The section
*
* @return string|null
*
* @since 4.0.0
*/
protected function getTableNameForSection(?string $section = null)
{
return '#__content';
}
/**
* Returns a table name for the state association
*
* @param ?string $section An optional section to separate different areas in the component
*
* @return string
*
* @since 4.0.0
*/
public function getWorkflowTableBySection(?string $section = null): string
{
return '#__content';
}
/**
* Returns the model name, based on the context
*
* @param string $context The context of the workflow
*
* @return string
*/
public function getModelName($context): string
{
$parts = explode('.', $context);
if (\count($parts) < 2) {
return '';
}
array_shift($parts);
$modelname = array_shift($parts);
if ($modelname === 'article' && Factory::getApplication()->isClient('site')) {
return 'Form';
}
if ($modelname === 'featured' && Factory::getApplication()->isClient('administrator')) {
return 'Article';
}
return ucfirst($modelname);
}
/**
* Method to filter transitions by given id of state.
*
* @param array $transitions The Transitions to filter
* @param int $pk Id of the state
*
* @return array
*
* @since 4.0.0
*/
public function filterTransitions(array $transitions, int $pk): array
{
return ContentHelper::filterTransitions($transitions, $pk);
}
/**
* Adds Count Items for Category Manager.
*
* @param \stdClass[] $items The category objects
* @param string $section The section
*
* @return void
*
* @since 4.0.0
*/
public function countItems(array $items, string $section)
{
$config = (object) [
'related_tbl' => 'content',
'state_col' => 'state',
'group_col' => 'catid',
'relation_type' => 'category_or_group',
'uses_workflows' => true,
'workflows_component' => 'com_content',
];
LibraryContentHelper::countRelations($items, $config);
}
/**
* Adds Count Items for Tag Manager.
*
* @param \stdClass[] $items The content objects
* @param string $extension The name of the active view.
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
public function countTagItems(array $items, string $extension)
{
$parts = explode('.', $extension);
$section = \count($parts) > 1 ? $parts[1] : null;
$config = (object) [
'related_tbl' => ($section === 'category' ? 'categories' : 'content'),
'state_col' => ($section === 'category' ? 'published' : 'state'),
'group_col' => 'tag_id',
'extension' => $extension,
'relation_type' => 'tag_assigments',
];
LibraryContentHelper::countRelations($items, $config);
}
/**
* Prepares the category form
*
* @param Form $form The form to prepare
* @param array|object $data The form data
*
* @return void
*/
public function prepareForm(Form $form, $data)
{
ContentHelper::onPrepareForm($form, $data);
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Field;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\Language\Associations;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Assoc Field class.
* Supports a generic list of options.
* Displays only when Associations are enabled.
*
* @since 4.0.0
*/
class AssocField extends ListField
{
/**
* The form field type.
*
* @var string
* @since 4.0.0
*/
protected $type = 'Assoc';
/**
* Method to attach a Form 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 AssocField::setup()
* @since 4.0.0
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
if (!Associations::isEnabled()) {
return false;
}
return parent::setup($element, $value, $group);
}
}

View File

@ -0,0 +1,189 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Field\Modal;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\ModalSelectField;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
use Joomla\Database\ParameterType;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Supports a modal article picker.
*
* @since 1.6
*/
class ArticleField extends ModalSelectField
{
/**
* The form field type.
*
* @var string
* @since 1.6
*/
protected $type = 'Modal_Article';
/**
* Method to attach a Form 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.
*
* @return boolean True on success.
*
* @see FormField::setup()
* @since 5.0.0
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
// Check if the value consist with id:alias, extract the id only
if ($value && str_contains($value, ':')) {
[$id] = explode(':', $value, 2);
$value = (int) $id;
}
$result = parent::setup($element, $value, $group);
if (!$result) {
return $result;
}
Factory::getApplication()->getLanguage()->load('com_content', JPATH_ADMINISTRATOR);
$languages = LanguageHelper::getContentLanguages([0, 1], false);
$language = (string) $this->element['language'];
// Prepare enabled actions
$this->canDo['propagate'] = ((string) $this->element['propagate'] == 'true') && \count($languages) > 2;
// Prepare Urls
$linkArticles = (new Uri())->setPath(Uri::base(true) . '/index.php');
$linkArticles->setQuery([
'option' => 'com_content',
'view' => 'articles',
'layout' => 'modal',
'tmpl' => 'component',
Session::getFormToken() => 1,
]);
$linkArticle = clone $linkArticles;
$linkArticle->setVar('view', 'article');
$linkCheckin = (new Uri())->setPath(Uri::base(true) . '/index.php');
$linkCheckin->setQuery([
'option' => 'com_content',
'task' => 'articles.checkin',
'format' => 'json',
Session::getFormToken() => 1,
]);
if ($language) {
$linkArticles->setVar('forcedLanguage', $language);
$linkArticle->setVar('forcedLanguage', $language);
$modalTitle = Text::_('COM_CONTENT_SELECT_AN_ARTICLE') . ' &#8212; ' . $this->getTitle();
$this->dataAttributes['data-language'] = $language;
} else {
$modalTitle = Text::_('COM_CONTENT_SELECT_AN_ARTICLE');
}
$urlSelect = $linkArticles;
$urlEdit = clone $linkArticle;
$urlEdit->setVar('task', 'article.edit');
$urlNew = clone $linkArticle;
$urlNew->setVar('task', 'article.add');
$this->urls['select'] = (string) $urlSelect;
$this->urls['new'] = (string) $urlNew;
$this->urls['edit'] = (string) $urlEdit;
$this->urls['checkin'] = (string) $linkCheckin;
// Prepare titles
$this->modalTitles['select'] = $modalTitle;
$this->modalTitles['new'] = Text::_('COM_CONTENT_NEW_ARTICLE');
$this->modalTitles['edit'] = Text::_('COM_CONTENT_EDIT_ARTICLE');
$this->hint = $this->hint ?: Text::_('COM_CONTENT_SELECT_AN_ARTICLE');
return $result;
}
/**
* Method to retrieve the title of selected item.
*
* @return string
*
* @since 5.0.0
*/
protected function getValueTitle()
{
$value = (int) $this->value ?: '';
$title = '';
if ($value) {
try {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName('title'))
->from($db->quoteName('#__content'))
->where($db->quoteName('id') . ' = :value')
->bind(':value', $value, ParameterType::INTEGER);
$db->setQuery($query);
$title = $db->loadResult();
} catch (\Throwable $e) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
}
}
return $title ?: $value;
}
/**
* Method to get the data to be passed to the layout for rendering.
*
* @return array
*
* @since 5.0.0
*/
protected function getLayoutData()
{
$data = parent::getLayoutData();
$data['language'] = (string) $this->element['language'];
return $data;
}
/**
* Get the renderer
*
* @param string $layoutId Id to load
*
* @return FileLayout
*
* @since 5.0.0
*/
protected function getRenderer($layoutId = 'default')
{
$layout = parent::getRenderer($layoutId);
$layout->setComponent('com_content');
$layout->setClient(1);
return $layout;
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Field;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Votelist Field class.
*
* @since 4.0.0
*/
class VotelistField extends ListField
{
/**
* The form field type.
*
* @var string
* @since 4.0.0
*/
protected $type = 'Votelist';
/**
* Method to attach a form 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.
*
* @since 4.0.0
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
// Requires vote plugin enabled
if (!PluginHelper::isEnabled('content', 'vote')) {
return false;
}
return parent::setup($element, $value, $group);
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Field;
use Joomla\CMS\Form\Field\RadioField;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Voteradio Field class.
*
* @since 3.8.0
*/
class VoteradioField extends RadioField
{
/**
* The form field type.
*
* @var string
* @since 3.8.0
*/
protected $type = 'Voteradio';
/**
* Method to attach a form 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.
*
* @since 4.0.0
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
// Requires vote plugin enabled
if (!PluginHelper::isEnabled('content', 'vote')) {
return false;
}
return parent::setup($element, $value, $group);
}
}

View File

@ -0,0 +1,206 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Helper;
use Joomla\CMS\Association\AssociationExtensionHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Table\Table;
use Joomla\Component\Content\Site\Helper\AssociationHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Content associations helper.
*
* @since 3.7.0
*/
class AssociationsHelper extends AssociationExtensionHelper
{
/**
* The extension name
*
* @var array $extension
*
* @since 3.7.0
*/
protected $extension = 'com_content';
/**
* Array of item types
*
* @var array $itemTypes
*
* @since 3.7.0
*/
protected $itemTypes = ['article', 'category'];
/**
* Has the extension association support
*
* @var boolean $associationsSupport
*
* @since 3.7.0
*/
protected $associationsSupport = true;
/**
* Method to get the associations for a given item.
*
* @param integer $id Id of the item
* @param string $view Name of the view
*
* @return array Array of associations for the item
*
* @since 4.0.0
*/
public function getAssociationsForItem($id = 0, $view = null)
{
return AssociationHelper::getAssociations($id, $view);
}
/**
* Get the associated items for an item
*
* @param string $typeName The item type
* @param int $id The id of item for which we need the associated items
*
* @return array
*
* @since 3.7.0
*/
public function getAssociations($typeName, $id)
{
$type = $this->getType($typeName);
$context = $this->extension . '.item';
$catidField = 'catid';
if ($typeName === 'category') {
$context = 'com_categories.item';
$catidField = '';
}
// Get the associations.
$associations = Associations::getAssociations(
$this->extension,
$type['tables']['a'],
$context,
$id,
'id',
'alias',
$catidField
);
return $associations;
}
/**
* Get item information
*
* @param string $typeName The item type
* @param int $id The id of item for which we need the associated items
*
* @return Table|null
*
* @since 3.7.0
*/
public function getItem($typeName, $id)
{
if (empty($id)) {
return null;
}
$table = null;
switch ($typeName) {
case 'article':
$table = Table::getInstance('Content');
break;
case 'category':
$table = Table::getInstance('Category');
break;
}
if (\is_null($table)) {
return null;
}
$table->load($id);
return $table;
}
/**
* Get information about the type
*
* @param string $typeName The item type
*
* @return array Array of item types
*
* @since 3.7.0
*/
public function getType($typeName = '')
{
$fields = $this->getFieldsTemplate();
$tables = [];
$joins = [];
$support = $this->getSupportTemplate();
$title = '';
if (\in_array($typeName, $this->itemTypes)) {
switch ($typeName) {
case 'article':
$support['state'] = true;
$support['acl'] = true;
$support['checkout'] = true;
$support['category'] = true;
$support['save2copy'] = true;
$tables = [
'a' => '#__content',
];
$title = 'article';
break;
case 'category':
$fields['created_user_id'] = 'a.created_user_id';
$fields['ordering'] = 'a.lft';
$fields['level'] = 'a.level';
$fields['catid'] = '';
$fields['state'] = 'a.published';
$support['state'] = true;
$support['acl'] = true;
$support['checkout'] = true;
$support['level'] = true;
$tables = [
'a' => '#__categories',
];
$title = 'category';
break;
}
}
return [
'fields' => $fields,
'support' => $support,
'tables' => $tables,
'joins' => $joins,
'title' => $title,
];
}
}

View File

@ -0,0 +1,191 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Helper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Table\Category;
use Joomla\CMS\Workflow\WorkflowServiceInterface;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Content component helper.
*
* @since 1.6
*/
class ContentHelper extends \Joomla\CMS\Helper\ContentHelper
{
/**
* Check if state can be deleted
*
* @param int $id Id of state to delete
*
* @return boolean
*
* @since 4.0.0
*/
public static function canDeleteState(int $id): bool
{
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->select('id')
->from($db->quoteName('#__content'))
->where($db->quoteName('state') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($query);
$states = $db->loadResult();
return empty($states);
}
/**
* Method to filter transitions by given id of state
*
* @param array $transitions Array of transitions
* @param int $pk Id of state
* @param int $workflowId Id of the workflow
*
* @return array
*
* @since 4.0.0
*/
public static function filterTransitions(array $transitions, int $pk, int $workflowId = 0): array
{
return array_values(
array_filter(
$transitions,
function ($var) use ($pk, $workflowId) {
return \in_array($var['from_stage_id'], [-1, $pk]) && $workflowId == $var['workflow_id'];
}
)
);
}
/**
* Prepares a form
*
* @param Form $form The form to change
* @param array|object $data The form data
*
* @return void
*/
public static function onPrepareForm(Form $form, $data)
{
if ($form->getName() != 'com_categories.categorycom_content') {
return;
}
$db = Factory::getDbo();
$data = (array) $data;
// Make workflows translatable
Factory::getLanguage()->load('com_workflow', JPATH_ADMINISTRATOR);
$form->setFieldAttribute('workflow_id', 'default', 'inherit');
$component = Factory::getApplication()->bootComponent('com_content');
if (
!$component instanceof WorkflowServiceInterface
|| !$component->isWorkflowActive('com_content.article')
) {
$form->removeField('workflow_id', 'params');
return;
}
$query = $db->getQuery(true);
$query->select($db->quoteName('title'))
->from($db->quoteName('#__workflows'))
->where(
[
$db->quoteName('default') . ' = 1',
$db->quoteName('published') . ' = 1',
$db->quoteName('extension') . ' = ' . $db->quote('com_content.article'),
]
);
$defaulttitle = $db->setQuery($query)->loadResult();
$option = Text::_('COM_WORKFLOW_INHERIT_WORKFLOW_NEW');
if (!empty($data['id'])) {
$category = new Category($db);
$categories = $category->getPath((int) $data['id']);
// Remove the current category, because we search for inheritance from parent.
array_pop($categories);
$option = Text::sprintf('COM_WORKFLOW_INHERIT_WORKFLOW', Text::_($defaulttitle));
if (!empty($categories)) {
$categories = array_reverse($categories);
$query = $db->getQuery(true);
$query->select($db->quoteName('title'))
->from($db->quoteName('#__workflows'))
->where(
[
$db->quoteName('id') . ' = :workflowId',
$db->quoteName('published') . ' = 1',
$db->quoteName('extension') . ' = ' . $db->quote('com_content.article'),
]
)
->bind(':workflowId', $workflow_id, ParameterType::INTEGER);
$db->setQuery($query);
foreach ($categories as $cat) {
$cat->params = new Registry($cat->params);
$workflow_id = $cat->params->get('workflow_id');
if ($workflow_id === 'inherit') {
continue;
}
if ($workflow_id === 'use_default') {
break;
}
if ($workflow_id = (int) $workflow_id) {
$title = $db->loadResult();
if (!\is_null($title)) {
$option = Text::sprintf('COM_WORKFLOW_INHERIT_WORKFLOW', Text::_($title));
break;
}
}
}
}
}
$field = $form->getField('workflow_id', 'params');
$field->addOption($option, ['value' => 'inherit']);
$field->addOption(Text::sprintf('COM_WORKFLOW_USE_DEFAULT_WORKFLOW', Text::_($defaulttitle)), ['value' => 'use_default']);
$field->addOption('- ' . Text::_('COM_CONTENT_WORKFLOWS') . ' -', ['disabled' => 'true']);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,638 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Database\ParameterType;
use Joomla\Database\QueryInterface;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of article records.
*
* @since 1.6
*/
class ArticlesModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @since 1.6
* @see \Joomla\CMS\MVC\Controller\BaseController
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'title', 'a.title',
'alias', 'a.alias',
'checked_out', 'a.checked_out',
'checked_out_time', 'a.checked_out_time',
'catid', 'a.catid', 'category_title',
'state', 'a.state',
'access', 'a.access', 'access_level',
'created', 'a.created',
'modified', 'a.modified',
'created_by', 'a.created_by',
'created_by_alias', 'a.created_by_alias',
'ordering', 'a.ordering',
'featured', 'a.featured',
'featured_up', 'fp.featured_up',
'featured_down', 'fp.featured_down',
'language', 'a.language',
'hits', 'a.hits',
'publish_up', 'a.publish_up',
'publish_down', 'a.publish_down',
'published', 'a.published',
'author_id',
'category_id',
'level',
'tag',
'rating_count', 'rating',
'stage', 'wa.stage_id',
'ws.title',
];
if (Associations::isEnabled()) {
$config['filter_fields'][] = 'association';
}
}
parent::__construct($config);
}
/**
* Get the filter form
*
* @param array $data data
* @param boolean $loadData load current data
*
* @return \Joomla\CMS\Form\Form|null The Form object or null if the form can't be found
*
* @since 3.2
*/
public function getFilterForm($data = [], $loadData = true)
{
$form = parent::getFilterForm($data, $loadData);
$params = ComponentHelper::getParams('com_content');
if (!$params->get('workflow_enabled')) {
$form->removeField('stage', 'filter');
} else {
$ordering = $form->getField('fullordering', 'list');
$ordering->addOption('JSTAGE_ASC', ['value' => 'ws.title ASC']);
$ordering->addOption('JSTAGE_DESC', ['value' => 'ws.title DESC']);
}
return $form;
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 1.6
*/
protected function populateState($ordering = 'a.id', $direction = 'desc')
{
$app = Factory::getApplication();
$input = $app->getInput();
$forcedLanguage = $input->get('forcedLanguage', '', 'cmd');
// Adjust the context to support modal layouts.
if ($layout = $input->get('layout')) {
$this->context .= '.' . $layout;
}
// Adjust the context to support forced languages.
if ($forcedLanguage) {
$this->context .= '.' . $forcedLanguage;
}
// Required content filters for the administrator menu
$this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id');
$this->getUserStateFromRequest($this->context . '.filter.level', 'filter_level');
$this->getUserStateFromRequest($this->context . '.filter.author_id', 'filter_author_id');
$this->getUserStateFromRequest($this->context . '.filter.tag', 'filter_tag', '');
$this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access');
$this->getUserStateFromRequest($this->context . '.filter.language', 'filter_language', '');
// List state information.
parent::populateState($ordering, $direction);
// Force a language
if (!empty($forcedLanguage)) {
$this->setState('filter.language', $forcedLanguage);
$this->setState('filter.forcedLanguage', $forcedLanguage);
}
}
/**
* Method to get a store id based on model configuration state.
*
* This is necessary because the model is used by the component and
* different modules that might need different sets of data or different
* ordering requirements.
*
* @param string $id A prefix for the store id.
*
* @return string A store id.
*
* @since 1.6
*/
protected function getStoreId($id = '')
{
// Compile the store id.
$id .= ':' . $this->getState('filter.search');
$id .= ':' . serialize($this->getState('filter.access'));
$id .= ':' . $this->getState('filter.published');
$id .= ':' . serialize($this->getState('filter.category_id'));
$id .= ':' . serialize($this->getState('filter.author_id'));
$id .= ':' . $this->getState('filter.language');
$id .= ':' . serialize($this->getState('filter.tag'));
return parent::getStoreId($id);
}
/**
* Build an SQL query to load the list data.
*
* @return QueryInterface
*
* @since 1.6
*/
protected function getListQuery()
{
// Create a new query object.
$db = $this->getDatabase();
$query = $db->getQuery(true);
$user = $this->getCurrentUser();
$params = ComponentHelper::getParams('com_content');
// Select the required fields from the table.
$query->select(
$this->getState(
'list.select',
[
$db->quoteName('a.id'),
$db->quoteName('a.asset_id'),
$db->quoteName('a.title'),
$db->quoteName('a.alias'),
$db->quoteName('a.checked_out'),
$db->quoteName('a.checked_out_time'),
$db->quoteName('a.catid'),
$db->quoteName('a.state'),
$db->quoteName('a.access'),
$db->quoteName('a.created'),
$db->quoteName('a.created_by'),
$db->quoteName('a.created_by_alias'),
$db->quoteName('a.modified'),
$db->quoteName('a.ordering'),
$db->quoteName('a.featured'),
$db->quoteName('a.language'),
$db->quoteName('a.hits'),
$db->quoteName('a.publish_up'),
$db->quoteName('a.publish_down'),
$db->quoteName('a.introtext'),
$db->quoteName('a.fulltext'),
$db->quoteName('a.note'),
$db->quoteName('a.images'),
$db->quoteName('a.metakey'),
$db->quoteName('a.metadesc'),
$db->quoteName('a.metadata'),
$db->quoteName('a.version'),
]
)
)
->select(
[
$db->quoteName('fp.featured_up'),
$db->quoteName('fp.featured_down'),
$db->quoteName('l.title', 'language_title'),
$db->quoteName('l.image', 'language_image'),
$db->quoteName('uc.name', 'editor'),
$db->quoteName('ag.title', 'access_level'),
$db->quoteName('c.title', 'category_title'),
$db->quoteName('c.created_user_id', 'category_uid'),
$db->quoteName('c.level', 'category_level'),
$db->quoteName('c.published', 'category_published'),
$db->quoteName('parent.title', 'parent_category_title'),
$db->quoteName('parent.id', 'parent_category_id'),
$db->quoteName('parent.created_user_id', 'parent_category_uid'),
$db->quoteName('parent.level', 'parent_category_level'),
$db->quoteName('ua.name', 'author_name'),
$db->quoteName('wa.stage_id', 'stage_id'),
$db->quoteName('ws.title', 'stage_title'),
$db->quoteName('ws.workflow_id', 'workflow_id'),
$db->quoteName('w.title', 'workflow_title'),
]
)
->from($db->quoteName('#__content', 'a'))
->where($db->quoteName('wa.extension') . ' = ' . $db->quote('com_content.article'))
->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language'))
->join('LEFT', $db->quoteName('#__content_frontpage', 'fp'), $db->quoteName('fp.content_id') . ' = ' . $db->quoteName('a.id'))
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out'))
->join('LEFT', $db->quoteName('#__viewlevels', 'ag'), $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access'))
->join('LEFT', $db->quoteName('#__categories', 'c'), $db->quoteName('c.id') . ' = ' . $db->quoteName('a.catid'))
->join('LEFT', $db->quoteName('#__categories', 'parent'), $db->quoteName('parent.id') . ' = ' . $db->quoteName('c.parent_id'))
->join('LEFT', $db->quoteName('#__users', 'ua'), $db->quoteName('ua.id') . ' = ' . $db->quoteName('a.created_by'))
->join('INNER', $db->quoteName('#__workflow_associations', 'wa'), $db->quoteName('wa.item_id') . ' = ' . $db->quoteName('a.id'))
->join('INNER', $db->quoteName('#__workflow_stages', 'ws'), $db->quoteName('ws.id') . ' = ' . $db->quoteName('wa.stage_id'))
->join('INNER', $db->quoteName('#__workflows', 'w'), $db->quoteName('w.id') . ' = ' . $db->quoteName('ws.workflow_id'));
if (PluginHelper::isEnabled('content', 'vote')) {
$query->select(
[
'COALESCE(NULLIF(ROUND(' . $db->quoteName('v.rating_sum') . ' / ' . $db->quoteName('v.rating_count') . ', 0), 0), 0)'
. ' AS ' . $db->quoteName('rating'),
'COALESCE(NULLIF(' . $db->quoteName('v.rating_count') . ', 0), 0) AS ' . $db->quoteName('rating_count'),
]
)
->join('LEFT', $db->quoteName('#__content_rating', 'v'), $db->quoteName('a.id') . ' = ' . $db->quoteName('v.content_id'));
}
// Join over the associations.
if (Associations::isEnabled()) {
$subQuery = $db->getQuery(true)
->select('COUNT(' . $db->quoteName('asso1.id') . ') > 1')
->from($db->quoteName('#__associations', 'asso1'))
->join('INNER', $db->quoteName('#__associations', 'asso2'), $db->quoteName('asso1.key') . ' = ' . $db->quoteName('asso2.key'))
->where(
[
$db->quoteName('asso1.id') . ' = ' . $db->quoteName('a.id'),
$db->quoteName('asso1.context') . ' = ' . $db->quote('com_content.item'),
]
);
$query->select('(' . $subQuery . ') AS ' . $db->quoteName('association'));
}
// Filter by access level.
$access = $this->getState('filter.access');
if (is_numeric($access)) {
$access = (int) $access;
$query->where($db->quoteName('a.access') . ' = :access')
->bind(':access', $access, ParameterType::INTEGER);
} elseif (\is_array($access)) {
$access = ArrayHelper::toInteger($access);
$query->whereIn($db->quoteName('a.access'), $access);
}
// Filter by featured.
$featured = (string) $this->getState('filter.featured');
if (\in_array($featured, ['0','1'])) {
$featured = (int) $featured;
$query->where($db->quoteName('a.featured') . ' = :featured')
->bind(':featured', $featured, ParameterType::INTEGER);
}
// Filter by access level on categories.
if (!$user->authorise('core.admin')) {
$groups = $user->getAuthorisedViewLevels();
$query->whereIn($db->quoteName('a.access'), $groups);
$query->whereIn($db->quoteName('c.access'), $groups);
}
// Filter by published state
$workflowStage = (string) $this->getState('filter.stage');
if ($params->get('workflow_enabled') && is_numeric($workflowStage)) {
$workflowStage = (int) $workflowStage;
$query->where($db->quoteName('wa.stage_id') . ' = :stage')
->bind(':stage', $workflowStage, ParameterType::INTEGER);
}
$published = (string) $this->getState('filter.published');
if ($published !== '*') {
if (is_numeric($published)) {
$state = (int) $published;
$query->where($db->quoteName('a.state') . ' = :state')
->bind(':state', $state, ParameterType::INTEGER);
} elseif (!is_numeric($workflowStage)) {
$query->whereIn(
$db->quoteName('a.state'),
[
ContentComponent::CONDITION_PUBLISHED,
ContentComponent::CONDITION_UNPUBLISHED,
]
);
}
}
// Filter by categories and by level
$categoryId = $this->getState('filter.category_id', []);
$level = (int) $this->getState('filter.level');
if (!\is_array($categoryId)) {
$categoryId = $categoryId ? [$categoryId] : [];
}
// Case: Using both categories filter and by level filter
if (\count($categoryId)) {
$categoryId = ArrayHelper::toInteger($categoryId);
$categoryTable = Table::getInstance('Category', '\\Joomla\\CMS\\Table\\');
$subCatItemsWhere = [];
foreach ($categoryId as $key => $filter_catid) {
$categoryTable->load($filter_catid);
// Because values to $query->bind() are passed by reference, using $query->bindArray() here instead to prevent overwriting.
$valuesToBind = [$categoryTable->lft, $categoryTable->rgt];
if ($level) {
$valuesToBind[] = $level + $categoryTable->level - 1;
}
// Bind values and get parameter names.
$bounded = $query->bindArray($valuesToBind);
$categoryWhere = $db->quoteName('c.lft') . ' >= ' . $bounded[0] . ' AND ' . $db->quoteName('c.rgt') . ' <= ' . $bounded[1];
if ($level) {
$categoryWhere .= ' AND ' . $db->quoteName('c.level') . ' <= ' . $bounded[2];
}
$subCatItemsWhere[] = '(' . $categoryWhere . ')';
}
$query->where('(' . implode(' OR ', $subCatItemsWhere) . ')');
} elseif ($level = (int) $level) {
// Case: Using only the by level filter
$query->where($db->quoteName('c.level') . ' <= :level')
->bind(':level', $level, ParameterType::INTEGER);
}
// Filter by author
$authorId = $this->getState('filter.author_id');
if (is_numeric($authorId)) {
$authorId = (int) $authorId;
$type = $this->getState('filter.author_id.include', true) ? ' = ' : ' <> ';
$query->where($db->quoteName('a.created_by') . $type . ':authorId')
->bind(':authorId', $authorId, ParameterType::INTEGER);
} elseif (\is_array($authorId)) {
// Check to see if by_me is in the array
if (\in_array('by_me', $authorId)) {
// Replace by_me with the current user id in the array
$authorId['by_me'] = $user->id;
}
$authorId = ArrayHelper::toInteger($authorId);
$query->whereIn($db->quoteName('a.created_by'), $authorId);
}
// Filter by search in title.
$search = $this->getState('filter.search');
if (!empty($search)) {
if (stripos($search, 'id:') === 0) {
$search = (int) substr($search, 3);
$query->where($db->quoteName('a.id') . ' = :search')
->bind(':search', $search, ParameterType::INTEGER);
} elseif (stripos($search, 'author:') === 0) {
$search = '%' . substr($search, 7) . '%';
$query->where('(' . $db->quoteName('ua.name') . ' LIKE :search1 OR ' . $db->quoteName('ua.username') . ' LIKE :search2)')
->bind([':search1', ':search2'], $search);
} elseif (stripos($search, 'content:') === 0) {
$search = '%' . substr($search, 8) . '%';
$query->where('(' . $db->quoteName('a.introtext') . ' LIKE :search1 OR ' . $db->quoteName('a.fulltext') . ' LIKE :search2)')
->bind([':search1', ':search2'], $search);
} elseif (stripos($search, 'checkedout:') === 0) {
$search = '%' . substr($search, 11) . '%';
$query->where('(' . $db->quoteName('uc.name') . ' LIKE :search1 OR ' . $db->quoteName('uc.username') . ' LIKE :search2)'
. ' AND ' . $db->quoteName('a.checked_out') . ' IS NOT NULL')
->bind([':search1', ':search2'], $search);
} else {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where(
'(' . $db->quoteName('a.title') . ' LIKE :search1 OR ' . $db->quoteName('a.alias') . ' LIKE :search2'
. ' OR ' . $db->quoteName('a.note') . ' LIKE :search3)'
)
->bind([':search1', ':search2', ':search3'], $search);
}
}
// Filter on the language.
if ($language = $this->getState('filter.language')) {
$query->where($db->quoteName('a.language') . ' = :language')
->bind(':language', $language);
}
// Filter by a single or group of tags.
$tag = $this->getState('filter.tag');
// Run simplified query when filtering by one tag.
if (\is_array($tag) && \count($tag) === 1) {
$tag = $tag[0];
}
if ($tag && \is_array($tag)) {
$tag = ArrayHelper::toInteger($tag);
$subQuery = $db->getQuery(true)
->select('DISTINCT ' . $db->quoteName('content_item_id'))
->from($db->quoteName('#__contentitem_tag_map'))
->where(
[
$db->quoteName('tag_id') . ' IN (' . implode(',', $query->bindArray($tag)) . ')',
$db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'),
]
);
$query->join(
'INNER',
'(' . $subQuery . ') AS ' . $db->quoteName('tagmap'),
$db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id')
);
} elseif ($tag = (int) $tag) {
$query->join(
'INNER',
$db->quoteName('#__contentitem_tag_map', 'tagmap'),
$db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id')
)
->where(
[
$db->quoteName('tagmap.tag_id') . ' = :tag',
$db->quoteName('tagmap.type_alias') . ' = ' . $db->quote('com_content.article'),
]
)
->bind(':tag', $tag, ParameterType::INTEGER);
}
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering', 'a.id');
$orderDirn = $this->state->get('list.direction', 'DESC');
if ($orderCol === 'a.ordering' || $orderCol === 'category_title') {
$ordering = [
$db->quoteName('c.title') . ' ' . $db->escape($orderDirn),
$db->quoteName('a.ordering') . ' ' . $db->escape($orderDirn),
];
} else {
$ordering = $db->escape($orderCol) . ' ' . $db->escape($orderDirn);
}
$query->order($ordering);
return $query;
}
/**
* Method to get all transitions at once for all articles
*
* @return array|boolean
*
* @since 4.0.0
*/
public function getTransitions()
{
// Get a storage key.
$store = $this->getStoreId('getTransitions');
// Try to load the data from internal storage.
if (isset($this->cache[$store])) {
return $this->cache[$store];
}
$db = $this->getDatabase();
$user = $this->getCurrentUser();
$items = $this->getItems();
if ($items === false) {
return false;
}
$stage_ids = ArrayHelper::getColumn($items, 'stage_id');
$stage_ids = ArrayHelper::toInteger($stage_ids);
$stage_ids = array_values(array_unique(array_filter($stage_ids)));
$workflow_ids = ArrayHelper::getColumn($items, 'workflow_id');
$workflow_ids = ArrayHelper::toInteger($workflow_ids);
$workflow_ids = array_values(array_unique(array_filter($workflow_ids)));
$this->cache[$store] = [];
try {
if (\count($stage_ids) || \count($workflow_ids)) {
Factory::getLanguage()->load('com_workflow', JPATH_ADMINISTRATOR);
$query = $db->getQuery(true);
$query ->select(
[
$db->quoteName('t.id', 'value'),
$db->quoteName('t.title', 'text'),
$db->quoteName('t.from_stage_id'),
$db->quoteName('t.to_stage_id'),
$db->quoteName('s.id', 'stage_id'),
$db->quoteName('s.title', 'stage_title'),
$db->quoteName('t.workflow_id'),
]
)
->from($db->quoteName('#__workflow_transitions', 't'))
->innerJoin(
$db->quoteName('#__workflow_stages', 's'),
$db->quoteName('t.to_stage_id') . ' = ' . $db->quoteName('s.id')
)
->where(
[
$db->quoteName('t.published') . ' = 1',
$db->quoteName('s.published') . ' = 1',
]
)
->order($db->quoteName('t.ordering'));
$where = [];
if (\count($stage_ids)) {
$where[] = $db->quoteName('t.from_stage_id') . ' IN (' . implode(',', $query->bindArray($stage_ids)) . ')';
}
if (\count($workflow_ids)) {
$where[] = '(' . $db->quoteName('t.from_stage_id') . ' = -1 AND ' . $db->quoteName('t.workflow_id') . ' IN (' . implode(',', $query->bindArray($workflow_ids)) . '))';
}
$query->where('((' . implode(') OR (', $where) . '))');
$transitions = $db->setQuery($query)->loadAssocList();
foreach ($transitions as $key => $transition) {
if (!$user->authorise('core.execute.transition', 'com_content.transition.' . (int) $transition['value'])) {
unset($transitions[$key]);
}
$transitions[$key]['text'] = Text::_($transition['text']);
}
$this->cache[$store] = $transitions;
}
} catch (\RuntimeException $e) {
$this->setError($e->getMessage());
return false;
}
return $this->cache[$store];
}
/**
* Method to get a list of articles.
* Overridden to add item type alias.
*
* @return mixed An array of data items on success, false on failure.
*
* @since 4.0.0
*/
public function getItems()
{
$items = parent::getItems();
foreach ($items as $item) {
$item->typeAlias = 'com_content.article';
if (isset($item->metadata)) {
$registry = new Registry($item->metadata);
$item->metadata = $registry->toArray();
}
}
return $items;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Model;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Feature model.
*
* @since 1.6
*/
class FeatureModel extends ArticleModel
{
/**
* Returns a Table object, always creating it.
*
* @param string $type The table type to instantiate
* @param string $prefix A prefix for the table class name. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\Table\Table A database object
*
* @since 1.6
*/
public function getTable($type = 'Featured', $prefix = 'Administrator', $config = [])
{
return parent::getTable($type, $prefix, $config);
}
/**
* A protected method to get a set of ordering conditions.
*
* @param object $table A record object.
*
* @return array An array of conditions to add to ordering queries.
*
* @since 1.6
*/
protected function getReorderConditions($table)
{
return [];
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Model;
use Joomla\Database\QueryInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of featured article records.
*
* @since 1.6
*/
class FeaturedModel extends ArticlesModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see \Joomla\CMS\MVC\Controller\BaseController
* @since 1.6
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'title', 'a.title',
'alias', 'a.alias',
'checked_out', 'a.checked_out',
'checked_out_time', 'a.checked_out_time',
'catid', 'a.catid', 'category_title',
'state', 'a.state',
'access', 'a.access', 'access_level',
'created', 'a.created',
'created_by', 'a.created_by',
'created_by_alias', 'a.created_by_alias',
'ordering', 'a.ordering',
'featured_up', 'fp.featured_up',
'featured_down', 'fp.featured_down',
'language', 'a.language',
'hits', 'a.hits',
'publish_up', 'a.publish_up',
'publish_down', 'a.publish_down',
'fp.ordering',
'published', 'a.published',
'author_id',
'category_id',
'level',
'tag',
'rating_count', 'rating',
'ws.title',
];
}
parent::__construct($config);
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 3.5
*/
protected function populateState($ordering = 'a.title', $direction = 'asc')
{
parent::populateState($ordering, $direction);
// Filter by featured articles.
$this->setState('filter.featured', 1);
}
/**
* Build an SQL query to load the list data.
*
* @return QueryInterface
*
* @since 4.0.0
*/
protected function getListQuery()
{
$query = parent::getListQuery();
$query->select($this->getDatabase()->quoteName('fp.ordering'));
return $query;
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\Database\ParameterType;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Content HTML helper
*
* @since 3.0
*/
class AdministratorService
{
/**
* Render the list of associated items
*
* @param integer $articleid The article item id
*
* @return string The language HTML
*
* @throws \Exception
*/
public function association($articleid)
{
// Defaults
$html = '';
// Get the associations
if ($associations = Associations::getAssociations('com_content', '#__content', 'com_content.item', $articleid)) {
foreach ($associations as $tag => $associated) {
$associations[$tag] = (int) $associated->id;
}
// Get the associated menu items
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select(
[
'c.*',
$db->quoteName('l.sef', 'lang_sef'),
$db->quoteName('l.lang_code'),
$db->quoteName('cat.title', 'category_title'),
$db->quoteName('l.image'),
$db->quoteName('l.title', 'language_title'),
]
)
->from($db->quoteName('#__content', 'c'))
->join('LEFT', $db->quoteName('#__categories', 'cat'), $db->quoteName('cat.id') . ' = ' . $db->quoteName('c.catid'))
->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('c.language') . ' = ' . $db->quoteName('l.lang_code'))
->whereIn($db->quoteName('c.id'), array_values($associations))
->where($db->quoteName('c.id') . ' != :articleId')
->bind(':articleId', $articleid, ParameterType::INTEGER);
$db->setQuery($query);
try {
$items = $db->loadObjectList('id');
} catch (\RuntimeException $e) {
throw new \Exception($e->getMessage(), 500, $e);
}
if ($items) {
$languages = LanguageHelper::getContentLanguages([0, 1]);
$content_languages = array_column($languages, 'lang_code');
foreach ($items as &$item) {
if (\in_array($item->lang_code, $content_languages)) {
$text = $item->lang_code;
$url = Route::_('index.php?option=com_content&task=article.edit&id=' . (int) $item->id);
$tooltip = '<strong>' . htmlspecialchars($item->language_title, ENT_QUOTES, 'UTF-8') . '</strong><br>'
. htmlspecialchars($item->title, ENT_QUOTES, 'UTF-8') . '<br>' . Text::sprintf('JCATEGORY_SPRINTF', $item->category_title);
$classes = 'badge bg-secondary';
$item->link = '<a href="' . $url . '" class="' . $classes . '">' . $text . '</a>'
. '<div role="tooltip" id="tip-' . (int) $articleid . '-' . (int) $item->id . '">' . $tooltip . '</div>';
} else {
// Display warning if Content Language is trashed or deleted
Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_ASSOCIATIONS_CONTENTLANGUAGE_WARNING', $item->lang_code), 'warning');
}
}
}
$html = LayoutHelper::render('joomla.content.associations', $items);
}
return $html;
}
}

View File

@ -0,0 +1,155 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Workflow\Workflow;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Content Component HTML Helper
*
* @since 4.0.0
*/
class Icon
{
/**
* Method to generate a link to the create item page for the given category
*
* @param object $category The category information
* @param Registry $params The item parameters
* @param array $attribs Optional attributes for the link
* @param boolean $legacy True to use legacy images, false to use icomoon based graphic
*
* @return string The HTML markup for the create item link
*
* @since 4.0.0
*/
public function create($category, $params, $attribs = [], $legacy = false)
{
$uri = Uri::getInstance();
$url = 'index.php?option=com_content&task=article.add&return=' . base64_encode($uri) . '&a_id=0&catid=' . $category->id;
$text = '';
if ($params->get('show_icons')) {
$text .= '<span class="icon-plus icon-fw" aria-hidden="true"></span>';
}
$text .= Text::_('COM_CONTENT_NEW_ARTICLE');
// Add the button classes to the attribs array
if (isset($attribs['class'])) {
$attribs['class'] .= ' btn btn-primary';
} else {
$attribs['class'] = 'btn btn-primary';
}
$button = HTMLHelper::_('link', Route::_($url), $text, $attribs);
return $button;
}
/**
* Display an edit icon for the article.
*
* This icon will not display in a popup window, nor if the article is trashed.
* Edit access checks must be performed in the calling code.
*
* @param object $article The article information
* @param Registry $params The item parameters
* @param array $attribs Optional attributes for the link
* @param boolean $legacy True to use legacy images, false to use icomoon based graphic
*
* @return string The HTML for the article edit icon.
*
* @since 4.0.0
*/
public function edit($article, $params, $attribs = [], $legacy = false)
{
$user = Factory::getUser();
$uri = Uri::getInstance();
// Ignore if in a popup window.
if ($params && $params->get('popup')) {
return '';
}
// Ignore if the state is negative (trashed).
if (!\in_array($article->state, [Workflow::CONDITION_UNPUBLISHED, Workflow::CONDITION_PUBLISHED])) {
return '';
}
// Show checked_out icon if the article is checked out by a different user
if (
property_exists($article, 'checked_out')
&& property_exists($article, 'checked_out_time')
&& !\is_null($article->checked_out)
&& $article->checked_out != $user->id
) {
$checkoutUser = Factory::getUser($article->checked_out);
$date = HTMLHelper::_('date', $article->checked_out_time);
$tooltip = Text::sprintf('COM_CONTENT_CHECKED_OUT_BY', $checkoutUser->name)
. ' <br> ' . $date;
$text = LayoutHelper::render('joomla.content.icons.edit_lock', ['article' => $article, 'tooltip' => $tooltip, 'legacy' => $legacy]);
$attribs['aria-describedby'] = 'editarticle-' . (int) $article->id;
$output = HTMLHelper::_('link', '#', $text, $attribs);
return $output;
}
$contentUrl = RouteHelper::getArticleRoute($article->slug, $article->catid, $article->language);
$url = $contentUrl . '&task=article.edit&a_id=' . $article->id . '&return=' . base64_encode($uri);
if ($article->state == Workflow::CONDITION_UNPUBLISHED) {
$tooltip = Text::_('COM_CONTENT_EDIT_UNPUBLISHED_ARTICLE');
} else {
$tooltip = Text::_('COM_CONTENT_EDIT_PUBLISHED_ARTICLE');
}
$text = LayoutHelper::render('joomla.content.icons.edit', ['article' => $article, 'tooltip' => $tooltip, 'legacy' => $legacy]);
$attribs['aria-describedby'] = 'editarticle-' . (int) $article->id;
$output = HTMLHelper::_('link', Route::_($url), $text, $attribs);
return $output;
}
/**
* Method to generate a link to print an article
*
* @param Registry $params The item parameters
* @param boolean $legacy True to use legacy images, false to use icomoon based graphic
*
* @return string The HTML markup for the popup link
*
* @since 4.0.0
*/
public function print_screen($params, $legacy = false)
{
$text = LayoutHelper::render('joomla.content.icons.print_screen', ['params' => $params, 'legacy' => $legacy]);
return '<button type="button" onclick="window.print();return false;">' . $text . '</button>';
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Table;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Article table
*
* @since 1.5
*/
class ArticleTable extends \Joomla\CMS\Table\Content
{
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\Table;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Featured Table class.
*
* @since 1.6
*/
class FeaturedTable extends Table
{
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 1.6
*/
public function __construct(DatabaseDriver $db, ?DispatcherInterface $dispatcher = null)
{
parent::__construct('#__content_frontpage', 'content_id', $db, $dispatcher);
}
}

View File

@ -0,0 +1,285 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\View\Article;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\Component\Content\Site\Helper\RouteHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View to edit an article.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* The \JForm object
*
* @var \Joomla\CMS\Form\Form
*/
protected $form;
/**
* The active item
*
* @var object
*/
protected $item;
/**
* The model state
*
* @var object
*/
protected $state;
/**
* The actions the user is authorised to perform
*
* @var \Joomla\Registry\Registry
*/
protected $canDo;
/**
* Pagebreak TOC alias
*
* @var string
*/
protected $eName;
/**
* Array of fieldsets not to display
*
* @var string[]
*
* @since 5.2.0
*/
public $ignore_fieldsets = [];
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @since 1.6
*
* @throws \Exception
*/
public function display($tpl = null)
{
if ($this->getLayout() == 'pagebreak') {
parent::display($tpl);
return;
}
$this->form = $this->get('Form');
$this->item = $this->get('Item');
$this->state = $this->get('State');
$this->canDo = ContentHelper::getActions('com_content', 'article', $this->item->id);
if ($this->getLayout() === 'modalreturn') {
parent::display($tpl);
return;
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// If we are forcing a language in modal (used for associations).
if ($this->getLayout() === 'modal' && $forcedLanguage = Factory::getApplication()->getInput()->get('forcedLanguage', '', 'cmd')) {
// Set the language field to the forcedLanguage and disable changing it.
$this->form->setValue('language', null, $forcedLanguage);
$this->form->setFieldAttribute('language', 'readonly', 'true');
// Only allow to select categories with All language or with the forced language.
$this->form->setFieldAttribute('catid', 'language', '*,' . $forcedLanguage);
// Only allow to select tags with All language or with the forced language.
$this->form->setFieldAttribute('tags', 'language', '*,' . $forcedLanguage);
}
if ($this->getLayout() !== 'modal') {
$this->addToolbar();
} else {
$this->addModalToolbar();
}
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*
* @throws \Exception
*/
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
$user = $this->getCurrentUser();
$userId = $user->id;
$isNew = ($this->item->id == 0);
$checkedOut = !(\is_null($this->item->checked_out) || $this->item->checked_out == $userId);
$toolbar = $this->getDocument()->getToolbar();
// Built the actions for new and existing records.
$canDo = $this->canDo;
ToolbarHelper::title(
Text::_('COM_CONTENT_PAGE_' . ($checkedOut ? 'VIEW_ARTICLE' : ($isNew ? 'ADD_ARTICLE' : 'EDIT_ARTICLE'))),
'pencil-alt article-add'
);
// For new records, check the create permission.
if ($isNew && (\count($user->getAuthorisedCategories('com_content', 'core.create')) > 0)) {
$toolbar->apply('article.apply');
$saveGroup = $toolbar->dropdownButton('save-group');
$saveGroup->configure(
function (Toolbar $childBar) use ($user) {
$childBar->save('article.save');
if ($user->authorise('core.create', 'com_menus.menu')) {
$childBar->save('article.save2menu', 'JTOOLBAR_SAVE_TO_MENU');
}
$childBar->save2new('article.save2new');
}
);
$toolbar->cancel('article.cancel', 'JTOOLBAR_CANCEL');
} else {
// Since it's an existing record, check the edit permission, or fall back to edit own if the owner.
$itemEditable = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId);
if (!$checkedOut && $itemEditable) {
$toolbar->apply('article.apply');
}
$saveGroup = $toolbar->dropdownButton('save-group');
$saveGroup->configure(
function (Toolbar $childBar) use ($checkedOut, $itemEditable, $canDo, $user) {
// Can't save the record if it's checked out and editable
if (!$checkedOut && $itemEditable) {
$childBar->save('article.save');
// We can save this record, but check the create permission to see if we can return to make a new one.
if ($canDo->get('core.create')) {
$childBar->save2new('article.save2new');
}
}
// If checked out, we can still save2menu
if ($user->authorise('core.create', 'com_menus.menu')) {
$childBar->save('article.save2menu', 'JTOOLBAR_SAVE_TO_MENU');
}
// If checked out, we can still save
if ($canDo->get('core.create')) {
$childBar->save2copy('article.save2copy');
}
}
);
$toolbar->cancel('article.cancel', 'JTOOLBAR_CLOSE');
if (!$isNew) {
if (ComponentHelper::isEnabled('com_contenthistory') && $this->state->params->get('save_history', 0) && $itemEditable) {
$toolbar->versions('com_content.article', $this->item->id);
}
$url = RouteHelper::getArticleRoute($this->item->id . ':' . $this->item->alias, $this->item->catid, $this->item->language);
$toolbar->preview(Route::link('site', $url, true), 'JGLOBAL_PREVIEW')
->bodyHeight(80)
->modalWidth(90);
if (PluginHelper::isEnabled('system', 'jooa11y')) {
$toolbar->jooa11y(Route::link('site', $url . '&jooa11y=1', true), 'JGLOBAL_JOOA11Y')
->bodyHeight(80)
->modalWidth(90);
}
if (Associations::isEnabled() && ComponentHelper::isEnabled('com_associations')) {
$toolbar->standardButton('associations', 'JTOOLBAR_ASSOCIATIONS', 'article.editAssociations')
->icon('icon-contract')
->listCheck(false);
}
}
}
$toolbar->divider();
$toolbar->inlinehelp();
$toolbar->help('Articles:_Edit');
}
/**
* Add the modal toolbar.
*
* @return void
*
* @since 5.0.0
*
* @throws \Exception
*/
protected function addModalToolbar()
{
$user = $this->getCurrentUser();
$userId = $user->id;
$isNew = ($this->item->id == 0);
$checkedOut = !(\is_null($this->item->checked_out) || $this->item->checked_out == $userId);
$toolbar = $this->getDocument()->getToolbar();
// Build the actions for new and existing records.
$canDo = $this->canDo;
ToolbarHelper::title(
Text::_('COM_CONTENT_PAGE_' . ($checkedOut ? 'VIEW_ARTICLE' : ($isNew ? 'ADD_ARTICLE' : 'EDIT_ARTICLE'))),
'pencil-alt article-add'
);
$canCreate = $isNew && (\count($user->getAuthorisedCategories('com_content', 'core.create')) > 0);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId);
// For new records, check the create permission.
if ($canCreate || $canEdit) {
$toolbar->apply('article.apply');
$toolbar->save('article.save');
}
$toolbar->cancel('article.cancel');
}
}

View File

@ -0,0 +1,259 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\View\Articles;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Administrator\Helper\ContentHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View class for a list of articles.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*/
public $activeFilters;
/**
* All transition, which can be executed of one if the items
*
* @var array
*/
protected $transitions = [];
/**
* Is this view an Empty State
*
* @var boolean
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Is the vote plugin enabled on the site
*
* @var boolean
* @since 4.4.0
*/
protected $vote = false;
/**
* Are hits being recorded on the site?
*
* @var boolean
* @since 4.4.0
*/
protected $hits = false;
/**
* Display the view
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->vote = PluginHelper::isEnabled('content', 'vote');
$this->hits = ComponentHelper::getParams('com_content')->get('record_hits', 1) == 1;
if (!\count($this->items) && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
if (ComponentHelper::getParams('com_content')->get('workflow_enabled')) {
PluginHelper::importPlugin('workflow');
$this->transitions = $this->get('Transitions');
}
// Check for errors.
if (\count($errors = $this->get('Errors')) || $this->transitions === false) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// We don't need toolbar in the modal window.
if ($this->getLayout() !== 'modal') {
$this->addToolbar();
// We do not need to filter by language when multilingual is disabled
if (!Multilanguage::isEnabled()) {
unset($this->activeFilters['language']);
$this->filterForm->removeField('language', 'filter');
}
} else {
// In article associations modal we need to remove language filter if forcing a language.
// We also need to change the category filter to show show categories with All or the forced language.
if ($forcedLanguage = Factory::getApplication()->getInput()->get('forcedLanguage', '', 'CMD')) {
// If the language is forced we can't allow to select the language, so transform the language selector filter into a hidden field.
$languageXml = new \SimpleXMLElement('<field name="language" type="hidden" default="' . $forcedLanguage . '" />');
$this->filterForm->setField($languageXml, 'filter', true);
// Also, unset the active language filter so the search tools is not open by default with this filter.
unset($this->activeFilters['language']);
// One last changes needed is to change the category filter to just show categories with All language or with the forced language.
$this->filterForm->setFieldAttribute('category_id', 'language', '*,' . $forcedLanguage, 'filter');
}
}
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_content', 'category', $this->state->get('filter.category_id'));
$user = $this->getCurrentUser();
$toolbar = $this->getDocument()->getToolbar();
ToolbarHelper::title(Text::_('COM_CONTENT_ARTICLES_TITLE'), 'copy article');
if ($canDo->get('core.create') || \count($user->getAuthorisedCategories('com_content', 'core.create')) > 0) {
$toolbar->addNew('article.add');
}
if (!$this->isEmptyState && ($canDo->get('core.edit.state') || \count($this->transitions))) {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('status-group')
->text('JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
if ($canDo->get('core.execute.transition') && \count($this->transitions)) {
$childBar->separatorButton('transition-headline')
->text('COM_CONTENT_RUN_TRANSITIONS')
->buttonClass('text-center py-2 h3');
$cmd = "Joomla.submitbutton('articles.runTransition');";
$messages = "{error: [Joomla.JText._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST')]}";
$alert = 'Joomla.renderMessages(' . $messages . ')';
$cmd = 'if (document.adminForm.boxchecked.value == 0) { ' . $alert . ' } else { ' . $cmd . ' }';
foreach ($this->transitions as $transition) {
$childBar->standardButton('transition', $transition['text'])
->buttonClass('transition-' . (int) $transition['value'])
->icon('icon-project-diagram')
->onclick('document.adminForm.transition_id.value=' . (int) $transition['value'] . ';' . $cmd);
}
$childBar->separatorButton('transition-separator');
}
if ($canDo->get('core.edit.state')) {
$childBar->publish('articles.publish')->listCheck(true);
$childBar->unpublish('articles.unpublish')->listCheck(true);
$childBar->standardButton('featured', 'JFEATURE', 'articles.featured')
->listCheck(true);
$childBar->standardButton('unfeatured', 'JUNFEATURE', 'articles.unfeatured')
->listCheck(true);
$childBar->archive('articles.archive')->listCheck(true);
$childBar->checkin('articles.checkin');
if ($this->state->get('filter.published') != ContentComponent::CONDITION_TRASHED) {
$childBar->trash('articles.trash')->listCheck(true);
}
}
// Add a batch button
if (
$user->authorise('core.create', 'com_content')
&& $user->authorise('core.edit', 'com_content')
) {
$childBar->popupButton('batch', 'JTOOLBAR_BATCH')
->popupType('inline')
->textHeader(Text::_('COM_CONTENT_BATCH_OPTIONS'))
->url('#joomla-dialog-batch')
->modalWidth('800px')
->modalHeight('fit-content')
->listCheck(true);
}
}
if (!$this->isEmptyState && $this->state->get('filter.published') == ContentComponent::CONDITION_TRASHED && $canDo->get('core.delete')) {
$toolbar->delete('articles.delete', 'JTOOLBAR_DELETE_FROM_TRASH')
->message('JGLOBAL_CONFIRM_DELETE')
->listCheck(true);
}
if ($user->authorise('core.admin', 'com_content') || $user->authorise('core.options', 'com_content')) {
$toolbar->preferences('com_content');
}
$toolbar->help('Articles');
}
}

View File

@ -0,0 +1,206 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Content\Administrator\View\Featured;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Administrator\Helper\ContentHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View class for a list of featured articles.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*/
public $activeFilters;
/**
* All transition, which can be executed of one if the items
*
* @var array
*/
protected $transitions = [];
/**
* Is this view an Empty State
*
* @var boolean
* @since 4.0.0
*/
private $isEmptyState = false;
/**
* Display the view
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->vote = PluginHelper::isEnabled('content', 'vote');
$this->hits = ComponentHelper::getParams('com_content')->get('record_hits', 1);
if (!\count($this->items) && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
if (ComponentHelper::getParams('com_content')->get('workflow_enabled')) {
PluginHelper::importPlugin('workflow');
$this->transitions = $this->get('Transitions');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
$this->addToolbar();
// We do not need to filter by language when multilingual is disabled
if (!Multilanguage::isEnabled()) {
unset($this->activeFilters['language']);
$this->filterForm->removeField('language', 'filter');
}
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_content', 'category', $this->state->get('filter.category_id'));
$user = $this->getCurrentUser();
$toolbar = $this->getDocument()->getToolbar();
ToolbarHelper::title(Text::_('COM_CONTENT_FEATURED_TITLE'), 'star featured');
if ($canDo->get('core.create') || \count($user->getAuthorisedCategories('com_content', 'core.create')) > 0) {
$toolbar->addNew('article.add');
}
if (!$this->isEmptyState && ($canDo->get('core.edit.state') || \count($this->transitions))) {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('status-group', 'JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
if (\count($this->transitions)) {
$childBar->separatorButton('transition-headline', 'COM_CONTENT_RUN_TRANSITIONS')
->buttonClass('text-center py-2 h3');
$cmd = "Joomla.submitbutton('articles.runTransition');";
$messages = "{error: [Joomla.JText._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST')]}";
$alert = 'Joomla.renderMessages(' . $messages . ')';
$cmd = 'if (document.adminForm.boxchecked.value == 0) { ' . $alert . ' } else { ' . $cmd . ' }';
foreach ($this->transitions as $transition) {
$childBar->standardButton('transition', $transition['text'])
->buttonClass('transition-' . (int) $transition['value'])
->icon('icon-project-diagram')
->onclick('document.adminForm.transition_id.value=' . (int) $transition['value'] . ';' . $cmd);
}
$childBar->separatorButton('transition-separator');
}
if ($canDo->get('core.edit.state')) {
$childBar->publish('articles.publish')->listCheck(true);
$childBar->unpublish('articles.unpublish')->listCheck(true);
$childBar->standardButton('unfeatured', 'JUNFEATURE')
->task('articles.unfeatured')
->listCheck(true);
$childBar->archive('articles.archive')->listCheck(true);
$childBar->checkin('articles.checkin');
if (!$this->state->get('filter.published') == ContentComponent::CONDITION_TRASHED) {
$childBar->trash('articles.trash')->listCheck(true);
}
}
}
if (!$this->isEmptyState && $this->state->get('filter.published') == ContentComponent::CONDITION_TRASHED && $canDo->get('core.delete')) {
$toolbar->delete('articles.delete', 'JTOOLBAR_DELETE_FROM_TRASH')
->message('JGLOBAL_CONFIRM_DELETE')
->listCheck(true);
}
if ($user->authorise('core.admin', 'com_content') || $user->authorise('core.options', 'com_content')) {
$toolbar->preferences('com_content');
}
$toolbar->help('Articles:_Featured');
}
}