core/model/modx/processors/element/getnodes.class.php
<?php
/*
* This file is part of MODX Revolution.
*
* Copyright (c) MODX, LLC. All Rights Reserved.
*
* For complete copyright and license information, see the COPYRIGHT and LICENSE
* files found in the top-level directory of this distribution.
*/
/**
* Grabs all elements for element tree
*
* @param string $id (optional) Parent ID of object to grab from. Defaults to 0.
*
* @package modx
* @subpackage processors.layout.tree.element
*/
class modElementGetNodesProcessor extends modProcessor {
public $typeMap = array(
'template' => 'modTemplate',
'tv' => 'modTemplateVar',
'chunk' => 'modChunk',
'snippet' => 'modSnippet',
'plugin' => 'modPlugin',
'category' => 'modCategory',
);
public $actionMap = array();
public function checkPermissions() {
return $this->modx->hasPermission('element_tree');
}
public function getLanguageTopics() {
return array('category','element');
}
public function initialize() {
$this->setDefaultProperties(array(
'stringLiterals' => false,
'id' => 0,
));
return true;
}
public function process() {
$this->getActions();
$map = $this->getMap();
/* load correct mode */
switch ($map[0]) {
case 'type': /* if in the element, but not in a category */
$nodes = $this->getTypeNodes($map);
break;
case 'root': /* if clicking one of the root nodes */
$nodes = $this->getRootNodes($map);
break;
case 'category': /* if browsing categories */
$nodes = $this->getCategoryNodes($map);
break;
default: /* if clicking a node in a category */
$nodes = $this->getInCategoryNodes($map);
break;
}
return $this->getProperty('stringLiterals', false)
? $this->modx->toJSON($nodes)
: $this->toJSON($nodes);
}
public function getActions() {
$this->actionMap = array(
'template' => 'element/template/update',
'tv' => 'element/tv/update',
'chunk' => 'element/chunk/update',
'snippet' => 'element/snippet/update',
'plugin' => 'element/plugin/update',
);
}
public function getMap() {
/* process ID prefixes */
$id = $this->getProperty('id');
$id = empty($id) ? 0 : (substr($id,0,2) == 'n_' ? substr($id,2) : $id);
/* split the array */
return explode('_',$id);
}
/**
* Default icons for element types
* @param $elementIdentifier string Element Type
* @return string
*/
public function getNodeIcon($elementIdentifier){
$defaults = array(
'template' => 'icon icon-columns',
'chunk' => 'icon icon-th-large',
'tv' => 'icon icon-list-alt',
'snippet' => 'icon icon-code',
'plugin' => 'icon icon-cogs',
'category' => 'icon icon-folder'
);
return $this->modx->getOption('mgr_tree_icon_'.$elementIdentifier,null, $defaults[$elementIdentifier]);
}
public function getRootNodes(array $map) {
$elementType = ucfirst($map[0]);
$nodes = array();
/* Templates */
if ($this->modx->hasPermission('view_template')) {
$class = $this->modx->hasPermission('new_template') ? ' pnew' : '';
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('templates'),
'id' => 'n_type_template',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('template'),
'page' => '',
'classKey' => 'root',
'type' => 'template',
'draggable' => false,
'pseudoroot' => true,
);
}
/* TVs */
if ($this->modx->hasPermission('view_tv')) {
$class = $this->modx->hasPermission('new_tv') ? ' pnew' : '';
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('tmplvars'),
'id' => 'n_type_tv',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('tv'),
'page' => '',
'classKey' => 'root',
'type' => 'tv',
'draggable' => false,
'pseudoroot' => true,
);
}
/* Chunks */
if ($this->modx->hasPermission('view_chunk')) {
$class = $this->modx->hasPermission('new_chunk') ? ' pnew' : '';
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('chunks'),
'id' => 'n_type_chunk',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('chunk'),
'page' => '',
'classKey' => 'root',
'type' => 'chunk',
'draggable' => false,
'pseudoroot' => true,
);
}
/* Snippets */
if ($this->modx->hasPermission('view_snippet')) {
$class = $this->modx->hasPermission('new_snippet') ? ' pnew' : '';
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('snippets'),
'id' => 'n_type_snippet',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('snippet'),
'page' => '',
'classKey' => 'root',
'type' => 'snippet',
'draggable' => false,
'pseudoroot' => true,
);
}
/* Plugins */
if ($this->modx->hasPermission('view_plugin')) {
$class = $this->modx->hasPermission('new_snippet') ? ' pnew' : '';
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('plugins'),
'id' => 'n_type_plugin',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('plugin'),
'page' => '',
'classKey' => 'root',
'type' => 'plugin',
'draggable' => false,
'pseudoroot' => true,
);
}
/* Categories */
if ($this->modx->hasPermission('view_category')) {
$class = $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= ' tree-pseudoroot-node';
$nodes[] = array(
'text' => $this->modx->lexicon('categories'),
'id' => 'n_category',
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('category'),
'page' => '',
'classKey' => 'root',
'type' => 'category',
'draggable' => false,
'pseudoroot' => true,
);
}
return $nodes;
}
/**
* @param array $map
* @return array
*/
public function getCategoryNodes(array $map)
{
if (!empty($map[1])) {
/* if grabbing subcategories */
$c = $this->modx->newQuery('modCategory');
$c->where(array(
'parent' => $map[1],
));
$c->sortby($this->modx->getSelectColumns('modCategory','modCategory','',array('category')),'ASC');
} else {
/* if trying to grab all root categories */
$c = $this->modx->newQuery('modCategory');
$c->where(array(
'parent' => 0,
));
$c->sortby($this->modx->getSelectColumns('modCategory','modCategory','',array('category')),'ASC');
}
$c->select($this->modx->getSelectColumns('modCategory','modCategory'));
$c->select(array(
'COUNT('.$this->modx->getSelectColumns('modCategory','Children','',array('id')).') AS childrenCount',
));
$c->leftJoin('modCategory','Children');
$c->groupby($this->modx->getSelectColumns('modCategory','modCategory'));
/* set permissions as css classes */
$class = array('folder');
$types = array('template','tv','chunk','snippet','plugin');
foreach ($types as $type) {
if ($this->modx->hasPermission('new_'.$type)) {
$class[] = 'pnew_'.$type;
}
}
if ($this->modx->hasPermission('new_category')) $class[] = 'pnewcat';
if ($this->modx->hasPermission('edit_category')) $class[] = 'peditcat';
if ($this->modx->hasPermission('delete_category')) $class[] = 'pdelcat';
$class = implode(' ',$class);
/* get and loop through categories */
$nodes = array();
$categories = $this->modx->getCollection('modCategory',$c);
/** @var modCategory $category */
foreach ($categories as $category) {
if (!$category->checkPolicy('list')) continue;
$idNote = $this->modx->hasPermission('tree_show_element_ids') ? ' (' . $category->get('id') . ')' : '';
$nodes[] = array(
'text' => strip_tags($category->get('category')).$idNote,
'id' => 'n_category_'.$category->get('id'),
'pk' => $category->get('id'),
'data' => $category->toArray(),
'category' => $category->get('id'),
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('category'),
'page' => '',
'classKey' => 'modCategory',
'type' => 'category',
);
}
if (isset($map[1])) {
foreach (array_keys($this->actionMap) as $type) {
$nodes = array_merge($nodes, $this->getInCategoryElements([$type, $map[1]]));
}
}
return $nodes;
}
public function getInCategoryNodes(array $map) {
$nodes = array();
/* 0: type, 1: element/category 2: elID 3: catID */
$categoryId = isset($map[3]) ? $map[3] : ($map[1] == 'category' ? $map[2] : 0);
$elementIdentifier = $map[0];
$elementType = ucfirst($elementIdentifier);
$elementClassKey = $this->typeMap[$elementIdentifier];
/* first handle subcategories */
$c = $this->modx->newQuery('modCategory');
$c->select($this->modx->getSelectColumns('modCategory','modCategory'));
$c->select('COUNT(DISTINCT '.$elementClassKey.'.id) AS elementCount');
$c->select('COUNT(DISTINCT '.$this->modx->getSelectColumns('modCategory','Children','',array('id')).') AS childrenCount');
$c->leftJoin($elementClassKey,$elementClassKey,$elementClassKey.'.category = modCategory.id');
$c->leftJoin('modCategory','Children');
$c->where(array(
'parent' => $categoryId,
));
$c->groupby($this->modx->getSelectColumns('modCategory','modCategory'));
$c->sortby($this->modx->getSelectColumns('modCategory','modCategory','',array('category')),'ASC');
$categories = $this->modx->getCollection('modCategory',$c);
/* set permissions as css classes */
$class = array('folder');
$types = array('template','tv','chunk','snippet','plugin');
foreach ($types as $type) {
if ($this->modx->hasPermission('new_'.$type)) {
$class[] = 'pnew_'.$type;
}
}
if ($this->modx->hasPermission('new_category')) $class[] = 'pnewcat';
if ($this->modx->hasPermission('edit_category')) $class[] = 'peditcat';
if ($this->modx->hasPermission('delete_category')) $class[] = 'pdelcat';
$class = implode(' ',$class);
/* loop through categories */
/** @var modCategory $category */
foreach ($categories as $category) {
if (!$category->checkPolicy('list')) continue;
if ($category->get('elementCount') <= 0 && $category->get('childrenCount') <= 0) continue;
/* check subcategories recursively */
if ($category->get('childrenCount') > 0 && $category->get('elementCount') < 1) {
if ($this->subCategoriesHaveElements($category->get('id'), $elementClassKey) == false) {
continue;
}
}
$cc = ($category->get('elementCount') > 0) ? ' (' . $category->get('elementCount') . ')' : '';
$nodes[] = array(
'text' => strip_tags($category->get('category')) . $cc,
'id' => 'n_'.$map[0].'_category_'.($category->get('id') != null ? $category->get('id') : 0),
'pk' => $category->get('id'),
'category' => $category->get('id'),
'data' => $category->toArray(),
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('category'),
'classKey' => 'modCategory',
'elementType' => $elementType,
'page' => '',
'type' => $elementIdentifier,
);
}
/* all elements in category */
$c = $this->modx->newQuery($elementClassKey);
$c->where(array(
'category' => $categoryId
));
$c->sortby($elementIdentifier == 'template' ? 'templatename' : 'name','ASC');
$elements = $this->modx->getCollection($elementClassKey,$c);
/* do permission checks */
$canNewElement = $this->modx->hasPermission('new_'.$elementIdentifier);
$canEditElement = $this->modx->hasPermission('edit_'.$elementIdentifier);
$canDeleteElement = $this->modx->hasPermission('delete_'.$elementIdentifier);
$canNewCategory = $this->modx->hasPermission('new_category');
$showElementIds = $this->modx->hasPermission('tree_show_element_ids');
/* loop through elements */
/** @var modElement $element */
foreach ($elements as $element) {
if (!$element->checkPolicy('list')) continue;
$name = $elementIdentifier == 'template' ? $element->get('templatename') : $element->get('name');
$caption = $elementClassKey == 'modTemplateVar' ? $element->get('caption') : '';
$class = array();
if ($canNewElement) $class[] = 'pnew';
if ($canEditElement && $element->checkPolicy(array('save' => true, 'view' => true))) $class[] = 'pedit';
if ($canDeleteElement && $element->checkPolicy('remove')) $class[] = 'pdelete';
if ($canNewCategory) $class[] = 'pnewcat';
if ($element->get('locked')) $class[] = 'element-node-locked';
if ($elementClassKey == 'modPlugin' && $element->get('disabled')) {
$class[] = 'element-node-disabled';
}
$active = false;
if ($this->getProperty('currentElement') == $element->id && $this->getProperty('currentAction') == $this->actionMap[$map[0]]) {
$active = true;
}
$idNote = $showElementIds ? ' (' . $element->get('id') . ')' : '';
$nodes[] = array(
'text' => strip_tags($name) . $idNote,
'id' => 'n_'.$elementIdentifier.'_element_'.$element->get('id').'_'.$element->get('category'),
'pk' => $element->get('id'),
'category' => $categoryId,
'leaf' => true,
'name' => $name,
'caption' => $caption,
'cls' => implode(' ', $class),
'iconCls' => 'icon ' . ($element->get('icon') ? $element->get('icon') : ($element->get('static') ? 'icon-file-code-o' : $this->getNodeIcon($elementIdentifier))),
'page' => '?a='.$this->actionMap[$elementIdentifier].'&id='.$element->get('id'),
'type' => $elementIdentifier,
'elementType' => $elementType,
'classKey' => $elementClassKey,
'active' => !$element->get('disabled'),
'qtip' => strip_tags($element->get('description')),
'selected' => $active,
);
}
return $nodes;
}
/**
* @param array $map
* 0: type of element
* 1: parent category
* @return array
*/
public function getInCategoryElements(array $map)
{
$nodes = array();
$elementIdentifier = $map[0];
$categoryId = $map[1];
$elementType = ucfirst($elementIdentifier);
$elementClassKey = $this->typeMap[$elementIdentifier];
/* all elements in category */
$c = $this->modx->newQuery($elementClassKey);
$c->where(array(
'category' => $categoryId
));
$c->sortby($elementIdentifier == 'template' ? 'templatename' : 'name','ASC');
$elements = $this->modx->getCollection($elementClassKey,$c);
/* do permission checks */
$canNewElement = $this->modx->hasPermission('new_'.$elementIdentifier);
$canEditElement = $this->modx->hasPermission('edit_'.$elementIdentifier);
$canDeleteElement = $this->modx->hasPermission('delete_'.$elementIdentifier);
$canNewCategory = $this->modx->hasPermission('new_category');
$showElementIds = $this->modx->hasPermission('tree_show_element_ids');
/* loop through elements */
/** @var modElement $element */
foreach ($elements as $element) {
if (!$element->checkPolicy('list')) continue;
$name = $elementIdentifier == 'template' ? $element->get('templatename') : $element->get('name');
$class = array();
if ($canNewElement) $class[] = 'pnew';
if ($canEditElement && $element->checkPolicy(array('save' => true, 'view' => true))) $class[] = 'pedit';
if ($canDeleteElement && $element->checkPolicy('remove')) $class[] = 'pdelete';
if ($canNewCategory) $class[] = 'pnewcat';
if ($element->get('locked')) $class[] = 'element-node-locked';
if ($elementClassKey == 'modPlugin' && $element->get('disabled')) {
$class[] = 'element-node-disabled';
}
$idNote = $showElementIds ? ' (' . $element->get('id') . ')' : '';
$nodes[] = array(
'text' => strip_tags($name) . $idNote,
'id' => 'n_c_'.$elementIdentifier.'_element_'.$element->get('id').'_'.$element->get('category'),
'pk' => $element->get('id'),
'category' => $categoryId,
'leaf' => true,
'name' => $name,
'cls' => implode(' ', $class),
'iconCls' => 'icon ' . $this->getNodeIcon($elementIdentifier),
'page' => '?a='.$this->actionMap[$elementIdentifier].'&id='.$element->get('id'),
'type' => $elementIdentifier,
'elementType' => $elementType,
'classKey' => $elementClassKey,
'active' => !$element->get('disabled'),
'qtip' => strip_tags($element->get('description'))
);
}
return $nodes;
}
public function getTypeNodes(array $map) {
$nodes = array();
$elementType = ucfirst($map[1]);
$elementClassKey = $this->typeMap[$map[1]];
/* get elements in this type */
$c = $this->modx->newQuery('modCategory');
$c->select($this->modx->getSelectColumns('modCategory','modCategory'));
$c->select('
COUNT(DISTINCT '.$this->modx->getSelectColumns($elementClassKey,$elementClassKey,'',array('id')).') AS elementCount,
COUNT(DISTINCT '.$this->modx->getSelectColumns('modCategory','Children','',array('id')).') AS childrenCount
');
$c->leftJoin($elementClassKey,$elementClassKey,$this->modx->getSelectColumns($elementClassKey,$elementClassKey,'',array('category')).' = '.$this->modx->getSelectColumns('modCategory','modCategory','',array('id')));
$c->leftJoin('modCategory','Children');
$c->where(array(
'modCategory.parent' => 0,
));
$c->sortby($this->modx->getSelectColumns('modCategory','modCategory','',array('category')),'ASC');
$c->groupby($this->modx->getSelectColumns('modCategory','modCategory'));
$categories = $this->modx->getCollection('modCategory',$c);
/* set permissions as css classes */
$class = 'folder';
$types = array('template','tv','chunk','snippet','plugin');
foreach ($types as $type) {
if ($this->modx->hasPermission('new_'.$type)) {
$class .= ' pnew_'.$type;
}
}
$class .= $this->modx->hasPermission('new_category') ? ' pnewcat' : '';
$class .= $this->modx->hasPermission('edit_category') ? ' peditcat' : '';
$class .= $this->modx->hasPermission('delete_category') ? ' pdelcat' : '';
/* loop through categories with elements in this type */
/** @var modCategory $category */
foreach ($categories as $category) {
if (!$category->checkPolicy('list')) continue;
$elCount = (int)$category->get('elementCount');
$catCount = (int)$category->get('childrenCount');
if ($elCount < 1 && $catCount < 1 && $category->get('id') != 0) {
continue;
}
if ($catCount > 0 && $elCount < 1) {
if ($this->subCategoriesHaveElements($category->get('id'), $elementClassKey) == false) {
continue;
}
}
$cc = $elCount > 0 ? ' ('.$elCount.')' : '';
$nodes[] = array(
'text' => strip_tags($category->get('category')).$cc,
'id' => 'n_'.$map[1].'_category_'.($category->get('id') != null ? $category->get('id') : 0),
'pk' => $category->get('id'),
'category' => $category->get('id'),
'data' => $category->toArray(),
'leaf' => false,
'cls' => $class,
'iconCls' => $this->getNodeIcon('category'),
'page' => '',
'classKey' => 'modCategory',
'elementType' => $elementType,
'type' => $map[1],
);
unset($elCount,$childCats);
}
/* now add elements in this type without a category */
$c = $this->modx->newQuery($elementClassKey);
$c->where(array(
'category' => 0,
));
$c->sortby($elementClassKey == 'modTemplate' ? 'templatename' : 'name','ASC');
$elements = $this->modx->getCollection($elementClassKey,$c);
/* do permission checks */
$canNewCategory = $this->modx->hasPermission('new_category');
$canEditElement = $this->modx->hasPermission('edit_'.$map[1]);
$canDeleteElement = $this->modx->hasPermission('delete_'.$map[1]);
$canNewElement = $this->modx->hasPermission('new_'.$map[1]);
$showElementIds = $this->modx->hasPermission('tree_show_element_ids');
/* loop through elements */
/** @var modElement $element */
foreach ($elements as $element) {
if (!$element->checkPolicy('list')) continue;
/* handle templatename case */
$name = $elementClassKey == 'modTemplate' ? $element->get('templatename') : $element->get('name');
$caption = $elementClassKey == 'modTemplateVar' ? $element->get('caption') : '';
$class = array();
if ($canNewElement) $class[] = 'pnew';
if ($canEditElement && $element->checkPolicy(array('save' => true,'view' => true))) $class[] = 'pedit';
if ($canDeleteElement && $element->checkPolicy('remove')) $class[] = 'pdelete';
if ($canNewCategory) $class[] = 'pnewcat';
if ($element->get('locked')) $class[] = 'element-node-locked';
if ($elementClassKey == 'modPlugin' && $element->get('disabled')) {
$class[] = 'element-node-disabled';
}
if (!empty($scriptProperties['currentElement']) && $scriptProperties['currentElement'] == $element->get('id') && $scriptProperties['currentAction'] == $this->actionMap[$map[1]]) {
$class[] = 'active-node';
}
$active = false;
if ($this->getProperty('currentElement') == $element->id && $this->getProperty('currentAction') == $this->actionMap[$map[1]]) {
$active = true;
}
$idNote = $showElementIds ? ' (' . $element->get('id') . ')' : '';
$nodes[] = array(
'text' => strip_tags($name) . $idNote,
'id' => 'n_'.$map[1].'_element_'.$element->get('id').'_0',
'pk' => $element->get('id'),
'category' => 0,
'leaf' => true,
'name' => $name,
'caption' => $caption,
'cls' => implode(' ', $class),
'iconCls' => 'icon ' . ($element->get('icon') ? $element->get('icon') : ($element->get('static') ? 'icon-file-code-o' : $this->getNodeIcon($map[1]))),
'page' => '?a='.$this->actionMap[$map[1]].'&id='.$element->get('id'),
'type' => $map[1],
'elementType' => $elementType,
'classKey' => $elementClassKey,
'active' => !$element->get('disabled'),
'qtip' => strip_tags($element->get('description')),
'selected' => $active,
);
}
return $nodes;
}
protected function subCategoriesHaveElements($categoryId, $elementClassKey) {
$return = false;
$categories = $this->modx->getCollection('modCategory', array(
'parent' => $categoryId
));
foreach ($categories as $category) {
$c = $this->modx->newQuery('modCategory');
$c->select($this->modx->getSelectColumns('modCategory','modCategory'));
$c->select('COUNT(DISTINCT '.$elementClassKey.'.id) AS elementCount');
$c->select('COUNT(DISTINCT '.$this->modx->getSelectColumns('modCategory','Children','',array('id')).') AS childrenCount');
$c->leftJoin($elementClassKey,$elementClassKey,$elementClassKey.'.category = modCategory.id');
$c->leftJoin('modCategory', 'Children');
$c->where(array(
'id' => $category->get('id'),
));
$c->groupby($this->modx->getSelectColumns('modCategory','modCategory'));
$subCategory = $this->modx->getObject('modCategory',$c);
if ($subCategory->get('elementCount') > 0) {
$return = true;
}
if ($return == false && $subCategory->get('childrenCount') > 0) {
$return = $this->subCategoriesHaveElements($subCategory->get('id'), $elementClassKey);
}
}
return $return;
}
}
return 'modElementGetNodesProcessor';