first commit
This commit is contained in:
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\FormController;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Association edit controller class.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationController extends FormController
|
||||
{
|
||||
/**
|
||||
* Method to edit an existing record.
|
||||
*
|
||||
* @param string $key The name of the primary key of the URL variable.
|
||||
* @param string $urlVar The name of the URL variable if different from the primary key
|
||||
* (sometimes required to avoid router collisions).
|
||||
*
|
||||
* @return FormController|boolean True if access level check and checkout passes, false otherwise.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function edit($key = null, $urlVar = null)
|
||||
{
|
||||
list($extensionName, $typeName) = explode('.', $this->input->get('itemtype', '', 'string'), 2);
|
||||
|
||||
$id = $this->input->get('id', 0, 'int');
|
||||
|
||||
// Check if reference item can be edited.
|
||||
if (!AssociationsHelper::allowEdit($extensionName, $typeName, $id)) {
|
||||
$this->setMessage(Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error');
|
||||
$this->setRedirect(Route::_('index.php?option=com_associations&view=associations', false));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for canceling the edit action
|
||||
*
|
||||
* @param string $key The name of the primary key of the URL variable.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function cancel($key = null)
|
||||
{
|
||||
$this->checkToken();
|
||||
|
||||
list($extensionName, $typeName) = explode('.', $this->input->get('itemtype', '', 'string'), 2);
|
||||
|
||||
// Only check in, if component item type allows to check out.
|
||||
if (AssociationsHelper::typeSupportsCheckout($extensionName, $typeName)) {
|
||||
$ids = [];
|
||||
$targetId = $this->input->get('target-id', '', 'string');
|
||||
|
||||
if ($targetId !== '') {
|
||||
$ids = array_unique(explode(',', $targetId));
|
||||
}
|
||||
|
||||
$ids[] = $this->input->get('id', 0, 'int');
|
||||
|
||||
foreach ($ids as $key => $id) {
|
||||
AssociationsHelper::getItem($extensionName, $typeName, $id)->checkIn();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setRedirect(Route::_('index.php?option=com_associations&view=associations', false));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\AdminController;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Associations controller class.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationsController extends AdminController
|
||||
{
|
||||
/**
|
||||
* The URL view list variable.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $view_list = 'associations';
|
||||
|
||||
/**
|
||||
* 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|boolean
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function getModel($name = 'Associations', $prefix = 'Administrator', $config = ['ignore_request' => true])
|
||||
{
|
||||
return parent::getModel($name, $prefix, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to purge the associations table.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->checkToken();
|
||||
|
||||
$this->getModel('associations')->purge();
|
||||
$this->setRedirect(Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to delete the orphans from the associations table.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
$this->checkToken();
|
||||
|
||||
$this->getModel('associations')->clean();
|
||||
$this->setRedirect(Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check in an item from the association item overview.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.1
|
||||
*/
|
||||
public function checkin()
|
||||
{
|
||||
// Set the redirect so we can just stop processing when we find a condition we can't process
|
||||
$this->setRedirect(Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false));
|
||||
|
||||
// Figure out if the item supports checking and check it in
|
||||
list($extensionName, $typeName) = explode('.', $this->input->get('itemtype'));
|
||||
|
||||
$extension = AssociationsHelper::getSupportedExtension($extensionName);
|
||||
$types = $extension->get('types');
|
||||
|
||||
if (!\array_key_exists($typeName, $types)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AssociationsHelper::typeSupportsCheckout($extensionName, $typeName) === false) {
|
||||
// How on earth we came to that point, eject internet
|
||||
return;
|
||||
}
|
||||
|
||||
$cid = (array) $this->input->get('cid', [], 'int');
|
||||
|
||||
if (empty($cid)) {
|
||||
// Seems we don't have an id to work with.
|
||||
return;
|
||||
}
|
||||
|
||||
// We know the first element is the one we need because we don't allow multi selection of rows
|
||||
$id = $cid[0];
|
||||
|
||||
if ($id === 0) {
|
||||
// Seems we don't have an id to work with.
|
||||
return;
|
||||
}
|
||||
|
||||
if (AssociationsHelper::canCheckinItem($extensionName, $typeName, $id) === true) {
|
||||
$item = AssociationsHelper::getItem($extensionName, $typeName, $id);
|
||||
|
||||
$item->checkIn($id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setRedirect(
|
||||
Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list),
|
||||
Text::_('COM_ASSOCIATIONS_YOU_ARE_NOT_ALLOWED_TO_CHECKIN_THIS_ITEM')
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Component Controller
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class DisplayController extends BaseController
|
||||
{
|
||||
/**
|
||||
* The default view.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $default_view = 'associations';
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Dispatcher;
|
||||
|
||||
use Joomla\CMS\Access\Exception\NotAllowed;
|
||||
use Joomla\CMS\Dispatcher\ComponentDispatcher;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* ComponentDispatcher class for com_associations
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Dispatcher extends ComponentDispatcher
|
||||
{
|
||||
/**
|
||||
* Method to check component access permission
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception|NotAllowed
|
||||
*/
|
||||
protected function checkAccess()
|
||||
{
|
||||
parent::checkAccess();
|
||||
|
||||
// Check if user has permission to access the component item type.
|
||||
$itemType = $this->input->get('itemtype', '', 'string');
|
||||
|
||||
if ($itemType !== '') {
|
||||
list($extensionName, $typeName) = explode('.', $itemType);
|
||||
|
||||
if (!AssociationsHelper::hasSupport($extensionName)) {
|
||||
throw new \Exception(
|
||||
Text::sprintf('COM_ASSOCIATIONS_COMPONENT_NOT_SUPPORTED', $this->app->getLanguage()->_($extensionName)),
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->app->getIdentity()->authorise('core.manage', $extensionName)) {
|
||||
throw new NotAllowed($this->app->getLanguage()->_('JERROR_ALERTNOAUTHOR'), 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\Field\ListField;
|
||||
use Joomla\CMS\Language\LanguageHelper;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
use Joomla\Utilities\ArrayHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Field listing item languages
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class ItemlanguageField extends ListField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $type = 'Itemlanguage';
|
||||
|
||||
/**
|
||||
* Method to get the field options.
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
$input = Factory::getApplication()->getInput();
|
||||
|
||||
list($extensionName, $typeName) = explode('.', $input->get('itemtype', '', 'string'), 2);
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = AssociationsHelper::getExtensionHelper($extensionName);
|
||||
|
||||
$languageField = $helper->getTypeFieldName($typeName, 'language');
|
||||
$referenceId = $input->get('id', 0, 'int');
|
||||
$reference = ArrayHelper::fromObject(AssociationsHelper::getItem($extensionName, $typeName, $referenceId));
|
||||
$referenceLang = $reference[$languageField];
|
||||
|
||||
// Get item associations given ID and item type
|
||||
$associations = AssociationsHelper::getAssociationList($extensionName, $typeName, $referenceId);
|
||||
|
||||
// Check if user can create items in this component item type.
|
||||
$canCreate = AssociationsHelper::allowAdd($extensionName, $typeName);
|
||||
|
||||
// Gets existing languages.
|
||||
$existingLanguages = LanguageHelper::getContentLanguages([0, 1], false);
|
||||
|
||||
$options = [];
|
||||
|
||||
// Each option has the format "<lang>|<id>", example: "en-GB|1"
|
||||
foreach ($existingLanguages as $langCode => $language) {
|
||||
// If language code is equal to reference language we don't need it.
|
||||
if ($language->lang_code == $referenceLang) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[$langCode] = new \stdClass();
|
||||
$options[$langCode]->text = $language->title;
|
||||
|
||||
// If association exists in this language.
|
||||
if (isset($associations[$language->lang_code])) {
|
||||
$itemId = (int) $associations[$language->lang_code]['id'];
|
||||
$options[$langCode]->value = $language->lang_code . ':' . $itemId . ':edit';
|
||||
|
||||
// Check if user does have permission to edit the associated item.
|
||||
$canEdit = AssociationsHelper::allowEdit($extensionName, $typeName, $itemId);
|
||||
|
||||
// Check if item can be checked out
|
||||
$canCheckout = AssociationsHelper::canCheckinItem($extensionName, $typeName, $itemId);
|
||||
|
||||
// Disable language if user is not allowed to edit the item associated to it.
|
||||
$options[$langCode]->disable = !($canEdit && $canCheckout);
|
||||
} else {
|
||||
// New item, id = 0 and disabled if user is not allowed to create new items.
|
||||
$options[$langCode]->value = $language->lang_code . ':0:add';
|
||||
|
||||
// Disable language if user is not allowed to create items.
|
||||
$options[$langCode]->disable = !$canCreate;
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(parent::getOptions(), $options);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Form\Field\GroupedlistField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* A drop down containing all component item types that implement associations.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class ItemtypeField extends GroupedlistField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $type = 'Itemtype';
|
||||
|
||||
/**
|
||||
* Method to get the field input markup.
|
||||
*
|
||||
* @return array The field option objects as a nested array in groups.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function getGroups()
|
||||
{
|
||||
$options = [];
|
||||
$extensions = AssociationsHelper::getSupportedExtensions();
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
if ($extension->get('associationssupport') === true) {
|
||||
foreach ($extension->get('types') as $type) {
|
||||
$context = $extension->get('component') . '.' . $type->get('name');
|
||||
$options[$extension->get('title')][] = HTMLHelper::_('select.option', $context, $type->get('title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by alpha order.
|
||||
uksort($options, 'strnatcmp');
|
||||
|
||||
// Add options to parent array.
|
||||
return array_merge(parent::getGroups(), $options);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Field\Modal;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\FormField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Session\Session;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Supports a modal item picker.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationField extends FormField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $type = 'Modal_Association';
|
||||
|
||||
/**
|
||||
* Method to get the field input markup.
|
||||
*
|
||||
* @return string The field input markup.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function getInput()
|
||||
{
|
||||
// @todo USE Layouts here!!!
|
||||
// The active item id field.
|
||||
$value = (int) $this->value ?: '';
|
||||
|
||||
$doc = Factory::getApplication()->getDocument();
|
||||
$wa = $doc->getWebAssetManager();
|
||||
|
||||
$doc->addScriptOptions('admin_associations_modal', ['itemId' => $value]);
|
||||
$wa->useScript('com_associations.admin-associations-modal');
|
||||
|
||||
// Setup variables for display.
|
||||
$html = [];
|
||||
|
||||
$linkAssociations = 'index.php?option=com_associations&view=associations&layout=modal&tmpl=component'
|
||||
. '&forcedItemType=' . Factory::getApplication()->getInput()->get('itemtype', '', 'string') . '&function=jSelectAssociation_' . $this->id;
|
||||
|
||||
$linkAssociations .= "&forcedLanguage=' + document.getElementById('target-association').getAttribute('data-language') + '";
|
||||
|
||||
$urlSelect = $linkAssociations . '&' . Session::getFormToken() . '=1';
|
||||
|
||||
// Select custom association button
|
||||
$html[] = '<button'
|
||||
. ' type="button"'
|
||||
. ' id="select-change"'
|
||||
. ' class="btn btn-secondary' . ($value ? '' : ' hidden') . '"'
|
||||
. ' data-bs-toggle="modal"'
|
||||
. ' data-select="' . Text::_('COM_ASSOCIATIONS_SELECT_TARGET') . '"'
|
||||
. ' data-change="' . Text::_('COM_ASSOCIATIONS_CHANGE_TARGET') . '"'
|
||||
. ' data-bs-target="#associationSelect' . $this->id . 'Modal">'
|
||||
. '<span class="icon-file" aria-hidden="true"></span> '
|
||||
. '<span id="select-change-text"></span>'
|
||||
. '</button>';
|
||||
|
||||
// Clear association button
|
||||
$html[] = '<button'
|
||||
. ' type="button"'
|
||||
. ' class="btn btn-secondary' . ($value ? '' : ' hidden') . '"'
|
||||
. ' onclick="return Joomla.submitbutton(\'undo-association\');"'
|
||||
. ' id="remove-assoc">'
|
||||
. '<span class="icon-times" aria-hidden="true"></span> ' . Text::_('JCLEAR')
|
||||
. '</button>';
|
||||
|
||||
$html[] = '<input type="hidden" id="' . $this->id . '_id" name="' . $this->name . '" value="' . $value . '">';
|
||||
|
||||
// Select custom association modal
|
||||
$html[] = HTMLHelper::_(
|
||||
'bootstrap.renderModal',
|
||||
'associationSelect' . $this->id . 'Modal',
|
||||
[
|
||||
'title' => Text::_('COM_ASSOCIATIONS_SELECT_TARGET'),
|
||||
'backdrop' => 'static',
|
||||
'url' => $urlSelect,
|
||||
'height' => '400px',
|
||||
'width' => '800px',
|
||||
'bodyHeight' => 70,
|
||||
'modalWidth' => 80,
|
||||
'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">'
|
||||
. Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>',
|
||||
]
|
||||
);
|
||||
|
||||
return implode("\n", $html);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,671 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Helper;
|
||||
|
||||
use Joomla\CMS\Association\AssociationExtensionInterface;
|
||||
use Joomla\CMS\Association\AssociationServiceInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Helper\ContentHelper;
|
||||
use Joomla\CMS\Language\LanguageHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Layout\LayoutHelper;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Database\ParameterType;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Associations component helper.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationsHelper extends ContentHelper
|
||||
{
|
||||
/**
|
||||
* Array of Registry objects of extensions
|
||||
*
|
||||
* @var array
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static $extensionsSupport = null;
|
||||
|
||||
/**
|
||||
* List of extensions name with support
|
||||
*
|
||||
* @var array
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static $supportedExtensionsList = [];
|
||||
|
||||
/**
|
||||
* Get the associated items for an item
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param int $itemId The id of item for which we need the associated items
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getAssociationList($extensionName, $typeName, $itemId)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
return $helper->getAssociationList($typeName, $itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the the instance of the extension helper class
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
*
|
||||
* @return \Joomla\CMS\Association\AssociationExtensionHelper|null
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getExtensionHelper($extensionName)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$support = self::$extensionsSupport[$extensionName];
|
||||
|
||||
return $support->get('helper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item information
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param int $itemId The id of item for which we need the associated items
|
||||
*
|
||||
* @return \Joomla\CMS\Table\Table|null
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getItem($extensionName, $typeName, $itemId)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
return $helper->getItem($typeName, $itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if extension supports associations
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function hasSupport($extensionName)
|
||||
{
|
||||
if (\is_null(self::$extensionsSupport)) {
|
||||
self::getSupportedExtensions();
|
||||
}
|
||||
|
||||
return \in_array($extensionName, self::$supportedExtensionsList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the helper for the given class.
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
*
|
||||
* @return AssociationExtensionInterface|null
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
private static function loadHelper($extensionName)
|
||||
{
|
||||
$component = Factory::getApplication()->bootComponent($extensionName);
|
||||
|
||||
if ($component instanceof AssociationServiceInterface) {
|
||||
return $component->getAssociationsExtension();
|
||||
}
|
||||
|
||||
// Check if associations helper exists
|
||||
if (!file_exists(JPATH_ADMINISTRATOR . '/components/' . $extensionName . '/helpers/associations.php')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
require_once JPATH_ADMINISTRATOR . '/components/' . $extensionName . '/helpers/associations.php';
|
||||
|
||||
$componentAssociationsHelperClassName = self::getExtensionHelperClassName($extensionName);
|
||||
|
||||
if (!class_exists($componentAssociationsHelperClassName, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an instance of the helper class
|
||||
return new $componentAssociationsHelperClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extension specific helper class name
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private static function getExtensionHelperClassName($extensionName)
|
||||
{
|
||||
$realName = self::getExtensionRealName($extensionName);
|
||||
|
||||
return ucfirst($realName) . 'AssociationsHelper';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real extension name. This means without com_
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private static function getExtensionRealName($extensionName)
|
||||
{
|
||||
return strpos($extensionName, 'com_') === false ? $extensionName : substr($extensionName, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated language edit links Html.
|
||||
*
|
||||
* @param string $extensionName Extension Name
|
||||
* @param string $typeName ItemType
|
||||
* @param integer $itemId Item id.
|
||||
* @param string $itemLanguage Item language code.
|
||||
* @param boolean $addLink True for adding edit links. False for just text.
|
||||
* @param boolean $assocLanguages True for showing non associated content languages. False only languages with associations.
|
||||
*
|
||||
* @return string The language HTML
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getAssociationHtmlList($extensionName, $typeName, $itemId, $itemLanguage, $addLink = true, $assocLanguages = true)
|
||||
{
|
||||
// Get the associations list for this item.
|
||||
$items = self::getAssociationList($extensionName, $typeName, $itemId);
|
||||
|
||||
$titleFieldName = self::getTypeFieldName($extensionName, $typeName, 'title');
|
||||
|
||||
// Get all content languages.
|
||||
$languages = LanguageHelper::getContentLanguages([0, 1], false);
|
||||
$content_languages = array_column($languages, 'lang_code');
|
||||
|
||||
// Display warning if Content Language is trashed or deleted
|
||||
foreach ($items as $item) {
|
||||
if (!\in_array($item['language'], $content_languages)) {
|
||||
Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_ASSOCIATIONS_CONTENTLANGUAGE_WARNING', $item['language']), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
$canEditReference = self::allowEdit($extensionName, $typeName, $itemId);
|
||||
$canCreate = self::allowAdd($extensionName, $typeName);
|
||||
|
||||
// Create associated items list.
|
||||
foreach ($languages as $langCode => $language) {
|
||||
// Don't do for the reference language.
|
||||
if ($langCode == $itemLanguage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't show languages with associations, if we don't want to show them.
|
||||
if ($assocLanguages && isset($items[$langCode])) {
|
||||
unset($items[$langCode]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't show languages without associations, if we don't want to show them.
|
||||
if (!$assocLanguages && !isset($items[$langCode])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get html parameters.
|
||||
if (isset($items[$langCode])) {
|
||||
$title = $items[$langCode][$titleFieldName];
|
||||
$additional = '';
|
||||
|
||||
if (isset($items[$langCode]['catid'])) {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Get the category name
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('title'))
|
||||
->from($db->quoteName('#__categories'))
|
||||
->where($db->quoteName('id') . ' = :id')
|
||||
->bind(':id', $items[$langCode]['catid'], ParameterType::INTEGER);
|
||||
|
||||
$db->setQuery($query);
|
||||
$categoryTitle = $db->loadResult();
|
||||
|
||||
$additional = '<strong>' . Text::sprintf('JCATEGORY_SPRINTF', $categoryTitle) . '</strong> <br>';
|
||||
} elseif (isset($items[$langCode]['menutype'])) {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Get the menutype name
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('title'))
|
||||
->from($db->quoteName('#__menu_types'))
|
||||
->where($db->quoteName('menutype') . ' = :menutype')
|
||||
->bind(':menutype', $items[$langCode]['menutype']);
|
||||
|
||||
$db->setQuery($query);
|
||||
$menutypeTitle = $db->loadResult();
|
||||
|
||||
$additional = '<strong>' . Text::sprintf('COM_MENUS_MENU_SPRINTF', $menutypeTitle) . '</strong><br>';
|
||||
}
|
||||
|
||||
$labelClass = 'bg-secondary';
|
||||
$target = $langCode . ':' . $items[$langCode]['id'] . ':edit';
|
||||
$allow = $canEditReference
|
||||
&& self::allowEdit($extensionName, $typeName, $items[$langCode]['id'])
|
||||
&& self::canCheckinItem($extensionName, $typeName, $items[$langCode]['id']);
|
||||
|
||||
$additional .= $addLink && $allow ? Text::_('COM_ASSOCIATIONS_EDIT_ASSOCIATION') : '';
|
||||
} else {
|
||||
$items[$langCode] = [];
|
||||
|
||||
$title = Text::_('COM_ASSOCIATIONS_NO_ASSOCIATION');
|
||||
$additional = $addLink ? Text::_('COM_ASSOCIATIONS_ADD_NEW_ASSOCIATION') : '';
|
||||
$labelClass = 'bg-warning text-dark';
|
||||
$target = $langCode . ':0:add';
|
||||
$allow = $canCreate;
|
||||
}
|
||||
|
||||
// Generate item Html.
|
||||
$options = [
|
||||
'option' => 'com_associations',
|
||||
'view' => 'association',
|
||||
'layout' => 'edit',
|
||||
'itemtype' => $extensionName . '.' . $typeName,
|
||||
'task' => 'association.edit',
|
||||
'id' => $itemId,
|
||||
'target' => $target,
|
||||
];
|
||||
|
||||
$url = Route::_('index.php?' . http_build_query($options));
|
||||
$url = $allow && $addLink ? $url : '';
|
||||
$text = $language->lang_code;
|
||||
|
||||
$tooltip = '<strong>' . htmlspecialchars($language->title, ENT_QUOTES, 'UTF-8') . '</strong><br>'
|
||||
. htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '<br><br>' . $additional;
|
||||
$classes = 'badge ' . $labelClass;
|
||||
|
||||
$items[$langCode]['link'] = '<a href="' . $url . '" class="' . $classes . '">' . $text . '</a>'
|
||||
. '<div role="tooltip">' . $tooltip . '</div>';
|
||||
}
|
||||
|
||||
return LayoutHelper::render('joomla.content.associations', $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all extensions with associations support.
|
||||
*
|
||||
* @return array The extensions.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getSupportedExtensions()
|
||||
{
|
||||
if (!\is_null(self::$extensionsSupport)) {
|
||||
return self::$extensionsSupport;
|
||||
}
|
||||
|
||||
self::$extensionsSupport = [];
|
||||
|
||||
$extensions = self::getEnabledExtensions();
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
$support = self::getSupportedExtension($extension->element);
|
||||
|
||||
if ($support->get('associationssupport') === true) {
|
||||
self::$supportedExtensionsList[] = $extension->element;
|
||||
}
|
||||
|
||||
self::$extensionsSupport[$extension->element] = $support;
|
||||
}
|
||||
|
||||
return self::$extensionsSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item context based on the item key.
|
||||
*
|
||||
* @param string $extensionName The extension identifier.
|
||||
*
|
||||
* @return \Joomla\Registry\Registry The item properties.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getSupportedExtension($extensionName)
|
||||
{
|
||||
$result = new Registry();
|
||||
|
||||
$result->def('component', $extensionName);
|
||||
$result->def('associationssupport', false);
|
||||
$result->def('helper', null);
|
||||
|
||||
$helper = self::loadHelper($extensionName);
|
||||
|
||||
if (!$helper) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result->set('helper', $helper);
|
||||
|
||||
if ($helper->hasAssociationsSupport() === false) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result->set('associationssupport', true);
|
||||
|
||||
// Get the translated titles.
|
||||
$languagePath = JPATH_ADMINISTRATOR . '/components/' . $extensionName;
|
||||
$lang = Factory::getLanguage();
|
||||
|
||||
$lang->load($extensionName . '.sys', JPATH_ADMINISTRATOR);
|
||||
$lang->load($extensionName . '.sys', $languagePath);
|
||||
$lang->load($extensionName, JPATH_ADMINISTRATOR);
|
||||
$lang->load($extensionName, $languagePath);
|
||||
|
||||
$result->def('title', Text::_(strtoupper($extensionName)));
|
||||
|
||||
// Get the supported types
|
||||
$types = $helper->getItemTypes();
|
||||
$rTypes = [];
|
||||
|
||||
foreach ($types as $typeName) {
|
||||
$details = $helper->getType($typeName);
|
||||
$context = 'component';
|
||||
$title = $helper->getTypeTitle($typeName);
|
||||
$languageKey = $typeName;
|
||||
|
||||
$typeNameExploded = explode('.', $typeName);
|
||||
|
||||
if (array_pop($typeNameExploded) === 'category') {
|
||||
$languageKey = strtoupper($extensionName) . '_CATEGORIES';
|
||||
$context = 'category';
|
||||
}
|
||||
|
||||
if ($lang->hasKey(strtoupper($extensionName . '_' . $title . 'S'))) {
|
||||
$languageKey = strtoupper($extensionName . '_' . $title . 'S');
|
||||
}
|
||||
|
||||
$title = $lang->hasKey($languageKey) ? Text::_($languageKey) : Text::_('COM_ASSOCIATIONS_ITEMS');
|
||||
|
||||
$rType = new Registry();
|
||||
|
||||
$rType->def('name', $typeName);
|
||||
$rType->def('details', $details);
|
||||
$rType->def('title', $title);
|
||||
$rType->def('context', $context);
|
||||
|
||||
$rTypes[$typeName] = $rType;
|
||||
}
|
||||
|
||||
$result->def('types', $rTypes);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all installed and enabled extensions
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private static function getEnabledExtensions()
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
|
||||
->where($db->quoteName('enabled') . ' = 1');
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the content languages.
|
||||
*
|
||||
* @return array Array of objects all content languages by language code.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getContentLanguages()
|
||||
{
|
||||
return LanguageHelper::getContentLanguages([0, 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated items for an item
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param int $itemId The id of item for which we need the associated items
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function allowEdit($extensionName, $typeName, $itemId)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
if (method_exists($helper, 'allowEdit')) {
|
||||
return $helper->allowEdit($typeName, $itemId);
|
||||
}
|
||||
|
||||
return Factory::getUser()->authorise('core.edit', $extensionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is allowed to create items.
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
*
|
||||
* @return boolean True on allowed.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function allowAdd($extensionName, $typeName)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
if (method_exists($helper, 'allowAdd')) {
|
||||
return $helper->allowAdd($typeName);
|
||||
}
|
||||
|
||||
return Factory::getUser()->authorise('core.create', $extensionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item is checked out
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param int $itemId The id of item for which we need the associated items
|
||||
*
|
||||
* @return boolean True if item is checked out.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function isCheckoutItem($extensionName, $typeName, $itemId)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!self::typeSupportsCheckout($extensionName, $typeName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
if (method_exists($helper, 'isCheckoutItem')) {
|
||||
return $helper->isCheckoutItem($typeName, $itemId);
|
||||
}
|
||||
|
||||
$item = self::getItem($extensionName, $typeName, $itemId);
|
||||
|
||||
$checkedOutFieldName = $helper->getTypeFieldName($typeName, 'checked_out');
|
||||
|
||||
return $item->{$checkedOutFieldName} != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can checkin an item.
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param int $itemId The id of item for which we need the associated items
|
||||
*
|
||||
* @return boolean True on allowed.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function canCheckinItem($extensionName, $typeName, $itemId)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!self::typeSupportsCheckout($extensionName, $typeName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
if (method_exists($helper, 'canCheckinItem')) {
|
||||
return $helper->canCheckinItem($typeName, $itemId);
|
||||
}
|
||||
|
||||
$item = self::getItem($extensionName, $typeName, $itemId);
|
||||
|
||||
$checkedOutFieldName = $helper->getTypeFieldName($typeName, 'checked_out');
|
||||
|
||||
$userId = Factory::getUser()->id;
|
||||
|
||||
return ($item->{$checkedOutFieldName} == $userId || $item->{$checkedOutFieldName} == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the type supports checkout
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
*
|
||||
* @return boolean True on allowed.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function typeSupportsCheckout($extensionName, $typeName)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
$support = $helper->getTypeSupport($typeName);
|
||||
|
||||
return !empty($support['checkout']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a table field name for a type
|
||||
*
|
||||
* @param string $extensionName The extension name with com_
|
||||
* @param string $typeName The item type
|
||||
* @param string $fieldName The item type
|
||||
*
|
||||
* @return boolean True on allowed.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function getTypeFieldName($extensionName, $typeName, $fieldName)
|
||||
{
|
||||
if (!self::hasSupport($extensionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the extension specific helper method
|
||||
$helper = self::getExtensionHelper($extensionName);
|
||||
|
||||
return $helper->getTypeFieldName($typeName, $fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the language filter system plugin extension id.
|
||||
*
|
||||
* @return integer The language filter system plugin extension id.
|
||||
*
|
||||
* @since 3.7.2
|
||||
*/
|
||||
public static function getLanguagefilterPluginId()
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('extension_id'))
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('languagefilter'));
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$result = (int) $db->loadResult();
|
||||
} catch (\RuntimeException $e) {
|
||||
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Model;
|
||||
|
||||
use Joomla\CMS\MVC\Model\ListModel;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Methods supporting a list of article records.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationModel extends ListModel
|
||||
{
|
||||
/**
|
||||
* Method to get the record form.
|
||||
*
|
||||
* @param array $data Data for the form.
|
||||
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
|
||||
*
|
||||
* @return \Joomla\CMS\Form\Form|boolean A Form object on success, false on failure
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function getForm($data = [], $loadData = true)
|
||||
{
|
||||
// Get the form.
|
||||
$form = $this->loadForm('com_associations.association', 'association', ['control' => 'jform', 'load_data' => $loadData]);
|
||||
|
||||
return !empty($form) ? $form : false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,568 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\Model;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
|
||||
use Joomla\CMS\MVC\Model\ListModel;
|
||||
use Joomla\CMS\Table\Table;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
use Joomla\Database\Exception\ExecutionFailureException;
|
||||
use Joomla\Database\ParameterType;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Methods supporting a list of article records.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class AssociationsModel extends ListModel
|
||||
{
|
||||
/**
|
||||
* Override parent constructor.
|
||||
*
|
||||
* @param array $config An optional associative array of configuration settings.
|
||||
* @param MVCFactoryInterface $factory The factory.
|
||||
*
|
||||
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
|
||||
* @since 3.7
|
||||
*/
|
||||
public function __construct($config = [], MVCFactoryInterface $factory = null)
|
||||
{
|
||||
if (empty($config['filter_fields'])) {
|
||||
$config['filter_fields'] = [
|
||||
'id',
|
||||
'title',
|
||||
'ordering',
|
||||
'itemtype',
|
||||
'language',
|
||||
'association',
|
||||
'menutype',
|
||||
'menutype_title',
|
||||
'level',
|
||||
'state',
|
||||
'category_id',
|
||||
'category_title',
|
||||
'access',
|
||||
'access_level',
|
||||
];
|
||||
}
|
||||
|
||||
parent::__construct($config, $factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to auto-populate the model state.
|
||||
*
|
||||
* Note. Calling getState in this method will result in recursion.
|
||||
*
|
||||
* @param string $ordering An optional ordering field.
|
||||
* @param string $direction An optional direction (asc|desc).
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function populateState($ordering = 'ordering', $direction = 'asc')
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
|
||||
$forcedLanguage = $app->getInput()->get('forcedLanguage', '', 'cmd');
|
||||
$forcedItemType = $app->getInput()->get('forcedItemType', '', 'string');
|
||||
|
||||
// Adjust the context to support modal layouts.
|
||||
if ($layout = $app->getInput()->get('layout')) {
|
||||
$this->context .= '.' . $layout;
|
||||
}
|
||||
|
||||
// Adjust the context to support forced languages.
|
||||
if ($forcedLanguage) {
|
||||
$this->context .= '.' . $forcedLanguage;
|
||||
}
|
||||
|
||||
// Adjust the context to support forced component item types.
|
||||
if ($forcedItemType) {
|
||||
$this->context .= '.' . $forcedItemType;
|
||||
}
|
||||
|
||||
$this->setState('itemtype', $this->getUserStateFromRequest($this->context . '.itemtype', 'itemtype', '', 'string'));
|
||||
$this->setState('language', $this->getUserStateFromRequest($this->context . '.language', 'language', '', 'string'));
|
||||
|
||||
$this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
|
||||
$this->setState('filter.state', $this->getUserStateFromRequest($this->context . '.filter.state', 'filter_state', '', 'cmd'));
|
||||
$this->setState('filter.category_id', $this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id', '', 'cmd'));
|
||||
$this->setState('filter.menutype', $this->getUserStateFromRequest($this->context . '.filter.menutype', 'filter_menutype', '', 'string'));
|
||||
$this->setState('filter.access', $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', '', 'string'));
|
||||
$this->setState('filter.level', $this->getUserStateFromRequest($this->context . '.filter.level', 'filter_level', '', 'cmd'));
|
||||
|
||||
// List state information.
|
||||
parent::populateState($ordering, $direction);
|
||||
|
||||
// Force a language.
|
||||
if (!empty($forcedLanguage)) {
|
||||
$this->setState('language', $forcedLanguage);
|
||||
}
|
||||
|
||||
// Force a component item type.
|
||||
if (!empty($forcedItemType)) {
|
||||
$this->setState('itemtype', $forcedItemType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 3.7.0
|
||||
*/
|
||||
protected function getStoreId($id = '')
|
||||
{
|
||||
// Compile the store id.
|
||||
$id .= ':' . $this->getState('itemtype');
|
||||
$id .= ':' . $this->getState('language');
|
||||
$id .= ':' . $this->getState('filter.search');
|
||||
$id .= ':' . $this->getState('filter.state');
|
||||
$id .= ':' . $this->getState('filter.category_id');
|
||||
$id .= ':' . $this->getState('filter.menutype');
|
||||
$id .= ':' . $this->getState('filter.access');
|
||||
$id .= ':' . $this->getState('filter.level');
|
||||
|
||||
return parent::getStoreId($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an SQL query to load the list data.
|
||||
*
|
||||
* @return \Joomla\Database\DatabaseQuery|boolean
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function getListQuery()
|
||||
{
|
||||
$type = null;
|
||||
|
||||
list($extensionName, $typeName) = explode('.', $this->state->get('itemtype'), 2);
|
||||
|
||||
$extension = AssociationsHelper::getSupportedExtension($extensionName);
|
||||
$types = $extension->get('types');
|
||||
|
||||
if (\array_key_exists($typeName, $types)) {
|
||||
$type = $types[$typeName];
|
||||
}
|
||||
|
||||
if (\is_null($type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new query object.
|
||||
$user = $this->getCurrentUser();
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$details = $type->get('details');
|
||||
|
||||
if (!\array_key_exists('support', $details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$support = $details['support'];
|
||||
|
||||
if (!\array_key_exists('fields', $details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = $details['fields'];
|
||||
|
||||
// Main query.
|
||||
$query->select($db->quoteName($fields['id'], 'id'))
|
||||
->select($db->quoteName($fields['title'], 'title'))
|
||||
->select($db->quoteName($fields['alias'], 'alias'));
|
||||
|
||||
if (!\array_key_exists('tables', $details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tables = $details['tables'];
|
||||
|
||||
foreach ($tables as $key => $table) {
|
||||
$query->from($db->quoteName($table, $key));
|
||||
}
|
||||
|
||||
if (!\array_key_exists('joins', $details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$joins = $details['joins'];
|
||||
|
||||
foreach ($joins as $join) {
|
||||
$query->join($join['type'], $db->quoteName($join['condition']));
|
||||
}
|
||||
|
||||
// Join over the language.
|
||||
$query->select($db->quoteName($fields['language'], 'language'))
|
||||
->select($db->quoteName('l.title', 'language_title'))
|
||||
->select($db->quoteName('l.image', 'language_image'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__languages', 'l'),
|
||||
$db->quoteName('l.lang_code') . ' = ' . $db->quoteName($fields['language'])
|
||||
);
|
||||
$extensionNameItem = $extensionName . '.item';
|
||||
|
||||
// Join over the associations.
|
||||
$query->select('COUNT(' . $db->quoteName('asso2.id') . ') > 1 AS ' . $db->quoteName('association'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__associations', 'asso'),
|
||||
$db->quoteName('asso.id') . ' = ' . $db->quoteName($fields['id'])
|
||||
. ' AND ' . $db->quoteName('asso.context') . ' = :context'
|
||||
)
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__associations', 'asso2'),
|
||||
$db->quoteName('asso2.key') . ' = ' . $db->quoteName('asso.key')
|
||||
)
|
||||
->bind(':context', $extensionNameItem);
|
||||
|
||||
// Prepare the group by clause.
|
||||
$groupby = [
|
||||
$fields['id'],
|
||||
$fields['title'],
|
||||
$fields['alias'],
|
||||
$fields['language'],
|
||||
'l.title',
|
||||
'l.image',
|
||||
];
|
||||
|
||||
// Select author for ACL checks.
|
||||
if (!empty($fields['created_user_id'])) {
|
||||
$query->select($db->quoteName($fields['created_user_id'], 'created_user_id'));
|
||||
|
||||
$groupby[] = $fields['created_user_id'];
|
||||
}
|
||||
|
||||
// Select checked out data for check in checkins.
|
||||
if (!empty($fields['checked_out']) && !empty($fields['checked_out_time'])) {
|
||||
$query->select($db->quoteName($fields['checked_out'], 'checked_out'))
|
||||
->select($db->quoteName($fields['checked_out_time'], 'checked_out_time'));
|
||||
|
||||
// Join over the users.
|
||||
$query->select($db->quoteName('u.name', 'editor'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__users', 'u'),
|
||||
$db->quoteName('u.id') . ' = ' . $db->quoteName($fields['checked_out'])
|
||||
);
|
||||
|
||||
$groupby[] = 'u.name';
|
||||
$groupby[] = $fields['checked_out'];
|
||||
$groupby[] = $fields['checked_out_time'];
|
||||
}
|
||||
|
||||
// If component item type supports ordering, select the ordering also.
|
||||
if (!empty($fields['ordering'])) {
|
||||
$query->select($db->quoteName($fields['ordering'], 'ordering'));
|
||||
|
||||
$groupby[] = $fields['ordering'];
|
||||
}
|
||||
|
||||
// If component item type supports state, select the item state also.
|
||||
if (!empty($fields['state'])) {
|
||||
$query->select($db->quoteName($fields['state'], 'state'));
|
||||
|
||||
$groupby[] = $fields['state'];
|
||||
}
|
||||
|
||||
// If component item type supports level, select the level also.
|
||||
if (!empty($fields['level'])) {
|
||||
$query->select($db->quoteName($fields['level'], 'level'));
|
||||
|
||||
$groupby[] = $fields['level'];
|
||||
}
|
||||
|
||||
// If component item type supports categories, select the category also.
|
||||
if (!empty($fields['catid'])) {
|
||||
$query->select($db->quoteName($fields['catid'], 'catid'));
|
||||
|
||||
// Join over the categories.
|
||||
$query->select($db->quoteName('c.title', 'category_title'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__categories', 'c'),
|
||||
$db->quoteName('c.id') . ' = ' . $db->quoteName($fields['catid'])
|
||||
);
|
||||
|
||||
$groupby[] = 'c.title';
|
||||
$groupby[] = $fields['catid'];
|
||||
}
|
||||
|
||||
// If component item type supports menu type, select the menu type also.
|
||||
if (!empty($fields['menutype'])) {
|
||||
$query->select($db->quoteName($fields['menutype'], 'menutype'));
|
||||
|
||||
// Join over the menu types.
|
||||
$query->select($db->quoteName('mt.title', 'menutype_title'))
|
||||
->select($db->quoteName('mt.id', 'menutypeid'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__menu_types', 'mt'),
|
||||
$db->quoteName('mt.menutype') . ' = ' . $db->quoteName($fields['menutype'])
|
||||
);
|
||||
|
||||
$groupby[] = 'mt.title';
|
||||
$groupby[] = 'mt.id';
|
||||
$groupby[] = $fields['menutype'];
|
||||
}
|
||||
|
||||
// If component item type supports access level, select the access level also.
|
||||
if (\array_key_exists('acl', $support) && $support['acl'] == true && !empty($fields['access'])) {
|
||||
$query->select($db->quoteName($fields['access'], 'access'));
|
||||
|
||||
// Join over the access levels.
|
||||
$query->select($db->quoteName('ag.title', 'access_level'))
|
||||
->join(
|
||||
'LEFT',
|
||||
$db->quoteName('#__viewlevels', 'ag'),
|
||||
$db->quoteName('ag.id') . ' = ' . $db->quoteName($fields['access'])
|
||||
);
|
||||
|
||||
$groupby[] = 'ag.title';
|
||||
$groupby[] = $fields['access'];
|
||||
|
||||
// Implement View Level Access.
|
||||
if (!$user->authorise('core.admin', $extensionName)) {
|
||||
$groups = $user->getAuthorisedViewLevels();
|
||||
$query->whereIn($db->quoteName($fields['access']), $groups);
|
||||
}
|
||||
}
|
||||
|
||||
// If component item type is menus we need to remove the root item and the administrator menu.
|
||||
if ($extensionName === 'com_menus') {
|
||||
$query->where($db->quoteName($fields['id']) . ' > 1')
|
||||
->where($db->quoteName('a.client_id') . ' = 0');
|
||||
}
|
||||
|
||||
// If component item type is category we need to remove all other component categories.
|
||||
if ($typeName === 'category') {
|
||||
$query->where($db->quoteName('a.extension') . ' = :extensionname')
|
||||
->bind(':extensionname', $extensionName);
|
||||
} elseif ($typeNameExploded = explode('.', $typeName)) {
|
||||
if (\count($typeNameExploded) > 1 && array_pop($typeNameExploded) === 'category') {
|
||||
$section = implode('.', $typeNameExploded);
|
||||
$extensionNameSection = $extensionName . '.' . $section;
|
||||
$query->where($db->quoteName('a.extension') . ' = :extensionsection')
|
||||
->bind(':extensionsection', $extensionNameSection);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter on the language.
|
||||
if ($language = $this->getState('language')) {
|
||||
$query->where($db->quoteName($fields['language']) . ' = :language')
|
||||
->bind(':language', $language);
|
||||
}
|
||||
|
||||
// Filter by item state.
|
||||
$state = $this->getState('filter.state');
|
||||
|
||||
if (is_numeric($state)) {
|
||||
$state = (int) $state;
|
||||
$query->where($db->quoteName($fields['state']) . ' = :state')
|
||||
->bind(':state', $state, ParameterType::INTEGER);
|
||||
} elseif ($state === '') {
|
||||
$query->whereIn($db->quoteName($fields['state']), [0, 1]);
|
||||
}
|
||||
|
||||
// Filter on the category.
|
||||
$baselevel = 1;
|
||||
|
||||
if ($categoryId = $this->getState('filter.category_id')) {
|
||||
$categoryTable = Table::getInstance('Category', '\\Joomla\\CMS\\Table\\');
|
||||
$categoryTable->load($categoryId);
|
||||
$baselevel = (int) $categoryTable->level;
|
||||
|
||||
$lft = (int) $categoryTable->lft;
|
||||
$rgt = (int) $categoryTable->rgt;
|
||||
$query->where($db->quoteName('c.lft') . ' >= :lft')
|
||||
->where($db->quoteName('c.rgt') . ' <= :rgt')
|
||||
->bind(':lft', $lft, ParameterType::INTEGER)
|
||||
->bind(':rgt', $rgt, ParameterType::INTEGER);
|
||||
}
|
||||
|
||||
// Filter on the level.
|
||||
if ($level = $this->getState('filter.level')) {
|
||||
$queryLevel = ((int) $level + (int) $baselevel - 1);
|
||||
$query->where($db->quoteName('a.level') . ' <= :alevel')
|
||||
->bind(':alevel', $queryLevel, ParameterType::INTEGER);
|
||||
}
|
||||
|
||||
// Filter by menu type.
|
||||
if ($menutype = $this->getState('filter.menutype')) {
|
||||
$query->where($db->quoteName($fields['menutype']) . ' = :menutype2')
|
||||
->bind(':menutype2', $menutype);
|
||||
}
|
||||
|
||||
// Filter by access level.
|
||||
if ($access = $this->getState('filter.access')) {
|
||||
$access = (int) $access;
|
||||
$query->where($db->quoteName($fields['access']) . ' = :access')
|
||||
->bind(':access', $access, ParameterType::INTEGER);
|
||||
}
|
||||
|
||||
// Filter by search in name.
|
||||
if ($search = $this->getState('filter.search')) {
|
||||
if (stripos($search, 'id:') === 0) {
|
||||
$search = (int) substr($search, 3);
|
||||
$query->where($db->quoteName($fields['id']) . ' = :searchid')
|
||||
->bind(':searchid', $search, ParameterType::INTEGER);
|
||||
} else {
|
||||
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
|
||||
$query->where('(' . $db->quoteName($fields['title']) . ' LIKE :title'
|
||||
. ' OR ' . $db->quoteName($fields['alias']) . ' LIKE :alias)')
|
||||
->bind(':title', $search)
|
||||
->bind(':alias', $search);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the group by clause
|
||||
$query->group($db->quoteName($groupby));
|
||||
|
||||
// Add the list ordering clause
|
||||
$listOrdering = $this->state->get('list.ordering', 'id');
|
||||
$orderDirn = $this->state->get('list.direction', 'ASC');
|
||||
|
||||
$query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete associations from #__associations table.
|
||||
*
|
||||
* @param string $context The associations context. Empty for all.
|
||||
* @param string $key The associations key. Empty for all.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function purge($context = '', $key = '')
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)->delete($db->quoteName('#__associations'));
|
||||
|
||||
// Filter by associations context.
|
||||
if ($context) {
|
||||
$query->where($db->quoteName('context') . ' = :context')
|
||||
->bind(':context', $context);
|
||||
}
|
||||
|
||||
// Filter by key.
|
||||
if ($key) {
|
||||
$query->where($db->quoteName('key') . ' = :key')
|
||||
->bind(':key', $key);
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$db->execute();
|
||||
} catch (ExecutionFailureException $e) {
|
||||
$app->enqueueMessage(Text::_('COM_ASSOCIATIONS_PURGE_FAILED'), 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$app->enqueueMessage(
|
||||
Text::_((int) $db->getAffectedRows() > 0 ? 'COM_ASSOCIATIONS_PURGE_SUCCESS' : 'COM_ASSOCIATIONS_PURGE_NONE'),
|
||||
'message'
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete orphans from the #__associations table.
|
||||
*
|
||||
* @param string $context The associations context. Empty for all.
|
||||
* @param string $key The associations key. Empty for all.
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function clean($context = '', $key = '')
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('key') . ', COUNT(*)')
|
||||
->from($db->quoteName('#__associations'))
|
||||
->group($db->quoteName('key'))
|
||||
->having('COUNT(*) = 1');
|
||||
|
||||
// Filter by associations context.
|
||||
if ($context) {
|
||||
$query->where($db->quoteName('context') . ' = :context')
|
||||
->bind(':context', $context);
|
||||
}
|
||||
|
||||
// Filter by key.
|
||||
if ($key) {
|
||||
$query->where($db->quoteName('key') . ' = :key')
|
||||
->bind(':key', $key);
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
$assocKeys = $db->loadObjectList();
|
||||
|
||||
$count = 0;
|
||||
|
||||
// We have orphans. Let's delete them.
|
||||
foreach ($assocKeys as $value) {
|
||||
$query->clear()
|
||||
->delete($db->quoteName('#__associations'))
|
||||
->where($db->quoteName('key') . ' = :valuekey')
|
||||
->bind(':valuekey', $value->key);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$db->execute();
|
||||
} catch (ExecutionFailureException $e) {
|
||||
$app->enqueueMessage(Text::_('COM_ASSOCIATIONS_DELETE_ORPHANS_FAILED'), 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$count += (int) $db->getAffectedRows();
|
||||
}
|
||||
|
||||
$app->enqueueMessage(
|
||||
Text::_($count > 0 ? 'COM_ASSOCIATIONS_DELETE_ORPHANS_SUCCESS' : 'COM_ASSOCIATIONS_DELETE_ORPHANS_NONE'),
|
||||
'message'
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,381 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\View\Association;
|
||||
|
||||
use Joomla\CMS\Application\AdministratorApplication;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\Form;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\View\GenericDataException;
|
||||
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
|
||||
use Joomla\CMS\Pagination\Pagination;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\CMS\Toolbar\Toolbar;
|
||||
use Joomla\CMS\Toolbar\ToolbarHelper;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
use Joomla\Component\Associations\Administrator\Model\AssociationModel;
|
||||
use Joomla\Input\Input;
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\Utilities\ArrayHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* View class for a list of articles.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class HtmlView extends BaseHtmlView
|
||||
{
|
||||
/**
|
||||
* An array of items
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* The pagination object
|
||||
*
|
||||
* @var Pagination
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $pagination;
|
||||
|
||||
/**
|
||||
* The model state
|
||||
*
|
||||
* @var \Joomla\Registry\Registry
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Selected item type properties.
|
||||
*
|
||||
* @var Registry
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $itemType;
|
||||
|
||||
/**
|
||||
* The application
|
||||
*
|
||||
* @var AdministratorApplication
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* The ID of the reference language
|
||||
*
|
||||
* @var integer
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $referenceId = 0;
|
||||
|
||||
/**
|
||||
* The type name
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $typeName = '';
|
||||
|
||||
/**
|
||||
* The reference language
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $referenceLanguage = '';
|
||||
|
||||
/**
|
||||
* The title of the reference language
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $referenceTitle = '';
|
||||
|
||||
/**
|
||||
* The value of the reference title
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $referenceTitleValue = '';
|
||||
|
||||
/**
|
||||
* The URL to the edit screen
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $editUri = '';
|
||||
|
||||
/**
|
||||
* The ID of the target field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $targetId = '';
|
||||
|
||||
/**
|
||||
* The target language
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $targetLanguage = '';
|
||||
|
||||
/**
|
||||
* The source of the target field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $defaultTargetSrc = '';
|
||||
|
||||
/**
|
||||
* The action to perform for the target field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $targetAction = '';
|
||||
|
||||
/**
|
||||
* The title of the target field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $targetTitle = '';
|
||||
|
||||
/**
|
||||
* The edit form
|
||||
*
|
||||
* @var Form
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* Set if the option is set to save as copy
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private $save2copy = false;
|
||||
|
||||
/**
|
||||
* The type of language
|
||||
*
|
||||
* @var Registry
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* The supported types
|
||||
*
|
||||
* @var array
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private $typeSupports = [];
|
||||
|
||||
/**
|
||||
* The extension name
|
||||
*
|
||||
* @var string
|
||||
* @since 3.7.0
|
||||
*/
|
||||
private $extensionName = '';
|
||||
|
||||
/**
|
||||
* Display the view
|
||||
*
|
||||
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function display($tpl = null): void
|
||||
{
|
||||
/** @var AssociationModel $model */
|
||||
$model = $this->getModel();
|
||||
|
||||
// Check for errors.
|
||||
if (\count($errors = $model->getErrors())) {
|
||||
throw new GenericDataException(implode("\n", $errors), 500);
|
||||
}
|
||||
|
||||
$this->app = Factory::getApplication();
|
||||
$this->form = $model->getForm();
|
||||
/** @var Input $input */
|
||||
$input = $this->app->getInput();
|
||||
$this->referenceId = $input->get('id', 0, 'int');
|
||||
|
||||
[$extensionName, $typeName] = explode('.', $input->get('itemtype', '', 'string'), 2);
|
||||
|
||||
/** @var Registry $extension */
|
||||
$extension = AssociationsHelper::getSupportedExtension($extensionName);
|
||||
$types = $extension->get('types');
|
||||
|
||||
if (\array_key_exists($typeName, $types)) {
|
||||
$this->type = $types[$typeName];
|
||||
$this->typeSupports = [];
|
||||
$details = $this->type->get('details');
|
||||
$this->save2copy = false;
|
||||
|
||||
if (\array_key_exists('support', $details)) {
|
||||
$support = $details['support'];
|
||||
$this->typeSupports = $support;
|
||||
}
|
||||
|
||||
if (!empty($this->typeSupports['save2copy'])) {
|
||||
$this->save2copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->extensionName = $extensionName;
|
||||
$this->typeName = $typeName;
|
||||
$this->itemType = $extensionName . '.' . $typeName;
|
||||
|
||||
$languageField = AssociationsHelper::getTypeFieldName($extensionName, $typeName, 'language');
|
||||
$referenceId = $input->get('id', 0, 'int');
|
||||
$reference = ArrayHelper::fromObject(AssociationsHelper::getItem($extensionName, $typeName, $referenceId));
|
||||
|
||||
$this->referenceLanguage = $reference[$languageField];
|
||||
$this->referenceTitle = AssociationsHelper::getTypeFieldName($extensionName, $typeName, 'title');
|
||||
$this->referenceTitleValue = $reference[$this->referenceTitle];
|
||||
|
||||
// Check for special case category
|
||||
$typeNameExploded = explode('.', $typeName);
|
||||
|
||||
if (array_pop($typeNameExploded) === 'category') {
|
||||
$this->typeName = 'category';
|
||||
|
||||
if ($typeNameExploded) {
|
||||
$extensionName .= '.' . implode('.', $typeNameExploded);
|
||||
}
|
||||
|
||||
$options = [
|
||||
'option' => 'com_categories',
|
||||
'view' => 'category',
|
||||
'extension' => $extensionName,
|
||||
'tmpl' => 'component',
|
||||
];
|
||||
} else {
|
||||
$options = [
|
||||
'option' => $extensionName,
|
||||
'view' => $typeName,
|
||||
'extension' => $extensionName,
|
||||
'tmpl' => 'component',
|
||||
];
|
||||
}
|
||||
|
||||
// Reference and target edit links.
|
||||
$this->editUri = 'index.php?' . http_build_query($options);
|
||||
|
||||
// Get target language.
|
||||
$this->targetId = '0';
|
||||
$this->targetLanguage = '';
|
||||
$this->defaultTargetSrc = '';
|
||||
$this->targetAction = '';
|
||||
$this->targetTitle = '';
|
||||
|
||||
if ($target = $input->get('target', '', 'string')) {
|
||||
$matches = preg_split("#[\:]+#", $target);
|
||||
$this->targetAction = $matches[2];
|
||||
$this->targetId = $matches[1];
|
||||
$this->targetLanguage = $matches[0];
|
||||
$this->targetTitle = AssociationsHelper::getTypeFieldName($extensionName, $typeName, 'title');
|
||||
$task = $typeName . '.' . $this->targetAction;
|
||||
|
||||
/**
|
||||
* Let's put the target src into a variable to use in the javascript code
|
||||
* to avoid race conditions when the reference iframe loads.
|
||||
*/
|
||||
$this->getDocument()->addScriptOptions('targetSrc', Route::_($this->editUri . '&task=' . $task . '&id=' . (int) $this->targetId));
|
||||
$this->form->setValue('itemlanguage', '', $this->targetLanguage . ':' . $this->targetId . ':' . $this->targetAction);
|
||||
}
|
||||
|
||||
$this->addToolbar();
|
||||
|
||||
parent::display($tpl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the page title and toolbar.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function addToolbar(): void
|
||||
{
|
||||
// Hide main menu.
|
||||
$this->app->getInput()->set('hidemainmenu', 1);
|
||||
|
||||
$helper = AssociationsHelper::getExtensionHelper($this->extensionName);
|
||||
$title = $helper->getTypeTitle($this->typeName);
|
||||
|
||||
$languageKey = strtoupper($this->extensionName . '_' . $title . 'S');
|
||||
|
||||
if ($this->typeName === 'category') {
|
||||
$languageKey = strtoupper($this->extensionName) . '_CATEGORIES';
|
||||
}
|
||||
|
||||
ToolbarHelper::title(
|
||||
Text::sprintf(
|
||||
'COM_ASSOCIATIONS_TITLE_EDIT',
|
||||
Text::_($this->extensionName),
|
||||
Text::_($languageKey)
|
||||
),
|
||||
'language assoc'
|
||||
);
|
||||
|
||||
$toolbar = Toolbar::getInstance();
|
||||
$toolbar->customButton('reference')
|
||||
->html('<joomla-toolbar-button><button onclick="Joomla.submitbutton(\'reference\')" '
|
||||
. 'class="btn btn-success"><span class="icon-save" aria-hidden="true"></span>'
|
||||
. Text::_('COM_ASSOCIATIONS_SAVE_REFERENCE') . '</button></joomla-toolbar-button>');
|
||||
|
||||
$toolbar->customButton('target')
|
||||
->html('<joomla-toolbar-button><button onclick="Joomla.submitbutton(\'target\')" '
|
||||
. 'class="btn btn-success"><span class="icon-save" aria-hidden="true"></span>'
|
||||
. Text::_('COM_ASSOCIATIONS_SAVE_TARGET') . '</button></joomla-toolbar-button>');
|
||||
|
||||
if ($this->typeName === 'category' || $this->extensionName === 'com_menus' || $this->save2copy === true) {
|
||||
$toolbar->standardButton('', 'COM_ASSOCIATIONS_COPY_REFERENCE', 'copy')
|
||||
->icon('icon-copy')
|
||||
->listCheck(false);
|
||||
}
|
||||
|
||||
$toolbar->cancel('association.cancel');
|
||||
$toolbar->help('Multilingual_Associations:_Edit');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_associations
|
||||
*
|
||||
* @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\Associations\Administrator\View\Associations;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Associations;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\CMS\Toolbar\Toolbar;
|
||||
use Joomla\CMS\Toolbar\ToolbarHelper;
|
||||
use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* View class for a list of articles.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class HtmlView extends BaseHtmlView
|
||||
{
|
||||
/**
|
||||
* An array of items
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* The pagination object
|
||||
*
|
||||
* @var \Joomla\CMS\Pagination\Pagination
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $pagination;
|
||||
|
||||
/**
|
||||
* The model state
|
||||
*
|
||||
* @var object
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Selected item type properties.
|
||||
*
|
||||
* @var \Joomla\Registry\Registry
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public $itemType = null;
|
||||
|
||||
/**
|
||||
* Display the view
|
||||
*
|
||||
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public function display($tpl = null)
|
||||
{
|
||||
$this->state = $this->get('State');
|
||||
$this->filterForm = $this->get('FilterForm');
|
||||
$this->activeFilters = $this->get('ActiveFilters');
|
||||
|
||||
if (!Associations::isEnabled()) {
|
||||
$link = Route::_('index.php?option=com_plugins&task=plugin.edit&extension_id=' . AssociationsHelper::getLanguagefilterPluginId());
|
||||
Factory::getApplication()->enqueueMessage(Text::sprintf('COM_ASSOCIATIONS_ERROR_NO_ASSOC', $link), 'warning');
|
||||
} elseif ($this->state->get('itemtype') != '' && $this->state->get('language') != '') {
|
||||
$type = null;
|
||||
|
||||
list($extensionName, $typeName) = explode('.', $this->state->get('itemtype'), 2);
|
||||
|
||||
$extension = AssociationsHelper::getSupportedExtension($extensionName);
|
||||
|
||||
$types = $extension->get('types');
|
||||
|
||||
if (\array_key_exists($typeName, $types)) {
|
||||
$type = $types[$typeName];
|
||||
}
|
||||
|
||||
$this->itemType = $type;
|
||||
|
||||
if (\is_null($type)) {
|
||||
Factory::getApplication()->enqueueMessage(Text::_('COM_ASSOCIATIONS_ERROR_NO_TYPE'), 'warning');
|
||||
} else {
|
||||
$this->extensionName = $extensionName;
|
||||
$this->typeName = $typeName;
|
||||
$this->typeSupports = [];
|
||||
$this->typeFields = [];
|
||||
|
||||
$details = $type->get('details');
|
||||
|
||||
if (\array_key_exists('support', $details)) {
|
||||
$support = $details['support'];
|
||||
$this->typeSupports = $support;
|
||||
}
|
||||
|
||||
if (\array_key_exists('fields', $details)) {
|
||||
$fields = $details['fields'];
|
||||
$this->typeFields = $fields;
|
||||
}
|
||||
|
||||
// Dynamic filter form.
|
||||
// This selectors doesn't have to activate the filter bar.
|
||||
unset($this->activeFilters['itemtype']);
|
||||
unset($this->activeFilters['language']);
|
||||
|
||||
// Remove filters options depending on selected type.
|
||||
if (empty($support['state'])) {
|
||||
unset($this->activeFilters['state']);
|
||||
$this->filterForm->removeField('state', 'filter');
|
||||
}
|
||||
|
||||
if (empty($support['category'])) {
|
||||
unset($this->activeFilters['category_id']);
|
||||
$this->filterForm->removeField('category_id', 'filter');
|
||||
}
|
||||
|
||||
if ($extensionName !== 'com_menus') {
|
||||
unset($this->activeFilters['menutype']);
|
||||
$this->filterForm->removeField('menutype', 'filter');
|
||||
}
|
||||
|
||||
if (empty($support['level'])) {
|
||||
unset($this->activeFilters['level']);
|
||||
$this->filterForm->removeField('level', 'filter');
|
||||
}
|
||||
|
||||
if (empty($support['acl'])) {
|
||||
unset($this->activeFilters['access']);
|
||||
$this->filterForm->removeField('access', 'filter');
|
||||
}
|
||||
|
||||
// Add extension attribute to category filter.
|
||||
if (empty($support['catid'])) {
|
||||
$this->filterForm->setFieldAttribute('category_id', 'extension', $extensionName, 'filter');
|
||||
|
||||
if ($this->getLayout() == 'modal') {
|
||||
// We need to change the category filter to only show categories tagged to All or to the forced language.
|
||||
if ($forcedLanguage = Factory::getApplication()->getInput()->get('forcedLanguage', '', 'CMD')) {
|
||||
$this->filterForm->setFieldAttribute('category_id', 'language', '*,' . $forcedLanguage, 'filter');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $this->get('Items');
|
||||
$this->pagination = $this->get('Pagination');
|
||||
|
||||
$linkParameters = [
|
||||
'layout' => 'edit',
|
||||
'itemtype' => $extensionName . '.' . $typeName,
|
||||
'task' => 'association.edit',
|
||||
];
|
||||
|
||||
$this->editUri = 'index.php?option=com_associations&view=association&' . http_build_query($linkParameters);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for errors.
|
||||
if (\count($errors = $this->get('Errors'))) {
|
||||
throw new \Exception(implode("\n", $errors), 500);
|
||||
}
|
||||
|
||||
$this->addToolbar();
|
||||
|
||||
parent::display($tpl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the page title and toolbar.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function addToolbar()
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
if (isset($this->typeName) && isset($this->extensionName)) {
|
||||
$helper = AssociationsHelper::getExtensionHelper($this->extensionName);
|
||||
$title = $helper->getTypeTitle($this->typeName);
|
||||
|
||||
$languageKey = strtoupper($this->extensionName . '_' . $title . 'S');
|
||||
|
||||
if ($this->typeName === 'category') {
|
||||
$languageKey = strtoupper($this->extensionName) . '_CATEGORIES';
|
||||
}
|
||||
|
||||
ToolbarHelper::title(
|
||||
Text::sprintf(
|
||||
'COM_ASSOCIATIONS_TITLE_LIST',
|
||||
Text::_($this->extensionName),
|
||||
Text::_($languageKey)
|
||||
),
|
||||
'language assoc'
|
||||
);
|
||||
} else {
|
||||
ToolbarHelper::title(Text::_('COM_ASSOCIATIONS_TITLE_LIST_SELECT'), 'language assoc');
|
||||
}
|
||||
|
||||
$toolbar = Toolbar::getInstance();
|
||||
|
||||
if ($user->authorise('core.admin', 'com_associations') || $user->authorise('core.options', 'com_associations')) {
|
||||
if (!isset($this->typeName)) {
|
||||
$toolbar->standardButton('', 'COM_ASSOCIATIONS_PURGE', 'associations.purge')
|
||||
->icon('icon-purge')
|
||||
->listCheck(false);
|
||||
$toolbar->standardButton('', 'COM_ASSOCIATIONS_DELETE_ORPHANS', 'associations.clean')
|
||||
->icon('icon-refresh')
|
||||
->listCheck(false);
|
||||
}
|
||||
|
||||
$toolbar->preferences('com_associations');
|
||||
}
|
||||
|
||||
$toolbar->help('Multilingual_Associations');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user