-{foreach from=$categoryNodeList item=category}
- {if !$maximumNestingLevel|isset || $maximumNestingLevel == -2 || $categoryNodeList->getDepth() <= $maximumNestingLevel}
+{foreach from=$categoryNodeList item='category'}
+ {if !$maximumNestingLevel|isset || $maximumNestingLevel == -1 || $categoryNodeList->getDepth() <= $maximumNestingLevel}
<option value="{$category->categoryID}"{if $categoryID|isset && $categoryID == $category->categoryID} selected="selected"{/if}>{section name=i loop=$categoryNodeList->getDepth()} {/section}{$category->getTitle()}</option>
{/if}
{/foreach}
\ No newline at end of file
<fieldset>
<legend>{lang}wcf.global.form.data{/lang}</legend>
- {if $objectType->getProcessor()->getMaximumNestingLevel() && $categoryNodeList|count}
+ {if $categoryNodeList->hasChildren() && $objectType->getProcessor()->getMaximumNestingLevel()}
<dl{if $errorField == 'parentCategoryID'} class="formError"{/if}>
<dt><label for="parentCategoryID">{@$objectType->getProcessor()->getLanguageVariable('parentCategoryID')}</label></dt>
<dd>
<select id="parentCategoryID" name="parentCategoryID">
<option value="0"></option>
- {include file='categoryOptionList' categoryID=$parentCategoryID maximumNestingLevel=$objectType->getProcessor()->getMaximumNestingLevel() - 1}
+ {include file='categoryOptionList' categoryID=$parentCategoryID maximumNestingLevel=$objectType->getProcessor()->getMaximumNestingLevel()}
</select>
{if $errorField == 'parentCategoryID'}
<small class="innerError">
{include file='header'}
-{if $categoryNodeList|count}
+{if $categoryNodeList->hasChildren()}
<script type="text/javascript">
//<![CDATA[
$(function() {
- {if $collapsibleObjectTypeID && $categoryNodeList|count > 1}
+ {if $collapsibleObjectTypeID}
new WCF.ACP.Category.Collapsible('wcf\\data\\category\\CategoryAction', {@$collapsibleObjectTypeID});
{/if}
-
+
{if $objectType->getProcessor()->canDeleteCategory()}
new WCF.ACP.Category.Delete('wcf\\data\\category\\CategoryAction', $('.jsCategory'));
{/if}
{if $objectType->getProcessor()->canEditCategory()}
new WCF.Action.Toggle('wcf\\data\\category\\CategoryAction', '.jsCategory', '> .sortableNodeLabel > .buttons > .jsToggleButton');
- {if $categoryNodeList|count > 1}
- var sortableNodes = $('.sortableNode');
- sortableNodes.each(function(index, node) {
- $(node).wcfIdentify();
- });
-
- new WCF.Sortable.List('categoryList', 'wcf\\data\\category\\CategoryAction', 0{if $objectType->getProcessor()->getMaximumNestingLevel() != -1}, {
- /**
- * Updates the sortable nodes after a sorting is started with
- * regard to their possibility to have child the currently sorted
- * category as a child category.
- */
- start: function(event, ui) {
- var sortedListItem = $(ui.item);
- var itemNestingLevel = sortedListItem.find('.sortableList:has(.sortableNode)').length;
+ var sortableNodes = $('.sortableNode');
+ sortableNodes.each(function(index, node) {
+ $(node).wcfIdentify();
+ });
+
+ new WCF.Sortable.List('categoryList', 'wcf\\data\\category\\CategoryAction', 0{if $objectType->getProcessor()->getMaximumNestingLevel() != -1}, {
+ /**
+ * Updates the sortable nodes after a sorting is started with
+ * regard to their possibility to have child the currently sorted
+ * category as a child category.
+ */
+ start: function(event, ui) {
+ var sortedListItem = $(ui.item);
+ var itemNestingLevel = sortedListItem.find('.sortableList:has(.sortableNode)').length;
+
+ sortableNodes.each(function(index, node) {
+ node = $(node);
- sortableNodes.each(function(index, node) {
- node = $(node);
-
- if (node.attr('id') != sortedListItem.attr('id')) {
- if (node.parents('.sortableList').length + itemNestingLevel >= {@$objectType->getProcessor()->getMaximumNestingLevel() + 1}) {
- node.addClass('sortableNoNesting');
- }
- else if (node.hasClass('sortableNoNesting')) {
- node.removeClass('sortableNoNesting');
- }
- }
- });
- },
- /**
- * Updates the sortable nodes after a sorting is completed with
- * regard to their possibility to have child categories.
- */
- stop: function(event, ui) {
- sortableNodes.each(function(index, node) {
- node = $(node);
-
- if (node.parents('.sortableList').length == {@$objectType->getProcessor()->getMaximumNestingLevel() + 1}) {
+ if (node.attr('id') != sortedListItem.attr('id')) {
+ if (node.parents('.sortableList').length + itemNestingLevel >= {@$objectType->getProcessor()->getMaximumNestingLevel() + 1}) {
node.addClass('sortableNoNesting');
}
else if (node.hasClass('sortableNoNesting')) {
node.removeClass('sortableNoNesting');
}
- });
- }
- }{/if});
- {/if}
+ }
+ });
+ },
+
+ /**
+ * Updates the sortable nodes after a sorting is completed with
+ * regard to their possibility to have child categories.
+ */
+ stop: function(event, ui) {
+ sortableNodes.each(function(index, node) {
+ node = $(node);
+
+ if (node.parents('.sortableList').length == {@$objectType->getProcessor()->getMaximumNestingLevel() + 1}) {
+ node.addClass('sortableNoNesting');
+ }
+ else if (node.hasClass('sortableNoNesting')) {
+ node.removeClass('sortableNoNesting');
+ }
+ });
+ }
+ }{/if});
{/if}
});
//]]>
<li><a href="{link controller=$addController application=$objectType->getProcessor()->getApplication()}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{@$objectType->getProcessor()->getLanguageVariable('add')}</span></a></li>
{/if}
- {event name='contentNavigationButtonsTop'}
+ {event name='contentNavigationButtons'}
{/content}
</ul>
</nav>
{/hascontent}
</div>
-{if $categoryNodeList|count}
- <section id="categoryList" class="container containerPadding marginTop{if $objectType->getProcessor()->canEditCategory() && $categoryNodeList|count > 1} sortableListContainer{/if}">
+{hascontent}
+ <section id="categoryList" class="container containerPadding marginTop{if $objectType->getProcessor()->canEditCategory()} sortableListContainer{/if}">
<ol class="categoryList sortableList" data-object-id="0">
- {assign var=oldDepth value=0}
- {foreach from=$categoryNodeList item=category}
- {section name=i loop=$oldDepth-$categoryNodeList->getDepth()}</ol></li>{/section}
-
- <li class="{if $objectType->getProcessor()->canEditCategory() && $categoryNodeList|count > 1}sortableNode {if $categoryNodeList->getDepth() == $objectType->getProcessor()->getMaximumNestingLevel()}sortableNoNesting {/if}{/if}jsCategory" data-object-id="{@$category->categoryID}"{if $collapsedCategoryIDs|is_array} data-is-open="{if $collapsedCategoryIDs[$category->categoryID]|isset}0{else}1{/if}"{/if}>
- <span class="sortableNodeLabel">
- <span class="title">
- {if $objectType->getProcessor()->canEditCategory()}
- <a href="{link controller=$editController application=$objectType->getProcessor()->getApplication() id=$category->categoryID title=$category->getTitle()}{/link}">{$category->getTitle()}</a>
- {else}
- {$category->getTitle()}
- {/if}
- </span>
-
- <span class="statusDisplay buttons">
- {if $objectType->getProcessor()->canEditCategory()}
- <a href="{link controller=$editController application=$objectType->getProcessor()->getApplication() id=$category->categoryID title=$category->getTitle()}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
- {/if}
-
- {if $objectType->getProcessor()->canDeleteCategory()}
- <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$category->categoryID}" data-confirm-message="{@$objectType->getProcessor()->getLanguageVariable('delete.sure')}"></span>
- {/if}
-
- {if $objectType->getProcessor()->canEditCategory()}
- <span class="icon icon16 icon-{if !$category->isDisabled}circle-blank{else}off{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if !$category->isDisabled}disable{else}enable{/if}{/lang}" data-object-id="{@$category->categoryID}"></span>
- {/if}
+ {content}
+ {assign var=oldDepth value=0}
+ {foreach from=$categoryNodeList item='category'}
+ {section name=i loop=$oldDepth-$categoryNodeList->getDepth()}</ol></li>{/section}
+
+ <li class="{if $objectType->getProcessor()->canEditCategory()}sortableNode {if $categoryNodeList->getDepth() == $objectType->getProcessor()->getMaximumNestingLevel()}sortableNoNesting {/if}{/if}jsCategory" data-object-id="{@$category->categoryID}"{if $collapsedCategoryIDs|is_array} data-is-open="{if $collapsedCategoryIDs[$category->categoryID]|isset}0{else}1{/if}"{/if}>
+ <span class="sortableNodeLabel">
+ <span class="title">
+ {if $objectType->getProcessor()->canEditCategory()}
+ <a href="{link controller=$editController application=$objectType->getProcessor()->getApplication() id=$category->categoryID title=$category->getTitle()}{/link}">{$category->getTitle()}</a>
+ {else}
+ {$category->getTitle()}
+ {/if}
+ </span>
- {event name='itemButtons'}
+ <span class="statusDisplay buttons">
+ {if $objectType->getProcessor()->canEditCategory()}
+ <a href="{link controller=$editController application=$objectType->getProcessor()->getApplication() id=$category->categoryID title=$category->getTitle()}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
+ {/if}
+
+ {if $objectType->getProcessor()->canDeleteCategory()}
+ <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$category->categoryID}" data-confirm-message="{@$objectType->getProcessor()->getLanguageVariable('delete.sure')}"></span>
+ {/if}
+
+ {if $objectType->getProcessor()->canEditCategory()}
+ <span class="icon icon16 icon-{if !$category->isDisabled}circle-blank{else}off{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if !$category->isDisabled}disable{else}enable{/if}{/lang}" data-object-id="{@$category->categoryID}"></span>
+ {/if}
+
+ {event name='itemButtons'}
+ </span>
</span>
- </span>
-
- <ol class="categoryList sortableList" data-object-id="{@$category->categoryID}">
- {if !$categoryNodeList->current()->hasChildren()}
- </ol></li>
- {/if}
- {assign var=oldDepth value=$categoryNodeList->getDepth()}
- {/foreach}
- {section name=i loop=$oldDepth}</ol></li>{/section}
+
+ <ol class="categoryList sortableList" data-object-id="{@$category->categoryID}">
+ {if !$categoryNodeList->current()->hasChildren()}
+ </ol></li>
+ {/if}
+ {assign var=oldDepth value=$categoryNodeList->getDepth()}
+ {/foreach}
+ {section name=i loop=$oldDepth}</ol></li>{/section}
+ {/content}
</ol>
-
- {if $objectType->getProcessor()->canEditCategory() && $categoryNodeList|count > 1}
- <div class="formSubmit">
- <button class="button buttonPrimary" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
- </div>
- {/if}
</section>
- <div class="contentNavigation">
- {hascontent}
- <nav>
- <ul>
- {content}
- {if $objectType->getProcessor()->canAddCategory()}
- <li><a href="{link controller=$addController application=$objectType->getProcessor()->getApplication()}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{@$objectType->getProcessor()->getLanguageVariable('add')}</span></a></li>
- {/if}
-
- {event name='contentNavigationButtonsBottom'}
- {/content}
- </ul>
- </nav>
- {/hascontent}
+ <div class="formSubmit">
+ <button class="button" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
</div>
-{else}
+{hascontentelse}
<p class="info">{@$objectType->getProcessor()->getLanguageVariable('noneAvailable')}</p>
-{/if}
+{/hascontent}
{include file='footer'}
\ No newline at end of file
-{foreach from=$categoryNodeList item=category}
- {if !$maximumNestingLevel|isset || $maximumNestingLevel == -2 || $categoryNodeList->getDepth() <= $maximumNestingLevel}
+{foreach from=$categoryNodeList item='category'}
+ {if !$maximumNestingLevel|isset || $maximumNestingLevel == -1 || $categoryNodeList->getDepth() <= $maximumNestingLevel}
<option value="{$category->categoryID}"{if $categoryID|isset && $categoryID == $category->categoryID} selected="selected"{/if}>{section name=i loop=$categoryNodeList->getDepth()} {/section}{$category->getTitle()}</option>
{/if}
{/foreach}
\ No newline at end of file
namespace wcf\acp\form;
use wcf\data\category\CategoryAction;
use wcf\data\category\CategoryEditor;
-use wcf\data\category\CategoryNodeList;
+use wcf\data\category\UncachedCategoryNodeTree;
use wcf\form\AbstractForm;
use wcf\system\acl\ACLHandler;
use wcf\system\category\CategoryHandler;
public $additionalData = array();
/**
- * list with the category nodes
- * @var wcf\data\category\CategoryNodeList
+ * tree with the category nodes
+ * @var wcf\data\category\CategoryNodeTree
*/
- public $categoryNodeList = null;
+ public $categoryNodeTree = null;
/**
* indicates if the category is disabled
'action' => 'add',
'addController' => $this->addController,
'additionalData' => $this->additionalData,
- 'categoryNodeList' => $this->categoryNodeList,
+ 'categoryNodeList' => $this->categoryNodeTree->getIterator(),
'editController' => $this->editController,
'isDisabled' => $this->isDisabled,
'listController' => $this->listController,
* Reads the categories.
*/
protected function readCategories() {
- $this->categoryNodeList = new CategoryNodeList($this->objectType->objectType, 0, true);
+ $this->categoryNodeTree = new UncachedCategoryNodeTree($this->objectType->objectType, 0, true);
}
/**
* @see wcf\page\IPage::readData()
*/
- public function readData() {
+ public function readData() {
$this->objectType = CategoryHandler::getInstance()->getObjectTypeByName($this->objectTypeName);
if ($this->objectType === null) {
throw new SystemException("Unknown category object type with name '".$this->objectTypeName."'");
}
I18nHandler::getInstance()->register('title');
- $this->readCategories();
-
parent::readData();
+
+ $this->readCategories();
}
/**
return;
}
- if (CategoryHandler::getInstance()->getCategory($this->parentCategoryID) === null) {
- throw new UserInputException('parentCategoryID', 'invalid');
+ $category = CategoryHandler::getInstance()->getCategory($this->parentCategoryID);
+ if ($category === null) {
+ throw new UserInputException('parentCategoryID', 'notValid');
}
if ($this->objectType->getProcessor()->getMaximumNestingLevel() != -1) {
- foreach ($this->categoryNodeList as $category) {
- if ($category->categoryID == $this->parentCategoryID) {
- if ($this->categoryNodeList->getDepth() > $this->objectType->getProcessor()->getMaximumNestingLevel() - 1) {
- throw new UserInputException('parentCategoryID', 'invalid');
- }
-
- break;
- }
+ if (count($category->getParentCategories()) > $this->objectType->getProcessor()->getMaximumNestingLevel()) {
+ throw new UserInputException('parentCategoryID', 'notValid');
}
}
}
namespace wcf\acp\form;
use wcf\data\category\Category;
use wcf\data\category\CategoryAction;
-use wcf\data\category\CategoryNodeList;
+use wcf\data\category\CategoryNodeTree;
use wcf\form\AbstractForm;
use wcf\system\acl\ACLHandler;
use wcf\system\category\CategoryHandler;
* @see wcf\acp\form\AbstractCategoryAddForm::readCategories()
*/
protected function readCategories() {
- $this->categoryNodeList = new CategoryNodeList($this->objectType->objectType, 0, true, array($this->category->categoryID));
+ $this->categoryNodeTree = new CategoryNodeTree($this->objectType->objectType, 0, true, array($this->category->categoryID));
}
/**
parent::validateParentCategory();
// check if new parent category is no child category of the category
- $childCategories = CategoryHandler::getInstance()->getChildCategories($this->category);
+ $childCategories = CategoryHandler::getInstance()->getChildCategories($this->categoryID, $this->objectType->objectTypeID);
if (isset($childCategories[$this->parentCategoryID])) {
throw new UserInputException('parentCategoryID', 'invalid');
}
<?php
namespace wcf\acp\page;
-use wcf\data\category\CategoryNodeList;
+use wcf\data\category\CategoryNodeTree;
use wcf\page\AbstractPage;
use wcf\system\category\CategoryHandler;
use wcf\system\exception\PermissionDeniedException;
public $addController = '';
/**
- * category node list
- * @var wcf\data\category\CategoryNodeList
+ * category node tree
+ * @var wcf\data\category\CategoryNodeTree
*/
- public $categoryNodeList = null;
+ public $categoryNodeTree = null;
/**
* ids of collapsed categories
WCF::getTPL()->assign(array(
'addController' => $this->addController,
- 'categoryNodeList' => $this->categoryNodeList,
+ 'categoryNodeList' => $this->categoryNodeTree->getIterator(),
'collapsedCategoryIDs' => $this->collapsedCategoryIDs,
'collapsibleObjectTypeID' => $this->collapsibleObjectTypeID,
'editController' => $this->editController,
* Reads the categories.
*/
protected function readCategories() {
- $this->categoryNodeList = new CategoryNodeList($this->objectType->objectType, 0, true);
+ $this->categoryNodeTree = new CategoryNodeTree($this->objectType->objectType, 0, true);
}
/**
--- /dev/null
+<?php
+namespace wcf\data\category;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\system\category\CategoryHandler;
+
+/**
+ * Abstract implementation of a decorated category.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.category
+ * @category Community Framework
+ */
+abstract class AbstractDecoratedCategory extends DatabaseObjectDecorator {
+ /**
+ * list of all child categories of this category
+ * @var array<wcf\data\category\Category>
+ */
+ protected $childCategories = null;
+
+ /**
+ * list of all parent category generations of this category
+ * @var array<wcf\data\category\AbstractDecoratedCategory>
+ */
+ protected $parentCategories = null;
+
+ /**
+ * parent category of this category
+ * @var wcf\data\category\AbstractDecoratedCategory
+ */
+ protected $parentCategory = null;
+
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\category\Category';
+
+ /**
+ * @see wcf\data\category\Category::getChildCategories()
+ */
+ public function getChildCategories() {
+ if ($this->childCategories === null) {
+ $this->childCategories = array();
+ foreach ($this->getDecoratedObject()->getChildCategories() as $category) {
+ $this->childCategories[$category->categoryID] = new static($category);
+ }
+ }
+
+ return $this->childCategories;
+ }
+
+ /**
+ * @see wcf\data\category\Category::getParentCategories()
+ */
+ public function getParentCategories() {
+ if ($this->parentCategories === null) {
+ $this->parentCategories = array();
+ foreach ($this->getDecoratedObject()->getParentCategories() as $category) {
+ $this->parentCategories[$category->categoryID] = new static($category);
+ }
+ }
+
+ return $this->parentCategories;
+ }
+
+ /**
+ * @see wcf\data\category\Category::getParentCategory()
+ */
+ public function getParentCategory() {
+ if ($this->parentCategoryID && $this->parentCategory === null) {
+ $this->parentCategory = new static($this->getDecoratedObject()->getParentCategory());
+ }
+
+ return $this->parentCategory;
+ }
+
+ /**
+ * Returns the decorated category with the given id or null if no such
+ * category exists.
+ *
+ * @param integer $categoryID
+ * @return wcf\data\category\AbstractDecoratedCategory
+ */
+ public static function getCategory($categoryID) {
+ $category = CategoryHandler::getInstance()->getCategory($categoryID);
+ if ($category) {
+ return new static($category);
+ }
+
+ return null;
+ }
+}
<?php
namespace wcf\data\category;
+use wcf\data\IPermissionObject;
use wcf\data\ProcessibleDatabaseObject;
use wcf\system\category\CategoryHandler;
+use wcf\system\category\CategoryPermissionHandler;
+use wcf\system\exception\PermissionDeniedException;
use wcf\system\request\IRouteController;
use wcf\system\WCF;
* @subpackage data.category
* @category Community Framework
*/
-class Category extends ProcessibleDatabaseObject implements IRouteController {
+class Category extends ProcessibleDatabaseObject implements IPermissionObject, IRouteController {
+ /**
+ * list of all child categories of this category
+ * @var array<wcf\data\category\Category>
+ */
+ protected $childCategories = null;
+
/**
* list of all parent category generations of this category
* @var array<wcf\data\category\Category>
*/
protected $parentCategory = null;
+ /**
+ * acl permissions for the active user of this category
+ * @var array<boolean>
+ */
+ protected $permissions = null;
+
/**
* @see wcf\data\DatabaseObject::$databaseTableIndexName
*/
return $value;
}
+ /**
+ * @see wcf\data\IPermissionObject::checkPermissions()
+ */
+ public function checkPermissions(array $permissions) {
+ foreach ($permissions as $permission) {
+ if (!$this->getPermission($permission)) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
/**
* Returns the category object type of the category.
*
return CategoryHandler::getInstance()->getObjectType($this->objectTypeID);
}
+ /**
+ * Returns the child categories of this category.
+ *
+ * @return array<wcf\data\category\Category>
+ */
+ public function getChildCategories() {
+ if ($this->childCategories === null) {
+ $this->childCategories = CategoryHandler::getInstance()->getChildCategories($this->categoryID);
+ }
+
+ return $this->childCategories;
+ }
+
/**
* Returns the parent category of this category.
*
return $this->parentCategories;
}
+ /**
+ * @see wcf\data\IPermissionObject::getPermission()
+ */
+ public function getPermission($permission) {
+ if ($this->permissions === null) {
+ $this->permissions = CategoryPermissionHandler::getInstance()->getPermissions($this);
+ }
+
+ if (isset($this->permissions[$permission])) {
+ return $this->permissions[$permission];
+ }
+
+ return false;
+ }
+
/**
* @see wcf\data\ITitledObject::getTitle()
*/
<?php
namespace wcf\data\category;
-use wcf\data\DatabaseObject;
use wcf\data\DatabaseObjectDecorator;
-use wcf\system\category\CategoryHandler;
/**
* Represents a category node.
* child category nodes
* @var array<wcf\data\category\CategoryNode>
*/
- protected $childCategories = array();
+ protected $children = array();
/**
- * indicates if disabled categories are included
+ * current iterator key
* @var integer
*/
- protected $includeDisabledCategories = false;
-
- /**
- * list of object type category ids of excluded categories
- * @var array<integer>
- */
- protected $excludedCategoryIDs = false;
+ protected $index = 0;
/**
* @see wcf\data\DatabaseObjectDecorator::$baseClass
protected static $baseClass = 'wcf\data\category\Category';
/**
- * @see wcf\data\DatabaseObjectDecorator::__construct()
+ * Adds the given category node as child node.
+ *
+ * @param wcf\data\category\CategoryNode $categoryNode
*/
- public function __construct(DatabaseObject $object, $includeDisabledCategories = false, array $excludedCategoryIDs = array()) {
- parent::__construct($object);
-
- $this->includeDisabledCategories = $includeDisabledCategories;
- $this->excludedCategoryIDs = $excludedCategoryIDs;
-
- $className = get_called_class();
- foreach (CategoryHandler::getInstance()->getChildCategories($this->getDecoratedObject()) as $category) {
- if ($this->fulfillsConditions($category)) {
- $this->childCategories[] = new $className($category, $includeDisabledCategories, $excludedCategoryIDs);
- }
- }
+ public function addChild(CategoryNode $categoryNode) {
+ $this->children[] = $categoryNode;
}
/**
* @see \Countable::count()
*/
public function count() {
- return count($this->childCategories);
+ return count($this->children);
}
/**
* @see \Iterator::current()
*/
public function current() {
- return $this->childCategories[$this->index];
- }
-
- /**
- * Returns true if the given category fulfills all needed conditions to
- * be included in the list.
- *
- * @param wcf\data\DatabaseObject $category
- * @return boolean
- */
- protected function fulfillsConditions(DatabaseObject $category) {
- return !in_array($category->categoryID, $this->excludedCategoryIDs) && ($this->includeDisabledCategories || !$category->isDisabled);
+ return $this->children[$this->index];
}
/**
* @see \RecursiveIterator::getChildren()
*/
public function getChildren() {
- return $this->childCategories[$this->index];
+ return $this->children[$this->index];
}
/**
* @see \RecursiveIterator::getChildren()
*/
public function hasChildren() {
- return !empty($this->childCategories);
+ return !empty($this->children);
}
/**
* @see \Iterator::valid()
*/
public function valid() {
- return isset($this->childCategories[$this->index]);
+ return isset($this->children[$this->index]);
}
}
+++ /dev/null
-<?php
-namespace wcf\data\category;
-use wcf\system\category\CategoryHandler;
-use wcf\system\exception\SystemException;
-
-/**
- * Represents a category node list.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2012 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package com.woltlab.wcf
- * @subpackage data.category
- * @category Community Framework
- */
-class CategoryNodeList extends \RecursiveIteratorIterator implements \Countable {
- /**
- * number of (real) category nodes in this list
- * @var integer
- */
- protected $count = null;
-
- /**
- * name of the category node class
- * @var string
- */
- protected $nodeClassName = '';
-
- /**
- * id of the parent category
- * @var integer
- */
- protected $parentCategoryID = 0;
-
- /**
- * Creates a new CategoryNodeList instance.
- *
- * @param string $objectType
- * @param integer $parentCategoryID
- * @param boolean $includeDisabledCategories
- * @param array<integer> $excludedCategoryIDs
- */
- public function __construct($objectType, $parentCategoryID = 0, $includeDisabledCategories = false, array $excludedCategoryIDs = array()) {
- if (empty($this->nodeClassName)) {
- $this->nodeClassName = str_replace('List', '', get_class($this));
- if (!class_exists($this->nodeClassName)) {
- throw new SystemException("Unknown category node class '".$this->nodeClassName."'.");
- }
- }
-
- $this->parentCategoryID = $parentCategoryID;
-
- // get parent category
- if (!$this->parentCategoryID) {
- // empty node
- $parentCategory = new Category(null, array(
- 'categoryID' => 0,
- 'objectTypeID' => CategoryHandler::getInstance()->getObjectTypeByName($objectType)->objectTypeID
- ));
- }
- else {
- $parentCategory = CategoryHandler::getInstance()->getCategory($this->parentCategoryID);
- if ($parentCategory === null) {
- throw new SystemException("There is no category with id '".$this->parentCategoryID."'");
- }
- }
-
- parent::__construct(new $this->nodeClassName($parentCategory, $includeDisabledCategories, $excludedCategoryIDs), \RecursiveIteratorIterator::SELF_FIRST);
- }
-
- /**
- * @see \Countable::count()
- */
- public function count() {
- if ($this->count === null) {
- $this->count = 0;
- foreach ($this as $categoryNode) {
- $this->count++;
- }
- }
-
- return $this->count;
- }
-}
--- /dev/null
+<?php
+namespace wcf\data\category;
+use wcf\system\category\CategoryHandler;
+use wcf\system\exception\SystemException;
+
+/**
+ * Represents a tree of category nodes.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.category
+ * @category Community Framework
+ */
+class CategoryNodeTree implements \IteratorAggregate {
+ /**
+ * name of the category node class
+ * @var string
+ */
+ protected $nodeClassName = 'wcf\data\category\CategoryNode';
+
+ /**
+ * id of the parent category
+ * @var integer
+ */
+ protected $parentCategoryID = 0;
+
+ /**
+ * parent category node
+ * @var wcf\data\category\CategoryNode
+ */
+ protected $parentNode = null;
+
+ /**
+ * Creates a new instance of CategoryNodeTree.
+ *
+ * @param string $objectType
+ * @param integer $parentCategoryID
+ * @param boolean $includeDisabledCategories
+ * @param array<integer> $excludedCategoryIDs
+ */
+ public function __construct($objectType, $parentCategoryID = 0, $includeDisabledCategories = false, array $excludedCategoryIDs = array()) {
+ $this->objectType = $objectType;
+ $this->parentCategoryID = $parentCategoryID;
+ $this->includeDisabledCategories = $includeDisabledCategories;
+ $this->excludedCategoryIDs = $excludedCategoryIDs;
+
+ // validate category object type
+ if (CategoryHandler::getInstance()->getObjectTypeByName($this->objectType) === null) {
+ throw new SystemException("Unknown category object type '".$this->objectType."'");
+ }
+ }
+
+ /**
+ * Builds the category node tree.
+ */
+ protected function buildTree() {
+ $this->parentNode = $this->getNode($this->parentCategoryID);
+ $this->buildTreeLevel($this->parentNode);
+ }
+
+ /**
+ * Builds a certain level of the tree.
+ *
+ * @param wcf\data\category\CategoryNode $parentNode
+ */
+ protected function buildTreeLevel(CategoryNode $parentNode) {
+ foreach ($this->getChildCategories($parentNode) as $childCategory) {
+ $childNode = $this->getNode($childCategory->categoryID);
+
+ if ($this->isIncluded($childNode)) {
+ $parentNode->addChild($childNode);
+
+ // build next level
+ $this->buildTreeLevel($childNode);
+ }
+ }
+ }
+
+ /**
+ * Returns the category with the given id.
+ *
+ * @param integer $categoryID
+ * @return wcf\data\category\Category
+ */
+ protected function getCategory($categoryID) {
+ return CategoryHandler::getInstance()->getCategory($categoryID);
+ }
+
+ /**
+ * Returns the child categories of the given category node.
+ *
+ * @param wcf\data\category\CategoryNode $parentNode
+ * @return array<wcf\data\category\Category>
+ */
+ protected function getChildCategories(CategoryNode $parentNode) {
+ return CategoryHandler::getInstance()->getChildCategories($parentNode->categoryID, $parentNode->objectTypeID);
+ }
+
+ /**
+ * @see \IteratorAggregate::getIterator()
+ */
+ public function getIterator() {
+ if ($this->parentNode === null) {
+ $this->buildTree();
+ }
+
+ return new \RecursiveIteratorIterator($this->parentNode, \RecursiveIteratorIterator::SELF_FIRST);
+ }
+
+ /**
+ * Returns the category node for the category with the given id.
+ *
+ * @param integer $categoryID
+ * @return wcf\data\category\CategoryNode
+ */
+ protected function getNode($categoryID) {
+ if (!$categoryID) {
+ $category = new Category(null, array(
+ 'categoryID' => 0,
+ 'objectTypeID' => CategoryHandler::getInstance()->getObjectTypeByName($this->objectType)->objectTypeID
+ ));
+ }
+ else {
+ $category = $this->getCategory($categoryID);
+ }
+
+ // decorate category if necessary
+ $decoratorClassName = call_user_func(array($this->nodeClassName, 'getBaseClass'));
+ if ($decoratorClassName != 'wcf\data\category\Category') {
+ $category = new $decoratorClassName($category);
+ }
+
+ return new $this->nodeClassName($category);
+ }
+
+ /**
+ * Returns true if the given category node fulfils all relevant conditions
+ * to be included in this tree.
+ *
+ * @param wcf\data\category\CategoryNode $categoryNode
+ * @return boolean
+ */
+ protected function isIncluded(CategoryNode $categoryNode) {
+ return (!$categoryNode->isDisabled || $this->includeDisabledCategories) && !in_array($categoryNode->categoryID, $this->excludedCategoryIDs);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\category;
+use wcf\system\category\CategoryHandler;
+
+/**
+ * Represents an uncached tree of category nodes.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.category
+ * @category Community Framework
+ */
+class UncachedCategoryNodeTree extends CategoryNodeTree {
+ /**
+ * locally cached categories
+ * @var array<wcf\data\category\Category>
+ */
+ protected $categoryCache = array();
+
+ /**
+ * locally cached category ids grouped by the id of their parent category
+ * @var array
+ */
+ protected $categoryStructureCache = array();
+
+ /**
+ * @see wcf\data\category\CategoryNodeTree::buildTree()
+ */
+ protected function buildTree() {
+ $categoryList = new CategoryList();
+ $categoryList->getConditionBuilder()->add('category.objectTypeID = ?', array(CategoryHandler::getInstance()->getObjectTypeByName($this->objectType)->objectTypeID));
+ $categoryList->readObjects();
+ foreach ($categoryList as $category) {
+ if (!isset($this->categoryStructureCache[$category->parentCategoryID])) {
+ $this->categoryStructureCache[$category->parentCategoryID] = array();
+ }
+
+ $this->categoryStructureCache[$category->parentCategoryID][] = $category->categoryID;
+ $this->categoryCache[$category->categoryID] = $category;
+ }
+
+ parent::buildTree();
+ }
+
+ /**
+ * @see wcf\data\category\CategoryNodeTree::getCategory()
+ */
+ protected function getCategory($categoryID) {
+ return $this->categoryCache[$categoryID];
+ }
+
+ /**
+ * @see wcf\data\category\CategoryNodeTree::getChildCategories()
+ */
+ protected function getChildCategories(CategoryNode $parentNode) {
+ $categories = array();
+ if (isset($this->categoryStructureCache[$parentNode->categoryID])) {
+ foreach ($this->categoryStructureCache[$parentNode->categoryID] as $categoryID) {
+ $categories[$categoryID] = $this->getCategory($categoryID);
+ }
+ }
+
+ return $categories;
+ }
+}
+++ /dev/null
-<?php
-namespace wcf\data\category;
-use wcf\data\DatabaseObjectDecorator;
-use wcf\data\IPermissionObject;
-use wcf\system\category\CategoryPermissionHandler;
-use wcf\system\exception\PermissionDeniedException;
-
-/**
- * Represents a viewable category.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2012 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package com.woltlab.wcf
- * @subpackage data.category
- * @category Community Framework
- */
-class ViewableCategory extends DatabaseObjectDecorator implements IPermissionObject {
- /**
- * list of all parent category generations of this category
- * @var array<wcf\data\category\ViewableCategory>
- */
- protected $parentCategories = null;
-
- /**
- * parent category of this category
- * @var wcf\data\category\ViewableCategory
- */
- protected $parentCategory = null;
-
- /**
- * @see wcf\data\DatabaseObjectDecorator::$baseClass
- */
- protected static $baseClass = 'wcf\data\category\Category';
-
- /**
- * acl permissions for the active user of this category
- * @var array<boolean>
- */
- protected $permissions = null;
-
- /**
- * @see wcf\data\IPermissionObject::checkPermissions()
- */
- public function checkPermissions(array $permissions) {
- foreach ($permissions as $permission) {
- if (!$this->getPermission($permission)) {
- throw new PermissionDeniedException();
- }
- }
- }
-
- /**
- * @see wcf\data\category\Category::getParentCategories()
- */
- public function getParentCategories() {
- if ($this->parentCategories === null) {
- $this->parentCategories = array();
- $className = get_class($this);
-
- foreach ($this->getDecoratedObject()->getParentCategories() as $category) {
- $this->parentCategories[$category->categoryID] = new $className($category);
- }
- }
-
- return $this->parentCategories;
- }
-
- /**
- * @see wcf\data\category\Category::getParentCategory()
- */
- public function getParentCategory() {
- if ($this->parentCategoryID && $this->parentCategory === null) {
- $className = get_class($this);
-
- $this->parentCategory = new $className($this->getDecoratedObject()->getParentCategory());
- }
-
- return $this->parentCategory;
- }
-
- /**
- * @see wcf\data\IPermissionObject::getPermission()
- */
- public function getPermission($permission) {
- if ($this->permissions === null) {
- $this->permissions = CategoryPermissionHandler::getInstance()->getPermissions($this->getDecoratedObject());
- }
-
- if (isset($this->permissions[$permission])) {
- return $this->permissions[$permission];
- }
-
- return false;
- }
-}
+++ /dev/null
-<?php
-namespace wcf\data\category;
-use wcf\data\DatabaseObject;
-
-/**
- * Represents a viewable category node.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2012 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package com.woltlab.wcf
- * @subpackage data.category
- * @category Community Framework
- */
-class ViewableCategoryNode extends CategoryNode {
- /**
- * @see wcf\data\DatabaseObjectDecorator::$baseClass
- */
- protected static $baseClass = 'wcf\data\category\ViewableCategory';
-
- /**
- * @see wcf\data\DatabaseObjectDecorator::__construct()
- */
- public function __construct(DatabaseObject $object, $includeDisabledCategories = false, array $excludedCategoryIDs = array()) {
- if (!($object instanceof static::$baseClass)) {
- $object = new static::$baseClass($object);
- }
-
- parent::__construct($object, $includeDisabledCategories, $excludedCategoryIDs);
- }
-}
+++ /dev/null
-<?php
-namespace wcf\data\category;
-
-/**
- * Represents a list of viewable category nodes.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2013 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package com.woltlab.wcf
- * @subpackage data.category
- * @category Community Framework
- */
-class ViewableCategoryNodeList extends CategoryNodeList { }
* @see wcf\system\category\ICategoryType::afterDeletion()
*/
public function afterDeletion(CategoryEditor $categoryEditor) {
- $categoryIDs = array_keys(CategoryHandler::getInstance()->getChildCategories($categoryEditor->getDecoratedObject()));
+ $categoryIDs = array_keys(CategoryHandler::getInstance()->getChildCategories($categoryEditor->categoryID));
if (!empty($categoryIDs)) {
// move child categories to parent category
<?php
namespace wcf\system\category;
-use wcf\data\category\Category;
use wcf\data\object\type\ObjectTypeCache;
-use wcf\data\DatabaseObject;
-use wcf\data\DatabaseObjectDecorator;
use wcf\system\cache\builder\CategoryCacheBuilder;
use wcf\system\exception\SystemException;
use wcf\system\SingletonFactory;
}
/**
- * Returns the child categories of the given category.
+ * Returns the child categories of the category with the given id.
*
- * @param wcf\data\DatabaseObject $category
+ * The second parameter is only needed if $categoryID is 0.
+ *
+ * @param integer $categoryID
+ * @param integer $objectTypeID
* @return array<wcf\data\category\Category>
*/
- public function getChildCategories(DatabaseObject $category) {
- if (!($category instanceof Category) && (!($category instanceof DatabaseObjectDecorator) || !($category->getDecoratedObject() instanceof Category))) {
- throw new SystemException("Invalid object given (class: ".get_class($category).")");
+ public function getChildCategories($categoryID, $objectTypeID = null) {
+ if (!$categoryID && $objectTypeID === null) {
+ throw new SystemException("Missing object type id");
}
$categories = array();
- foreach ($this->categories as $__category) {
- if ($__category->parentCategoryID == $category->categoryID && ($category->categoryID || $category->objectTypeID == $__category->objectTypeID)) {
- $categories[$__category->categoryID] = $__category;
+ foreach ($this->categories as $category) {
+ if ($category->parentCategoryID == $categoryID && ($categoryID || $category->objectTypeID == $objectTypeID)) {
+ $categories[$category->categoryID] = $category;
}
}