From 130b59a0c5d956bb9bdad04c315b749d6ea7d446 Mon Sep 17 00:00:00 2001 From: Arne Stappen Date: Thu, 29 Jun 2017 18:18:29 +0200 Subject: [PATCH] Add an articletree widget --- src/Resources/contao/classes/Ajax.php | 16 +- src/Resources/contao/config/config.php | 1 + src/Resources/contao/dca/tl_content.php | 168 +---------- src/Resources/contao/widgets/ArticleTree.php | 284 +++++++++++++++++++ 4 files changed, 304 insertions(+), 165 deletions(-) create mode 100644 src/Resources/contao/widgets/ArticleTree.php diff --git a/src/Resources/contao/classes/Ajax.php b/src/Resources/contao/classes/Ajax.php index ec594bd7fd..e75c618df6 100644 --- a/src/Resources/contao/classes/Ajax.php +++ b/src/Resources/contao/classes/Ajax.php @@ -267,6 +267,7 @@ public function executePostActions(DataContainer $dc) // Reload the page/file picker case 'reloadPagetree': case 'reloadFiletree': + case 'reloadArticletree': $intId = \Input::get('id'); $strField = $dc->inputName = \Input::post('name'); @@ -332,7 +333,20 @@ public function executePostActions(DataContainer $dc) // Set the new value $varValue = \Input::post('value', true); - $strKey = ($this->strAction == 'reloadPagetree') ? 'pageTree' : 'fileTree'; + + switch($this->strAction) + { + case 'reloadPagetree': + $strKey = 'pageTree'; + + break; + case 'reloadFiletree': + $strKey = 'fileTree'; + + break; + case 'reloadArticletree': + $strKey = 'articleTree'; + } // Convert the selected values if ($varValue != '') diff --git a/src/Resources/contao/config/config.php b/src/Resources/contao/config/config.php index 099d7d0f8f..cf99e99bbb 100644 --- a/src/Resources/contao/config/config.php +++ b/src/Resources/contao/config/config.php @@ -215,6 +215,7 @@ 'inputUnit' => 'InputUnit', 'trbl' => 'TrblField', 'chmod' => 'ChmodTable', + 'articleTree' => 'ArticleTree', 'pageTree' => 'PageTree', 'pageSelector' => 'PageSelector', 'fileTree' => 'FileTree', diff --git a/src/Resources/contao/dca/tl_content.php b/src/Resources/contao/dca/tl_content.php index 830d68ed39..7dd2ee8eb0 100644 --- a/src/Resources/contao/dca/tl_content.php +++ b/src/Resources/contao/dca/tl_content.php @@ -716,26 +716,16 @@ ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['articleAlias'], 'exclude' => true, - 'inputType' => 'select', - 'options_callback' => array('tl_content', 'getArticleAlias'), - 'eval' => array('mandatory'=>true, 'chosen'=>true, 'submitOnChange'=>true, 'tl_class'=>'w50 wizard'), - 'wizard' => array - ( - array('tl_content', 'editArticleAlias') - ), + 'inputType' => 'articleTree', + 'eval' => array('mandatory'=>true, 'fieldType'=>'radio'), 'sql' => "int(10) unsigned NOT NULL default '0'" ), 'article' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['article'], 'exclude' => true, - 'inputType' => 'select', - 'options_callback' => array('tl_content', 'getArticles'), - 'eval' => array('mandatory'=>true, 'chosen'=>true, 'submitOnChange'=>true, 'tl_class'=>'w50 wizard'), - 'wizard' => array - ( - array('tl_content', 'editArticle') - ), + 'inputType' => 'articleTree', + 'eval' => array('mandatory'=>true, 'fieldType'=>'radio'), 'sql' => "int(10) unsigned NOT NULL default '0'" ), 'form' => array @@ -1195,68 +1185,6 @@ public function addCteType($arrRow) } - /** - * Return the edit article alias wizard - * - * @param DataContainer $dc - * - * @return string - */ - public function editArticleAlias(DataContainer $dc) - { - return ($dc->value < 1) ? '' : ' value))) . '\',\'url\':this.href});return false">' . Image::getHtml('alias.svg', $GLOBALS['TL_LANG']['tl_content']['editalias'][0]) . ''; - } - - - /** - * Get all articles and return them as array (article alias) - * - * @param DataContainer $dc - * - * @return array - */ - public function getArticleAlias(DataContainer $dc) - { - $arrPids = array(); - $arrAlias = array(); - - if (!$this->User->isAdmin) - { - foreach ($this->User->pagemounts as $id) - { - $arrPids[] = $id; - $arrPids = array_merge($arrPids, $this->Database->getChildRecords($id, 'tl_page')); - } - - if (empty($arrPids)) - { - return $arrAlias; - } - - $objAlias = $this->Database->prepare("SELECT a.id, a.pid, a.title, a.inColumn, p.title AS parent FROM tl_article a LEFT JOIN tl_page p ON p.id=a.pid WHERE a.pid IN(". implode(',', array_map('intval', array_unique($arrPids))) .") AND a.id!=(SELECT pid FROM tl_content WHERE id=?) ORDER BY parent, a.sorting") - ->execute($dc->id); - } - else - { - $objAlias = $this->Database->prepare("SELECT a.id, a.pid, a.title, a.inColumn, p.title AS parent FROM tl_article a LEFT JOIN tl_page p ON p.id=a.pid WHERE a.id!=(SELECT pid FROM tl_content WHERE id=?) ORDER BY parent, a.sorting") - ->execute($dc->id); - } - - if ($objAlias->numRows) - { - System::loadLanguageFile('tl_article'); - - while ($objAlias->next()) - { - $key = $objAlias->parent . ' (ID ' . $objAlias->pid . ')'; - $arrAlias[$key][$objAlias->id] = $objAlias->title . ' (' . ($GLOBALS['TL_LANG']['COLS'][$objAlias->inColumn] ?: $objAlias->inColumn) . ', ID ' . $objAlias->id . ')'; - } - } - - return $arrAlias; - } - - /** * Return the edit alias wizard * @@ -1431,94 +1359,6 @@ public function getElementTemplates(DataContainer $dc) } - /** - * Return the edit article teaser wizard - * - * @param DataContainer $dc - * - * @return string - */ - public function editArticle(DataContainer $dc) - { - return ($dc->value < 1) ? '' : ' value))) . '\',\'url\':this.href});return false">' . Image::getHtml('alias.svg', $GLOBALS['TL_LANG']['tl_content']['editarticle'][0]) . ''; - } - - - /** - * Get all articles and return them as array (article teaser) - * - * @param DataContainer $dc - * - * @return array - */ - public function getArticles(DataContainer $dc) - { - $arrPids = array(); - $arrArticle = array(); - $arrRoot = array(); - $intPid = $dc->activeRecord->pid; - - if (Input::get('act') == 'overrideAll') - { - $intPid = Input::get('id'); - } - - // Limit pages to the website root - $objArticle = $this->Database->prepare("SELECT pid FROM tl_article WHERE id=?") - ->limit(1) - ->execute($intPid); - - if ($objArticle->numRows) - { - $objPage = PageModel::findWithDetails($objArticle->pid); - $arrRoot = $this->Database->getChildRecords($objPage->rootId, 'tl_page'); - array_unshift($arrRoot, $objPage->rootId); - } - - unset($objArticle); - - // Limit pages to the user's pagemounts - if ($this->User->isAdmin) - { - $objArticle = $this->Database->execute("SELECT a.id, a.pid, a.title, a.inColumn, p.title AS parent FROM tl_article a LEFT JOIN tl_page p ON p.id=a.pid" . (!empty($arrRoot) ? " WHERE a.pid IN(". implode(',', array_map('intval', array_unique($arrRoot))) .")" : "") . " ORDER BY parent, a.sorting"); - } - else - { - foreach ($this->User->pagemounts as $id) - { - if (!in_array($id, $arrRoot)) - { - continue; - } - - $arrPids[] = $id; - $arrPids = array_merge($arrPids, $this->Database->getChildRecords($id, 'tl_page')); - } - - if (empty($arrPids)) - { - return $arrArticle; - } - - $objArticle = $this->Database->execute("SELECT a.id, a.pid, a.title, a.inColumn, p.title AS parent FROM tl_article a LEFT JOIN tl_page p ON p.id=a.pid WHERE a.pid IN(". implode(',', array_map('intval', array_unique($arrPids))) .") ORDER BY parent, a.sorting"); - } - - // Edit the result - if ($objArticle->numRows) - { - System::loadLanguageFile('tl_article'); - - while ($objArticle->next()) - { - $key = $objArticle->parent . ' (ID ' . $objArticle->pid . ')'; - $arrArticle[$key][$objArticle->id] = $objArticle->title . ' (' . ($GLOBALS['TL_LANG']['COLS'][$objArticle->inColumn] ?: $objArticle->inColumn) . ', ID ' . $objArticle->id . ')'; - } - } - - return $arrArticle; - } - - /** * Dynamically set the ace syntax * diff --git a/src/Resources/contao/widgets/ArticleTree.php b/src/Resources/contao/widgets/ArticleTree.php new file mode 100644 index 0000000000..3e7cfa3984 --- /dev/null +++ b/src/Resources/contao/widgets/ArticleTree.php @@ -0,0 +1,284 @@ + + */ +class ArticleTree extends \Widget implements DcaFilterInterface +{ + + /** + * Submit user input + * @var boolean + */ + protected $blnSubmitInput = true; + + /** + * Template + * @var string + */ + protected $strTemplate = 'be_widget'; + + /** + * Order ID + * @var string + */ + protected $strOrderId; + + /** + * Order name + * @var string + */ + protected $strOrderName; + + + /** + * Load the database object + * + * @param array $arrAttributes + */ + public function __construct($arrAttributes=null) + { + $this->import('Database'); + parent::__construct($arrAttributes); + + // Prepare the order field + if ($this->orderField != '') + { + $this->strOrderId = $this->orderField . str_replace($this->strField, '', $this->strId); + $this->strOrderName = $this->orderField . str_replace($this->strField, '', $this->strName); + + // Retrieve the order value + $objRow = $this->Database->prepare("SELECT {$this->orderField} FROM {$this->strTable} WHERE id=?") + ->limit(1) + ->execute($this->activeRecord->id); + + $tmp = \StringUtil::deserialize($objRow->{$this->orderField}); + $this->{$this->orderField} = (!empty($tmp) && is_array($tmp)) ? array_filter($tmp) : array(); + } + } + + + /** + * {@inheritdoc} + */ + public function getDcaFilter() + { + $arrFilters = array(); + + // Predefined node set (see #3563) + if (is_array($this->rootNodes)) + { + // Allow only those roots that are allowed in root nodes + if (!empty($GLOBALS['TL_DCA']['tl_page']['list']['sorting']['root'])) + { + $root = array_intersect(array_merge($this->rootNodes, $this->Database->getChildRecords($this->rootNodes, 'tl_article')), $GLOBALS['TL_DCA']['tl_page']['list']['sorting']['root']); + + if (empty($root)) + { + $root = $this->rootNodes; + $GLOBALS['TL_DCA']['tl_page']['list']['sorting']['breadcrumb'] = ''; // hide the breadcrumb menu + } + + $arrFilters['root'] = $this->eliminateNestedPages($root); + } + else + { + $arrFilters['root'] = $this->eliminateNestedPages($this->rootNodes); + } + } + + return $arrFilters; + } + + + /** + * Return an array if the "multiple" attribute is set + * + * @param mixed $varInput + * + * @return mixed + */ + protected function validator($varInput) + { + $this->checkValue($varInput); + + if ($this->hasErrors()) + { + return ''; + } + + // Store the order value + if ($this->orderField != '') + { + $arrNew = explode(',', \Input::post($this->strOrderName)); + + // Only proceed if the value has changed + if ($arrNew !== $this->{$this->orderField}) + { + $this->Database->prepare("UPDATE {$this->strTable} SET tstamp=?, {$this->orderField}=? WHERE id=?") + ->execute(time(), serialize($arrNew), $this->activeRecord->id); + + $this->objDca->createNewVersion = true; // see #6285 + } + } + + // Return the value as usual + if ($varInput == '') + { + if ($this->mandatory) + { + $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['mandatory'], $this->strLabel)); + } + + return ''; + } + elseif (strpos($varInput, ',') === false) + { + return $this->multiple ? array(intval($varInput)) : intval($varInput); + } + else + { + $arrValue = array_map('intval', array_filter(explode(',', $varInput))); + + return $this->multiple ? $arrValue : $arrValue[0]; + } + } + + + /** + * Check the selected value + * + * @param mixed $varInput + */ + protected function checkValue($varInput) + { + if ($varInput == '' || !is_array($this->rootNodes)) + { + return; + } + + if (strpos($varInput, ',') === false) + { + $arrIds = array(intval($varInput)); + } + else + { + $arrIds = array_map('intval', array_filter(explode(',', $varInput))); + } + + if (count(array_diff($arrIds, array_merge($this->rootNodes, $this->Database->getChildRecords($this->rootNodes, 'tl_article')))) > 0) + { + $this->addError($GLOBALS['TL_LANG']['ERR']['invalidPages']); + } + } + + + /** + * Generate the widget and return it as string + * + * @return string + */ + public function generate() + { + $arrSet = array(); + $arrValues = array(); + $blnHasOrder = ($this->orderField != '' && is_array($this->{$this->orderField})); + + if (!empty($this->varValue)) // Can be an array + { + $objArticles = \ArticleModel::findMultipleByIds((array)$this->varValue); + + if ($objArticles !== null) + { + while ($objArticles->next()) + { + $arrSet[] = $objArticles->id; + $arrValues[$objArticles->id] = \Image::getHtml($this->getPageStatusIcon($objArticles)) . ' ' . $objArticles->title . ' (' . $objArticles->alias . \Config::get('urlSuffix') . ')'; + } + } + + // Apply a custom sort order + if ($blnHasOrder) + { + $arrNew = array(); + + foreach ((array) $this->{$this->orderField} as $i) + { + if (isset($arrValues[$i])) + { + $arrNew[$i] = $arrValues[$i]; + unset($arrValues[$i]); + } + } + + if (!empty($arrValues)) + { + foreach ($arrValues as $k=>$v) + { + $arrNew[$k] = $v; + } + } + + $arrValues = $arrNew; + unset($arrNew); + } + } + + $return = '' . ($blnHasOrder ? ' + ' : '') . ' +
' . (($blnHasOrder && count($arrValues) > 1) ? ' +

' . $GLOBALS['TL_LANG']['MSC']['dragItemsHint'] . '

' : '') . ' + +

'.$GLOBALS['TL_LANG']['MSC']['changeSelection'].'

+ ' . ($blnHasOrder ? ' + ' : '') . ' +
'; + + $return = '
' . $return . '
'; + + return $return; + } +}