Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6453 lines (5767 sloc) 266 KB
<?php
/**
* File containing the eZContentObjectTreeNode class.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
* @version //autogentag//
* @package kernel
*/
/**
* Encapsulates data about and methods to work with content objects inside the content object tree
*
* @package kernel
*/
class eZContentObjectTreeNode extends eZPersistentObject
{
const SORT_FIELD_PATH = 1;
const SORT_FIELD_PUBLISHED = 2;
const SORT_FIELD_MODIFIED = 3;
const SORT_FIELD_SECTION = 4;
const SORT_FIELD_DEPTH = 5;
const SORT_FIELD_CLASS_IDENTIFIER = 6;
const SORT_FIELD_CLASS_NAME = 7;
const SORT_FIELD_PRIORITY = 8;
const SORT_FIELD_NAME = 9;
const SORT_FIELD_MODIFIED_SUBNODE = 10;
const SORT_FIELD_NODE_ID = 11;
const SORT_FIELD_CONTENTOBJECT_ID = 12;
const SORT_FIELD_VISIBILITY = 13;
const SORT_ORDER_DESC = 0;
const SORT_ORDER_ASC = 1;
/**
* @inheritdoc
*/
static function definition()
{
static $definition = array( "fields" => array( "node_id" => array( 'name' => "NodeID",
'datatype' => 'integer',
'default' => 0,
'required' => true ),
"parent_node_id" => array( 'name' => "ParentNodeID",
'datatype' => 'integer',
'default' => 0,
'required' => true,
'foreign_class' => 'eZContentObjectTreeNode',
'foreign_attribute' => 'node_id',
'multiplicity' => '1..*' ),
"main_node_id" => array( 'name' => "MainNodeID",
'datatype' => 'integer',
'default' => 0,
'required' => true,
'foreign_class' => 'eZContentObjectTreeNode',
'foreign_attribute' => 'node_id',
'multiplicity' => '1..*' ),
"contentobject_id" => array( 'name' => "ContentObjectID",
'datatype' => 'integer',
'default' => 0,
'required' => true,
'foreign_class' => 'eZContentObject',
'foreign_attribute' => 'id',
'multiplicity' => '1..*' ),
'contentobject_version' => array( 'name' => 'ContentObjectVersion',
'datatype' => 'integer',
'default' => 0,
'required' => true ),
'contentobject_is_published' => array( 'name' => 'ContentObjectIsPublished',
'datatype' => 'integer',
'default' => 0,
'required' => true ),
"depth" => array( 'name' => "Depth",
'datatype' => 'integer',
'default' => 0,
'required' => true ),
'sort_field' => array( 'name' => 'SortField',
'datatype' => 'integer',
'default' => self::SORT_FIELD_PATH,
'required' => true ),
'sort_order' => array( 'name' => 'SortOrder',
'datatype' => 'integer',
'default' => self::SORT_ORDER_ASC,
'required' => true ),
'priority' => array( 'name' => 'Priority',
'datatype' => 'integer',
'default' => 0,
'required' => true ),
'modified_subnode' => array( 'name' => 'ModifiedSubNode',
'datatype' => 'integer',
'default' => 0,
'required' => true ),
"path_string" => array( 'name' => "PathString",
'datatype' => 'string',
'default' => '',
'required' => true ),
"path_identification_string" => array( 'name' => "PathIdentificationString",
'datatype' => 'text',
'default' => '',
'required' => true ),
'remote_id' => array( 'name' => 'RemoteID',
'datatype' => 'string',
'default' => '',
'required' => true ),
"is_hidden" => array( 'name' => "IsHidden",
'datatype' => 'integer',
'default' => 0,
'required' => true ),
"is_invisible" => array( 'name' => "IsInvisible",
'datatype' => 'integer',
'default' => 0,
'required' => true ) ),
"keys" => array( "node_id" ),
"function_attributes" => array( "name" => "getName",
'data_map' => 'dataMap',
'remote_id' => 'remoteID', // Note: This overrides remote_id field
"object" => "object",
"subtree" => "subTree",
"children" => "children",
"children_count" => "childrenCount",
'view_count' => 'viewCount',
'contentobject_version_object' => 'contentObjectVersionObject',
'sort_array' => 'sortArray',
'can_read' => 'canRead',
'can_pdf' => 'canPdf',
'can_create' => 'canCreate',
'can_edit' => 'canEdit',
'can_hide' => 'canHide',
'can_remove' => 'canRemove',
'can_move' => 'canMoveFrom',
'can_move_from' => 'canMoveFrom',
'can_add_location' => 'canAddLocation',
'can_remove_location' => 'canRemoveLocation',
'can_view_embed' => 'canViewEmbed',
'is_main' => 'isMain',
'creator' => 'creator',
"path_with_names" => "pathWithNames",
"path" => "fetchPath",
'path_array' => 'pathArray',
"parent" => "fetchParent",
'url' => 'url',
'url_alias' => 'urlAlias',
'class_identifier' => 'classIdentifier',
'class_name' => 'className',
'hidden_invisible_string' => 'hiddenInvisibleString',
'hidden_status_string' => 'hiddenStatusString',
'classes_js_array' => 'availableClassesJsArray',
'is_container' => 'classIsContainer' ),
"increment_key" => "node_id",
"class_name" => "eZContentObjectTreeNode",
"name" => "ezcontentobject_tree" );
return $definition;
}
/**
* Creates a new tree node and returns it.
*
* The attribute remote_id will get an automatic and unique value.
*
* @param int $parentNodeID The ID of the parent or null if the node is not known yet.
* @param int $contentObjectID The ID of the object it points to or null if it is not known yet.
* @param int $contentObjectVersion The version of the object or 0 if not known yet.
* @param int $sortField Number describing the field to sort by, or 0 if not known yet.
* @param bool $sortOrder Which way to sort, true means ascending while false is descending.
*
* @return eZContentObjectTreeNode
*/
static function create( $parentNodeID = null, $contentObjectID = null, $contentObjectVersion = 0,
$sortField = 0, $sortOrder = true )
{
$row = array( 'node_id' => null,
'main_node_id' => null,
'parent_node_id' => $parentNodeID,
'contentobject_id' => $contentObjectID,
'contentobject_version' => $contentObjectVersion,
'contentobject_is_published' => false,
'depth' => 1,
'path_string' => null,
'path_identification_string' => null,
'is_hidden' => false,
'is_invisible' => false,
'sort_field' => $sortField,
'sort_order' => $sortOrder,
'modified_subnode' => 0,
'remote_id' => eZRemoteIdUtility::generate( 'node' ),
'priority' => 0 );
$node = new eZContentObjectTreeNode( $row );
return $node;
}
/**
* @since 4.7
* @var bool
*/
static protected $useCurrentUserDraft = false;
/**
* Enables / disables Use current user draft mode for data map
*
* When this mode is enabled (disabled by default), current user draft is used _if_ available
* on all dataMap calls.
*
* @since 4.7
* @param bool $enable
*/
static public function setUseCurrentUserDraft( $enable )
{
self::$useCurrentUserDraft = (bool) $enable;
}
/**
* Returns an array with all the content object attributes where the keys are the attribute identifiers.
*
* @see eZContentObject::fetchDataMap()
* @return eZContentObjectAttribute[]
*/
function dataMap()
{
$object = $this->object();
if ( self::$useCurrentUserDraft )
{
$draft = eZContentObjectVersion::fetchLatestUserDraft(
$object->attribute( 'id' ),
eZUser::currentUserID(),
$object->currentLanguageObject()->attribute( 'id' ),
$object->attribute( 'modified' )
);
if ( $draft instanceof eZContentObjectVersion )
return $object->fetchDataMap( $draft->attribute( 'version' ) );
}
return $object->fetchDataMap( $this->attribute( 'contentobject_version' ) );
}
/**
* Get the remote id of content node
*
* If there is no remote ID a new unique one will be generated.
*
* The remote ID is often used to synchronise imports and exports.
*
* @return string
*/
function remoteID()
{
$remoteID = $this->attribute( 'remote_id', true );
if ( !$remoteID )
{
$this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'node' ) );
$this->sync( array( 'remote_id' ) );
$remoteID = $this->attribute( 'remote_id', true );
}
return $remoteID;
}
/**
* Returns true if this node is the main node.
*
* @return bool
*/
function isMain()
{
return $this->NodeID == $this->MainNodeID;
}
/**
* Returns the ID of the class attribute with the given ID or false if no class/attribute by that identifier
* is found. If multiple classes have the same identifier, the first found is returned.
*
* @param string $identifier
* @return int|bool
*/
static function classAttributeIDByIdentifier( $identifier )
{
return eZContentClassAttribute::classAttributeIDByIdentifier( $identifier );
}
/**
* Return the ID of the class with the given ID or false if no class by that identifier is found.
* If multiple classes have the same identifier, the first found is returned.
*
* @param string $identifier
* @return int|bool
*/
static function classIDByIdentifier( $identifier )
{
return eZContentClass::classIDByIdentifier( $identifier );
}
/**
* Returns true if the node can be read by the current user.
*
* @return bool
*/
function canRead( )
{
if ( !isset( $this->Permissions["can_read"] ) )
{
$this->Permissions["can_read"] = $this->checkAccess( 'read' );
}
return ( $this->Permissions["can_read"] == 1 );
}
/**
* Returns true if the current user can create a pdf of this content object.
*
* @return bool
*/
function canPdf( )
{
if ( !isset( $this->Permissions["can_pdf"] ) )
{
$this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' );
}
return ( $this->Permissions["can_pdf"] == 1 );
}
/**
* Returns true if the node can be viewed as embeded object by the current user.
*
* @return bool
*/
function canViewEmbed( )
{
if ( !isset( $this->Permissions["can_view_embed"] ) )
{
$this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' );
}
return ( $this->Permissions["can_view_embed"] == 1 );
}
/**
* Returns true if the node can be edited by the current user.
*
* @return bool
*/
function canEdit( )
{
if ( !isset( $this->Permissions["can_edit"] ) )
{
$this->Permissions["can_edit"] = $this->checkAccess( 'edit' );
if ( $this->Permissions["can_edit"] != 1 )
{
$user = eZUser::currentUser();
if ( $user->id() == $this->ContentObject->attribute( 'id' ) )
{
$access = $user->hasAccessTo( 'user', 'selfedit' );
if ( $access['accessWord'] == 'yes' )
{
$this->Permissions["can_edit"] = 1;
}
}
}
}
return ( $this->Permissions["can_edit"] == 1 );
}
/**
* Returns true if the node can be hidden by the current user.
*
* @return bool
*/
function canHide( )
{
if ( !isset( $this->Permissions["can_hide"] ) )
{
$this->Permissions["can_hide"] = $this->checkAccess( 'hide' );
}
return ( $this->Permissions["can_hide"] == 1 );
}
/**
* Returns true if the current user can create a new node as child of this node.
*
* @return bool
*/
function canCreate( )
{
if ( !isset( $this->Permissions["can_create"] ) )
{
$this->Permissions["can_create"] = $this->checkAccess( 'create' );
}
return ( $this->Permissions["can_create"] == 1 );
}
/**
* Returns true if the node can be removed by the current user.
*
* @return bool
*/
function canRemove( )
{
if ( !isset( $this->Permissions["can_remove"] ) )
{
$this->Permissions["can_remove"] = $this->checkAccess( 'remove' );
}
return ( $this->Permissions["can_remove"] == 1 );
}
/**
* Returns true if the node can be moved by the current user.
*
* @return bool
*/
function canMoveFrom( )
{
if ( !isset( $this->Permissions['can_move_from'] ) )
{
$this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' );
}
return ( $this->Permissions['can_move_from'] == 1 );
}
/**
* Returns true if a node of class $classID can be moved to the current node by the current user.
*
* @param bool $classID
* @return bool
*/
function canMoveTo( $classID = false )
{
if ( !isset( $this->Permissions['can_move_to'] ) )
{
$this->Permissions['can_move_to'] = $this->checkAccess( 'create', $classID );
}
return ( $this->Permissions['can_move_to'] == 1 );
}
/**
* Returns true if a node can be swaped by the current user.
*
* @return bool
*/
function canSwap()
{
if ( !isset( $this->Permissions['can_swap'] ) )
{
$this->Permissions['can_swap'] = $this->checkAccess( 'edit' );
}
return ( $this->Permissions['can_swap'] == 1 );
}
/**
* Returns true if current user can add object locations to current node.
*
* @return bool
*/
function canAddLocation()
{
if ( !isset( $this->Permissions['can_add_location'] ) )
{
$this->Permissions['can_add_location'] = $this->checkAccess( 'can_add_location' );
}
return ( $this->Permissions['can_add_location'] == 1 );
}
/**
* Returns true if current user can add object locations to current node.
*
* @return bool
*/
function canRemoveLocation()
{
if ( !isset( $this->Permissions['can_remove_location'] ) )
{
$this->Permissions['can_remove_location'] = $this->checkAccess( 'can_remove_location' );
}
return ( $this->Permissions['can_remove_location'] == 1 );
}
/**
* Returns the sort key for the given classAttributeID or false if it can't be retrieved
*
* @param int $classAttributeID
* @return int|string|bool
*/
static function sortKeyByClassAttributeID( $classAttributeID )
{
return eZContentClassAttribute::sortKeyTypeByID( $classAttributeID );
}
/**
* Returns the datatype of a class attribute
*
* @param int $classAttributeID
* @return string
*/
static function dataTypeByClassAttributeID( $classAttributeID )
{
return eZContentClassAttribute::dataTypeByID( $classAttributeID );
}
/**
* Fetches the number of nodes which exists in the system.
*
* @return int
*/
static function fetchListCount()
{
$sql = "SELECT count( node_id ) as count FROM ezcontentobject_tree";
$db = eZDB::instance();
$rows = $db->arrayQuery( $sql );
return $rows[0]['count'];
}
/**
* Fetches a list of nodes and returns it. Offset and limitation can be set if needed.
*
* @param bool $asObject
* @param int|bool $offset
* @param int|bool $limit
* @return eZContentObjectTreeNode[]
*/
static function fetchList( $asObject = true, $offset = false, $limit = false )
{
$sql = "SELECT * FROM ezcontentobject_tree";
$parameters = array();
if ( $offset !== false )
$parameters['offset'] = $offset;
if ( $limit !== false )
$parameters['limit'] = $limit;
$db = eZDB::instance();
$rows = $db->arrayQuery( $sql, $parameters );
$nodes = array();
if ( $asObject )
{
foreach ( $rows as $row )
{
$nodes[] = new eZContentObjectTreeNode( $row );
}
return $nodes;
}
else
return $rows;
}
/**
* Creates an array with sorting SQL strings to be appended to a query
*
* @param array|bool $sortList
* @param string $treeTableName
* @param bool $allowCustomColumns
* @return array
*/
static function createSortingSQLStrings( $sortList, $treeTableName = 'ezcontentobject_tree', $allowCustomColumns = false )
{
$sortingInfo = array( 'sortCount' => 0,
'sortingFields' => " path_string ASC",
'attributeJoinCount' => 0,
'attributeFromSQL' => "",
'attributeTargetSQL' => "",
'attributeWhereSQL' => "" );
if ( $sortList and is_array( $sortList ) and count( $sortList ) > 0 )
{
if ( count( $sortList ) > 1 and !is_array( $sortList[0] ) )
{
$sortList = array( $sortList );
}
$sortingFields = '';
$sortCount = 0;
$attributeJoinCount = 0;
$stateJoinCount = 0;
$attributeFromSQL = "";
$attributeWhereSQL = "";
$datatypeSortingTargetSQL = "";
foreach ( $sortList as $sortBy )
{
if ( is_array( $sortBy ) and count( $sortBy ) > 0 )
{
if ( $sortCount > 0 )
{
$sortingFields .= ', ';
}
$sortField = $sortBy[0];
switch ( $sortField )
{
case 'path':
{
$sortingFields .= 'path_string';
} break;
case 'path_string':
{
$sortingFields .= 'path_identification_string';
} break;
case 'published':
{
$sortingFields .= 'ezcontentobject.published';
} break;
case 'modified':
{
$sortingFields .= 'ezcontentobject.modified';
} break;
case 'modified_subnode':
{
$sortingFields .= 'modified_subnode';
} break;
case 'section':
{
$sortingFields .= 'ezcontentobject.section_id';
} break;
case 'node_id':
{
$sortingFields .= $treeTableName . '.node_id';
} break;
case 'contentobject_id':
{
$sortingFields .= 'ezcontentobject.id';
} break;
case 'depth':
{
$sortingFields .= 'depth';
} break;
case 'class_identifier':
{
$sortingFields .= 'ezcontentclass.identifier';
} break;
case 'class_name':
{
$classNameFilter = eZContentClassName::sqlFilter();
$sortingFields .= 'contentclass_name';
$datatypeSortingTargetSQL .= ", $classNameFilter[nameField] AS contentclass_name";
$attributeFromSQL .= " INNER JOIN $classNameFilter[from] ON ($classNameFilter[where])";
} break;
case 'priority':
{
$sortingFields .= $treeTableName . '.priority';
} break;
case 'visibility':
{
$sortingFields .= $treeTableName . '.is_invisible';
} break;
case 'name':
{
$sortingFields .= 'ezcontentobject_name.name';
} break;
case 'attribute':
{
$classAttributeID = $sortBy[2];
if ( !is_numeric( $classAttributeID ) )
$classAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $classAttributeID );
$contentAttributeTableAlias = "a$attributeJoinCount";
$datatypeFromSQL = "ezcontentobject_attribute $contentAttributeTableAlias";
$datatypeWhereSQL = "
$contentAttributeTableAlias.contentobject_id = ezcontentobject.id AND
$contentAttributeTableAlias.contentclassattribute_id = $classAttributeID AND
$contentAttributeTableAlias.version = ezcontentobject.current_version AND";
$datatypeWhereSQL .= eZContentLanguage::sqlFilter( $contentAttributeTableAlias, 'ezcontentobject' );
$dataType = eZDataType::create( eZContentObjectTreeNode::dataTypeByClassAttributeID( $classAttributeID ) );
if( is_object( $dataType ) && $dataType->customSorting() )
{
$params = array();
$params['contentobject_attr_id'] = "$contentAttributeTableAlias.id";
$params['contentobject_attr_version'] = "$contentAttributeTableAlias.version";
$params['table_alias_suffix'] = "$attributeJoinCount";
$sql = $dataType->customSortingSQL( $params );
$datatypeFromSQL .= " INNER JOIN $sql[from] ON ($sql[where])";
$datatypeSortingFieldSQL = $sql['sorting_field'];
$datatypeSortingTargetSQL .= ', ' . $sql['sorting_field'];
}
else
{
// Look up datatype for standard sorting
$sortKeyType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $classAttributeID );
switch ( $sortKeyType )
{
case 'string':
{
$sortKey = 'sort_key_string';
} break;
case 'int':
default:
{
$sortKey = 'sort_key_int';
} break;
}
$datatypeSortingFieldSQL = "a$attributeJoinCount.$sortKey";
$datatypeSortingTargetSQL .= ', ' . $datatypeSortingFieldSQL;
}
$sortingFields .= "$datatypeSortingFieldSQL";
$attributeFromSQL .= " INNER JOIN $datatypeFromSQL ON ($datatypeWhereSQL)";
$attributeJoinCount++;
}break;
case 'state':
{
$stateGroupID = $sortBy[2];
if ( !is_numeric( $stateGroupID ) )
{
$stateGroup = eZContentObjectStateGroup::fetchByIdentifier( $stateGroupID );
if ( $stateGroup )
{
$stateGroupID = $stateGroup->attribute( 'id' );
}
else
{
eZDebug::writeError( "Unknown content object state group '$stateGroupID'" );
continue 2;
}
}
$stateAlias = "s$stateJoinCount";
$stateLinkAlias = "sl$stateJoinCount";
$sortingFields .= "$stateAlias.priority";
$datatypeSortingTargetSQL .= ", $stateAlias.priority";
$attributeFromSQL .=
" INNER JOIN ezcobj_state $stateAlias ON ($stateAlias.group_id = $stateGroupID)" .
" INNER JOIN ezcobj_state_link $stateLinkAlias" .
" ON ($stateLinkAlias.contentobject_id = ezcontentobject.id AND $stateLinkAlias.contentobject_state_id = $stateAlias.id)";
} break;
default:
{
if ( $allowCustomColumns )
{
$sortingFields .= $sortField;
}
else
{
eZDebug::writeWarning( 'Unknown sort field: ' . $sortField, __METHOD__ );
continue;
}
}
}
$sortOrder = true; // true is ascending
if ( isset( $sortBy[1] ) )
$sortOrder = $sortBy[1];
$sortingFields .= $sortOrder ? " ASC" : " DESC";
++$sortCount;
}
}
$sortingInfo['sortCount'] = $sortCount;
$sortingInfo['sortingFields'] = $sortingFields;
$sortingInfo['attributeTargetSQL'] = $datatypeSortingTargetSQL;
$sortingInfo['attributeJoinCount'] = $attributeJoinCount;
$sortingInfo['attributeFromSQL'] = $attributeFromSQL;
$sortingInfo['attributeWhereSQL'] = $attributeWhereSQL;
}
else if ( $sortList === array() )
{
$sortingInfo['sortingFields'] = '';
}
return $sortingInfo;
}
/**
* Returns an SQL string to filter query results by classes
*
* @param string|bool $classFilterType
* @param array $classFilterArray
* @return string|bool
*/
static function createClassFilteringSQLString( $classFilterType, &$classFilterArray )
{
// Check for class filtering
$classCondition = '';
if ( isset( $classFilterType ) &&
( $classFilterType == 'include' || $classFilterType == 'exclude' ) &&
count( $classFilterArray ) > 0 )
{
$classCondition = ' ';
$i = 0;
$classCount = count( $classFilterArray );
$classIDArray = array();
foreach ( $classFilterArray as $classID )
{
$originalClassID = $classID;
// Check if classes are recerenced by identifier
if ( is_string( $classID ) && !is_numeric( $classID ) )
{
$classID = eZContentClass::classIDByIdentifier( $classID );
}
if ( is_numeric( $classID ) )
{
$classIDArray[] = $classID;
}
else
{
eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
}
}
if ( count( $classIDArray ) > 0 )
{
$classCondition .= " ezcontentobject.contentclass_id ";
if ( $classFilterType == 'include' )
$classCondition .= " IN ";
else
$classCondition .= " NOT IN ";
$classIDString = implode( ', ', $classIDArray );
$classCondition .= ' ( ' . $classIDString . ' ) AND';
}
else
{
if ( count( $classIDArray ) == 0 and count( $classFilterArray ) > 0 and $classFilterType == 'include' )
{
$classCondition = false;
}
}
}
return $classCondition;
}
/**
* Creates a filter array from extended attribute filters
*
* The filter array includes tables, joins, columns and grouping information
*
* @param array $extendedAttributeFilter
* @return array
*/
static function createExtendedAttributeFilterSQLStrings( &$extendedAttributeFilter )
{
$filter = array( 'tables' => '',
'joins' => '',
'columns' => '',
'group_by' => '' );
if ( $extendedAttributeFilter and count( $extendedAttributeFilter ) > 1 )
{
$extendedAttributeFilterID = $extendedAttributeFilter['id'];
$extendedAttributeFilterParams = $extendedAttributeFilter['params'];
$filterINI = eZINI::instance( 'extendedattributefilter.ini' );
if ( !$filterINI->hasGroup( $extendedAttributeFilterID ) )
{
eZDebug::writeError( "Unable to find configuration for the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
return $filter;
}
$filterClassName = $filterINI->variable( $extendedAttributeFilterID, 'ClassName' );
$filterMethodName = $filterINI->variable( $extendedAttributeFilterID, 'MethodName' );
if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'FileName' ) )
{
$filterFile = $filterINI->variable( $extendedAttributeFilterID, 'FileName' );
if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'ExtensionName' ) )
{
$extensionName = $filterINI->variable( $extendedAttributeFilterID, 'ExtensionName' );
include_once( eZExtension::baseDirectory() . "/$extensionName/$filterFile" );
}
else
{
include_once( $filterFile );
}
}
if ( !class_exists( $filterClassName, true ) )
{
eZDebug::writeError( "Unable to find the PHP class '$filterClassName' associated with the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
return $filter;
}
$classObject = new $filterClassName();
$parameterArray = array( $extendedAttributeFilterParams );
$sqlResult = call_user_func_array( array( $classObject, $filterMethodName ), $parameterArray );
$filter['tables'] = $sqlResult['tables'];
$filter['joins'] = $sqlResult['joins'];
$filter['columns'] = isset( $sqlResult['columns'] ) ? $sqlResult['columns'] : '';
if( isset( $sqlResult['group_by'] ) )
$filter['group_by'] = $sqlResult['group_by'];
}
return $filter;
}
/**
* If $mainNodeOnly is set to true, creates an SQL part which makes sure the fetched node(s) are main nodes
*
* @param bool $mainNodeOnly
* @return string
*/
static function createMainNodeConditionSQLString( $mainNodeOnly )
{
// Main node check
$mainNodeCondition = '';
if ( isset( $mainNodeOnly ) && $mainNodeOnly === true )
{
$mainNodeCondition = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
}
return $mainNodeCondition;
}
/**
* Creates an SQL part to match objects with a name starting with $filter
*
* If $filter is "others", the SQL part will match only names which do NOT start with a letter from the
* alphabet.
*
* @see eZAlphabetOperator::fetchAlphabet()
*
* @param string $filter
* @return string
*/
static function createObjectNameFilterConditionSQLString( $filter )
{
if ( !$filter )
return '';
$db = eZDB::instance();
if ( $filter == 'others' )
{
$alphabet = eZAlphabetOperator::fetchAlphabet();
$sql = '';
foreach ( $alphabet as $letter )
{
$sql .= " AND ezcontentobject_name.name NOT LIKE '". $db->escapeString( $letter ) . "%' ";
}
return $sql;
}
$objectNameFilterSQL = " AND ezcontentobject_name.name LIKE '" . $db->escapeString( $filter ) ."%'";
return $objectNameFilterSQL;
}
/**
* Returns an array to filter a query by the given attributes in $attributeFilter
*
* @param array|bool $attributeFilter
* @param array $sortingInfo
* @param array|bool $language
* @return array|bool
*/
static function createAttributeFilterSQLStrings( &$attributeFilter, &$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 ), $language = false )
{
// Check for attribute filtering
$filterSQL = array( 'from' => '',
'where' => '' );
if ( $language !== false && !is_array( $language ) )
{
$language = array( $language );
}
$totalAttributesFiltersCount = 0;
$invalidAttributesFiltersCount = 0;
if ( isset( $attributeFilter ) && $attributeFilter !== false )
{
if ( !is_array( $attributeFilter ) )
{
eZDebug::writeError( "\$attributeFilter needs to be an array", __METHOD__ );
return $filterSQL;
}
$filterArray = $attributeFilter;
// Check if first value of array is a string.
// To check for and/or filtering
$filterJoinType = 'AND';
if ( is_string( $filterArray[0] ) )
{
if ( strtolower( $filterArray[0] ) == 'or' )
{
$filterJoinType = 'OR';
}
else if ( strtolower( $filterArray[0] ) == 'and' )
{
$filterJoinType = 'AND';
}
unset( $filterArray[0] );
}
$attibuteFilterJoinSQL = "";
$filterCount = $sortingInfo['sortCount'];
$justFilterCount = 0;
$db = eZDB::instance();
if ( is_array( $filterArray ) )
{
// Handle attribute filters and generate SQL
$totalAttributesFiltersCount = count( $filterArray );
foreach ( $filterArray as $filter )
{
$isFilterValid = true; // by default assumes that filter is valid
$filterAttributeID = $filter[0];
$filterType = $filter[1];
$filterValue = is_array( $filter[2] ) ? '' : $db->escapeString( $filter[2] );
$useAttributeFilter = false;
$filterFieldType = 'integer';
switch ( $filterAttributeID )
{
case 'path':
{
$filterField = 'ezcontentobject_tree.path_string';
$filterFieldType = 'string';
} break;
case 'published':
{
$filterField = 'ezcontentobject.published';
} break;
case 'modified':
{
$filterField = 'ezcontentobject.modified';
} break;
case 'modified_subnode':
{
$filterField = 'ezcontentobject_tree.modified_subnode';
} break;
case 'node_id':
{
$filterField = 'ezcontentobject_tree.node_id';
} break;
case 'contentobject_id':
{
$filterField = 'ezcontentobject_tree.contentobject_id';
} break;
case 'section':
{
$filterField = 'ezcontentobject.section_id';
} break;
case 'state':
{
// state only supports =, !=, in, and not_in
// other operators do not make any sense in this context
$hasFilterOperator = true;
switch ( $filterType )
{
case '=' :
case '!=':
{
$subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2];
$filterOperator = ( $filterType == '=' ? 'IN' : 'NOT IN' );
} break;
case 'in':
case 'not_in' :
{
if ( is_array( $filter[2] ) )
{
$subQueryCondition = $db->generateSQLINStatement( $filter[2], 'contentobject_state_id', false, false, 'int' );
$filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
}
else
{
$hasFilterOperator = false;
}
} break;
default :
{
$hasFilterOperator = false;
eZDebug::writeError( "Unknown attribute filter type for state: $filterType", __METHOD__ );
} break;
}
if ( $hasFilterOperator )
{
if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
$attibuteFilterJoinSQL .= " $filterJoinType ";
$attibuteFilterJoinSQL .= "ezcontentobject.id $filterOperator (SELECT contentobject_id FROM ezcobj_state_link WHERE $subQueryCondition)";
$filterCount++;
$justFilterCount++;
}
continue 2;
} break;
case 'depth':
{
$filterField = 'ezcontentobject_tree.depth';
} break;
case 'class_identifier':
{
$filterField = 'ezcontentclass.identifier';
$filterFieldType = 'string';
} break;
case 'class_name':
{
$classNameFilter = eZContentClassName::sqlFilter();
$filterField = $classNameFilter['nameField'];
$filterFieldType = 'string';
$filterSQL['from'] .= " INNER JOIN $classNameFilter[from] ON ($classNameFilter[where])";
} break;
case 'priority':
{
$filterField = 'ezcontentobject_tree.priority';
} break;
case 'name':
{
$filterField = 'ezcontentobject_name.name';
$filterFieldType = 'string';
} break;
case 'owner':
{
$filterField = 'ezcontentobject.owner_id';
} break;
case 'visibility':
{
$filterValue = ( $filterValue == '1' ) ? 0 : 1;
$filterField = 'ezcontentobject_tree.is_invisible';
} break;
default:
{
$useAttributeFilter = true;
} break;
}
if ( $useAttributeFilter )
{
if ( !is_numeric( $filterAttributeID ) )
$filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $filterAttributeID );
if ( $filterAttributeID === false )
{
$isFilterValid = false;
if( $filterJoinType === 'AND' )
{
// go out
$invalidAttributesFiltersCount = $totalAttributesFiltersCount;
break;
}
++$invalidAttributesFiltersCount;
}
else
{
// Check datatype for filtering
$filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $filterAttributeID );
if ( $filterDataType === false )
{
$isFilterValid = false;
if( $filterJoinType === 'AND' )
{
// go out
$invalidAttributesFiltersCount = $totalAttributesFiltersCount;
break;
}
// check next filter
++$invalidAttributesFiltersCount;
}
else
{
$sortKey = false;
if ( $filterDataType == 'string' )
{
$sortKey = 'sort_key_string';
$filterFieldType = 'string';
}
else
{
$sortKey = 'sort_key_int';
}
$filterField = "a$filterCount.$sortKey";
// Use the same joins as we do when sorting,
// if more attributes are filtered by we will append them
if ( $filterCount >= $sortingInfo['attributeJoinCount'] )
{
$filterSQL['from'] .= " INNER JOIN ezcontentobject_attribute a$filterCount ON (a$filterCount.contentobject_id = ezcontentobject.id) ";
}
// Ref http://issues.ez.no/19190
// If language param is set, we must take it into account.
if ( $language )
{
eZContentLanguage::setPrioritizedLanguages( $language );
}
$filterSQL['where'] .= "
a$filterCount.contentobject_id = ezcontentobject.id AND
a$filterCount.contentclassattribute_id = $filterAttributeID AND
a$filterCount.version = ezcontentobject_name.content_version AND ";
$filterSQL['where'] .= eZContentLanguage::sqlFilter( "a$filterCount", 'ezcontentobject' ). ' AND ';
if ( $language )
{
eZContentLanguage::clearPrioritizedLanguages();
}
}
}
}
if ( $isFilterValid )
{
$hasFilterOperator = true;
// Controls quotes around filter value, some filters do this manually
$noQuotes = false;
// Controls if $filterValue or $filter[2] is used, $filterValue is already escaped
$unEscape = false;
switch ( $filterType )
{
case '=' :
{
$filterOperator = '=';
}break;
case '!=' :
{
$filterOperator = '<>';
}break;
case '>' :
{
$filterOperator = '>';
}break;
case '<' :
{
$filterOperator = '<';
}break;
case '<=' :
{
$filterOperator = '<=';
}break;
case '>=' :
{
$filterOperator = '>=';
}break;
case 'like':
case 'not_like':
{
$filterOperator = ( $filterType == 'like' ? 'LIKE' : 'NOT LIKE' );
// We escape the string ourselves, this MUST be done before wildcard replace
$filter[2] = $db->escapeString( $filter[2] );
$unEscape = true;
// Since * is used as wildcard we need to transform the string to
// use % as wildcard. The following rules apply:
// - % -> \%
// - * -> %
// - \* -> *
// - \\ -> \
$filter[2] = preg_replace( array( '#%#m',
'#(?<!\\\\)\\*#m',
'#(?<!\\\\)\\\\\\*#m',
'#\\\\\\\\#m' ),
array( '\\%',
'%',
'*',
'\\\\' ),
$filter[2] );
} break;
case 'in':
case 'not_in' :
{
$filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
// Turn off quotes for value, we do this ourselves
$noQuotes = true;
if ( is_array( $filter[2] ) )
{
reset( $filter[2] );
foreach ( $filter[2] as $key => $value )
{
// Non-numerics must be escaped to avoid SQL injection
switch ( $filterFieldType )
{
case 'string':
$filter[2][$key] = "'" . $db->escapeString( $value ) . "'";
break;
case 'integer':
default:
$filter[2][$key] = (int) $value;
}
}
$filterValue = '(' . implode( ",", $filter[2] ) . ')';
}
else
{
$hasFilterOperator = false;
}
} break;
case 'between':
case 'not_between' :
{
$filterOperator = ( $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN' );
// Turn off quotes for value, we do this ourselves
$noQuotes = true;
if ( is_array( $filter[2] ) )
{
// Check for non-numerics to avoid SQL injection
if ( !is_numeric( $filter[2][0] ) )
$filter[2][0] = "'" . $db->escapeString( $filter[2][0] ) . "'";
if ( !is_numeric( $filter[2][1] ) )
$filter[2][1] = "'" . $db->escapeString( $filter[2][1] ) . "'";
$filterValue = $filter[2][0] . ' AND ' . $filter[2][1];
}
} break;
default :
{
$hasFilterOperator = false;
eZDebug::writeError( "Unknown attribute filter type: $filterType", __METHOD__ );
}break;
}
if ( $hasFilterOperator )
{
if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
$attibuteFilterJoinSQL .= " $filterJoinType ";
// If $unEscape is true we get the filter value from the 2nd element instead
// which must have been escaped by filter type
$filterValue = $unEscape ? $filter[2] : $filterValue;
$attibuteFilterJoinSQL .= "$filterField $filterOperator ";
$attibuteFilterJoinSQL .= $noQuotes ? "$filterValue " : "'$filterValue' ";
$filterCount++;
$justFilterCount++;
}
}
} // end of 'foreach ( $filterArray as $filter )'
if ( $totalAttributesFiltersCount == $invalidAttributesFiltersCount )
{
eZDebug::writeNotice( "Attribute filter returned false" );
$filterSQL = false;
}
else
{
if ( $justFilterCount > 0 )
$filterSQL['where'] .= " ( " . $attibuteFilterJoinSQL . " ) AND ";
}
} // end of 'if ( is_array( $filterArray ) )'
}
return $filterSQL;
}
/**
* Creates an SQL part to exclude the parent node from a query to fetch children of the node $nodeID, if needed
*
* @param int $nodeID
* @param int|bool $depth
* @param string $depthOperator
* @return string
*/
static function createNotEqParentSQLString( $nodeID, $depth = false, $depthOperator = 'le' )
{
$notEqParentString = '';
if( !$depth || $depthOperator == 'le' || $depthOperator == 'lt' )
{
$notEqParentString = "ezcontentobject_tree.node_id != $nodeID AND";
}
return $notEqParentString;
}
/**
* Returns an SQL part which makes sure that fetched nodes are (not) part of the given node path
*
* @param string $nodePath
* @param int $nodeDepth
* @param bool $depth
* @param string $depthOperator
* @return string
*/
static function createPathConditionSQLString( $nodePath, $nodeDepth, $depth = false, $depthOperator = 'le' )
{
$pathCondition = '';
$depthCondition = '';
if ( $depth )
{
$sqlDepthOperator = '<=';
if ( $depthOperator )
{
if ( $depthOperator == 'lt' )
{
$sqlDepthOperator = '<';
}
else if ( $depthOperator == 'gt' )
{
$sqlDepthOperator = '>';
}
else if ( $depthOperator == 'le' )
{
$sqlDepthOperator = '<=';
}
else if ( $depthOperator == 'ge' )
{
$sqlDepthOperator = '>=';
}
else if ( $depthOperator == 'eq' )
{
$sqlDepthOperator = '=';
}
}
$nodeDepth += $depth;
$depthCondition = ' ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' and ';
}
$pathCondition = " ezcontentobject_tree.path_string like '$nodePath%' and $depthCondition ";
return $pathCondition;
}
/**
* Returns an SQL part which makes sure that fetched nodes are (not) part of the given node path
* and not the parent node
*
* @param string $outPathConditionStr
* @param string $outNotEqParentStr
* @param int $nodeID
* @param bool $depth
* @param string $depthOperator
* @return bool
*/
static function createPathConditionAndNotEqParentSQLStrings( &$outPathConditionStr, &$outNotEqParentStr, $nodeID, $depth = false, $depthOperator = 'le' )
{
if ( !$depthOperator )
{
$depthOperator = 'le';
}
// check if we are only fetching children
// - depth (lower than or) eqaul to 1
// - depth lower than 2 = depth equal to 1
$onlyChildren = ( $depth === 1 && ( $depthOperator === 'le' || $depthOperator === 'eq' ) ) ||
( $depth === 2 && $depthOperator === 'lt' );
if ( is_array( $nodeID ) && count( $nodeID ) == 1 )
{
$nodeID = $nodeID[0];
}
if ( is_array( $nodeID ) )
{
$outNotEqParentStr = '';
// a parent_node_id condition suffits when only fetching children
if ( $onlyChildren )
{
$db = eZDB::instance();
$outPathConditionStr = $db->generateSQLINStatement( $nodeID, 'ezcontentobject_tree.parent_node_id', false, true, 'int' ) . ' and';
}
else
{
$nodeIDList = $nodeID;
$sqlPartForOneNodeList = array();
foreach ( $nodeIDList as $nodeID )
{
$node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
if ( !is_array( $node ) )
return false;
$nodePath = $node['path_string'];
$nodeDepth = $node['depth'];
$depthCond = '';
if ( $depth )
{
$sqlDepthOperator = '<=';
if ( $depthOperator )
{
if ( $depthOperator == 'lt' )
{
$sqlDepthOperator = '<';
}
else if ( $depthOperator == 'gt' )
{
$sqlDepthOperator = '>';
}
else if ( $depthOperator == 'le' )
{
$sqlDepthOperator = '<=';
}
else if ( $depthOperator == 'ge' )
{
$sqlDepthOperator = '>=';
}
else if ( $depthOperator == 'eq' )
{
$sqlDepthOperator = '=';
}
}
$nodeDepth += $depth;
$depthCond = ' and ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' ';
}
$requireNotEqParentStr = !$depth || $depthOperator == 'le' || $depthOperator == 'lt';
$notEqParentStr = $requireNotEqParentStr ? " and ezcontentobject_tree.node_id != $nodeID " : '';
$sqlPartForOneNodeList[] = " ( ezcontentobject_tree.path_string like '$nodePath%' $depthCond $notEqParentStr ) ";
}
$outPathConditionStr = implode( ' or ', $sqlPartForOneNodeList );
$outPathConditionStr = ' (' . $outPathConditionStr . ') and';
}
}
else
{
if ( $nodeID == 0 )
{
return false;
}
// a parent_node_id condition suffits when only fetching children
if ( $onlyChildren )
{
$outNotEqParentStr = '';
$outPathConditionStr = 'ezcontentobject_tree.parent_node_id = ' . (int) $nodeID . ' and';
}
else
{
$node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
if ( !is_array( $node ) )
return false;
$nodePath = $node['path_string'];
$nodeDepth = $node['depth'];
$outNotEqParentStr = eZContentObjectTreeNode::createNotEqParentSQLString( $nodeID, $depth, $depthOperator );
$outPathConditionStr = eZContentObjectTreeNode::createPathConditionSQLString( $nodePath, $nodeDepth, $depth, $depthOperator );
}
}
return true;
}
/*!
\a static
*/
static function createGroupBySQLStrings( &$outGroupBySelectText, &$outGroupByText, $groupBy )
{
if ( $groupBy )
{
if ( isset( $groupBy['field'] ) and isset( $groupBy['type'] ) )
{
$groupByField = $groupBy['field'];
$groupByFieldType = $groupBy['type'];
switch ( $groupByField )
{
case 'published':
{
$groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
$groupBySelect['field'] = "ezcontentobject." . $groupByField;
} break;
case 'modified':
{
$groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
$groupBySelect['field'] = "ezcontentobject." . $groupByField;
} break;
}
$outGroupBySelectText = ", " . $groupBySelect['select'];
$outGroupByText = "GROUP BY " . $groupBySelect['group_field'];
}
}
}
/**
* @deprecated Since 5.0
*/
static function createVersionNameTablesSQLString( $useVersionName )
{
eZDebug::writeStrict( 'Method ' . __METHOD__ . ' has been deprecated in 5.0', 'Deprecation' );
$versionNameTables = '';
if ( $useVersionName )
{
$versionNameTables = ' INNER JOIN ezcontentobject_name ';
}
return $versionNameTables;
}
/**
* @deprecated Since 5.0
*/
static function createVersionNameTargetsSQLString( $useVersionName )
{
eZDebug::writeStrict( 'Method ' . __METHOD__ . ' has been deprecated in 5.0', 'Deprecation' );
$versionNameTargets = '';
if ( $useVersionName )
{
$versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation ';
}
return $versionNameTargets;
}
/**
* @deprecated Since 5.0
*/
static function createVersionNameJoinsSQLString( $useVersionName, $includeAnd = true, $onlyTranslated = false, $lang = false, $treeTableName = 'ezcontentobject_tree' )
{
eZDebug::writeStrict( 'Method ' . __METHOD__ . ' has been deprecated in 5.0', 'Deprecation' );
$versionNameJoins = '';
if ( $useVersionName )
{
if ( $includeAnd )
{
$versionNameJoins .= ' AND ';
}
$versionNameJoins .= " $treeTableName.contentobject_id = ezcontentobject_name.contentobject_id and
$treeTableName.contentobject_version = ezcontentobject_name.content_version and ";
$versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
}
return $versionNameJoins;
}
/*!
\a static
*/
static function createPermissionCheckingSQL( $limitationList, $treeTableName = 'ezcontentobject_tree', $tableAliasName = 'ezcontentobject_tree' )
{
$db = eZDB::instance();
$sqlPermissionCheckingFrom = '';
$sqlPermissionCheckingWhere = '';
$sqlPermissionTempTables = array();
$groupPermTempTable = false;
$createdStateAliases = array();
if ( is_array( $limitationList ) && count( $limitationList ) > 0 )
{
$sqlParts = array();
foreach( $limitationList as $limitationArray )
{
$sqlPartPart = array();
$sqlPartPartPart = array();
$sqlPlacementPart = array();
foreach ( array_keys( $limitationArray ) as $ident )
{
switch( $ident )
{
case 'Class':
{
$sqlPartPart[] = 'ezcontentobject.contentclass_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
} break;
case 'Section':
case 'User_Section':
{
$sqlPartPart[] = 'ezcontentobject.section_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
} break;
case 'Owner':
{
$user = eZUser::currentUser();
$userID = $user->attribute( 'contentobject_id' );
$sqlPartPart[] = "ezcontentobject.owner_id = '" . $db->escapeString( $userID ) . "'";
} break;
case 'Group':
{
if ( !$groupPermTempTable )
{
$user = eZUser::currentUser();
$userContentObject = $user->attribute( 'contentobject' );
$parentList = $userContentObject->attribute( 'parent_nodes' );
$groupPermTempTable = $db->generateUniqueTempTableName( 'ezgroup_perm_tmp_%' );
$sqlPermissionTempTables[] = $groupPermTempTable;
$db->createTempTable( "CREATE TEMPORARY TABLE $groupPermTempTable ( user_id int NOT NULL PRIMARY KEY )" );
$db->query( "INSERT INTO $groupPermTempTable
SELECT DISTINCT contentobject_id AS user_id
FROM ezcontentobject_tree
WHERE parent_node_id IN (" . implode( ', ', $parentList ) . ')',
eZDBInterface::SERVER_SLAVE );
$sqlPermissionCheckingFrom .= " LEFT JOIN $groupPermTempTable ON $groupPermTempTable.user_id = ezcontentobject.owner_id";
}
$sqlPartPart[] = "ezcontentobject.owner_id = $groupPermTempTable.user_id";
} break;
case 'Node':
{
$sqlPlacementPart[] = $tableAliasName . '.node_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
} break;
case 'Subtree':
{
$sqlSubtreePart = array();
foreach ( $limitationArray[$ident] as $limitationPathString )
{
$sqlSubtreePart[] = "$tableAliasName.path_string like '$limitationPathString%'";
}
$sqlPlacementPart[] = implode( ' OR ', $sqlSubtreePart );
} break;
case 'User_Subtree':
{
$sqlPartUserSubtree = array();
foreach ( $limitationArray[$ident] as $limitationPathString )
{
$sqlPartUserSubtree[] = "$tableAliasName.path_string like '$limitationPathString%'";
}
$sqlPartPart[] = implode( ' OR ', $sqlPartUserSubtree );
} break;
default:
{
if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 )
{
$stateIdentifier = substr( $ident, 11 );
if ( !isset( $createdStateAliases[$stateIdentifier] ) )
{
$stateIndex = count( $createdStateAliases );
}
else
{
$stateIndex = $createdStateAliases[$stateIdentifier];
}
$stateTable = "ezcobj_state_{$stateIndex}_perm";
if ( !isset( $createdStateAliases[$stateIdentifier] ) )
{
$createdStateAliases[$stateIdentifier] = $stateIndex;
$stateLinkTable = "ezcobj_state_lnk_{$stateIndex}_perm";
$stateGroupTable = "ezcobj_state_grp_{$stateIndex}_perm";
$stateAliasTables[$stateIdentifier] = $stateTable;
$sqlPermissionCheckingFrom .=
" INNER JOIN ezcobj_state_link $stateLinkTable ON ($stateLinkTable.contentobject_id = ezcontentobject.id) " .
" INNER JOIN ezcobj_state_group $stateGroupTable ON ($stateGroupTable.identifier = '" . $db->escapeString( $stateIdentifier ) . "') " .
" INNER JOIN ezcobj_state $stateTable ON ($stateTable.id = $stateLinkTable.contentobject_state_id AND $stateTable.group_id = $stateGroupTable.id) ";
}
if ( count( $limitationArray[$ident] ) > 1 )
{
$sqlPartPart[] = $db->generateSQLINStatement( $limitationArray[$ident], "$stateTable.id" );
}
else
{
$sqlPartPart[] = "$stateTable.id = " . $limitationArray[$ident][0];
}
}
}
}
}
if ( $sqlPlacementPart )
{
$sqlPartPart[] = '( ( ' . implode( ' ) OR ( ', $sqlPlacementPart ) . ' ) )';
}
if ( $sqlPartPartPart )
{
$sqlPartPart[] = '( ' . implode( ' ) OR ( ', $sqlPartPartPart ) . ' )';
}
$sqlParts[] = implode( ' AND ', $sqlPartPart );
}
$sqlPermissionCheckingWhere .= ' AND ((' . implode( ") OR (", $sqlParts ) . ')) ';
}
$sqlPermissionChecking = array( 'from' => $sqlPermissionCheckingFrom,
'where' => $sqlPermissionCheckingWhere,
'temp_tables' => $sqlPermissionTempTables );
return $sqlPermissionChecking;
}
/*!
\a static
\param $limit maximum number of nodes in the path to use, starting from last node
*/
static function createNodesConditionSQLStringFromPath( $nodePath, $includingLastNodeInThePath, $limit = false )
{
$pathString = false;
$pathArray = explode( '/', trim( $nodePath, '/' ) );
$pathArrayCount = count( $pathArray );
if ( $limit && $includingLastNodeInThePath == false )
{
$limit++;
}
$sliceOffset = $limit && $pathArrayCount > $limit ? $pathArrayCount - $limit : 0;
$sliceLength = $includingLastNodeInThePath ? $pathArrayCount - $sliceOffset : $pathArrayCount - ( $sliceOffset + 1 );
// only take a slice when necessary
if ( ( $sliceOffset + $sliceLength ) < $pathArrayCount )
{
$pathArray = array_slice( $pathArray, $sliceOffset, $sliceLength );
}
if ( $sliceLength == 1 )
{
$pathString = ' node_id = ' . implode( '', $pathArray ) . ' and ';
}
else if ( $sliceLength > 0 )
{
$db = eZDB::instance();
$pathString = ' ' . $db->generateSQLINStatement( $pathArray, 'node_id' ) . ' and ';
}
return $pathString;
}
/*!
\a static
If \a $useSettings is true \a $fetchHidden will be ignored.
If \a $useSettings is false \a $fetchHidden will be used.
*/
static function createShowInvisibleSQLString( $useSettings, $fetchHidden = true )
{
$showInvisibleNodesCond = '';
$showInvisible = $fetchHidden;
if ( $useSettings )
$showInvisible = eZContentObjectTreeNode::showInvisibleNodes();
if ( !$showInvisible )
$showInvisibleNodesCond = 'AND ezcontentobject_tree.is_invisible = 0';
return $showInvisibleNodesCond;
}
/*!
\a static
\returns true if we should show invisible nodes (determined by ini setting), false otherwise.
*/
static function showInvisibleNodes()
{
static $cachedResult;
if ( !isset( $cachedResult ) )
{
$ini = eZINI::instance( 'site.ini' );
$cachedResult = $ini->hasVariable( 'SiteAccessSettings', 'ShowHiddenNodes' ) ?
$ini->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) == 'true' :
true;
}
return $cachedResult;
}
/*!
\a static
*/
static function getLimitationList( &$limitation )
{
// do not check currentUser if limitation is disabled
if ( empty( $limitation ) && is_array( $limitation ) )
{
return $limitation;
}
$currentUser = eZUser::currentUser();
$currentUserID = $currentUser->attribute( 'contentobject_id' );
$limitationList = array();
if ( $limitation !== false )
{
$limitationList = $limitation;
}
else if ( isset( $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] ) )
{
$limitationList =& $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'];
eZDebugSetting::writeDebug( 'kernel-content-treenode', $limitationList, "limitation list" );
}
else
{
$accessResult = $currentUser->hasAccessTo( 'content', 'read' );
if ( $accessResult['accessWord'] == 'no' )
{
$limitationList = false;
$GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = false;
}
else if ( $accessResult['accessWord'] == 'limited' )
{
$limitationList = $accessResult['policies'];
$GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = $accessResult['policies'];
}
}
return $limitationList;
}
/**
* @param array|bool $params
* @param int $nodeID
* @return array|null
*/
static function subTreeByNodeID( $params = false, $nodeID = 0 )
{
if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
{
return null;
}
if ( $params === false )
{
$params = array( 'Depth' => false,
'Offset' => false,
//'OnlyTranslated' => false,
'Language' => false,
'Limit' => false,
'SortBy' => false,
'AttributeFilter' => false,
'ExtendedAttributeFilter' => false,
'ClassFilterType' => false,
'ClassFilterArray' => false,
'GroupBy' => false );
}
$offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false;
//$onlyTranslated = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated'] : false;
$language = ( isset( $params['Language'] ) ) ? $params['Language'] : false;
$limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false;
$depth = ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) ? $params['Depth'] : false;
$depthOperator = ( isset( $params['DepthOperator'] ) ) ? $params['DepthOperator'] : false;
$asObject = ( isset( $params['AsObject'] ) ) ? $params['AsObject'] : true;
$loadDataMap = ( isset( $params['LoadDataMap'] ) ) ? $params['LoadDataMap'] : false;
$groupBy = ( isset( $params['GroupBy'] ) ) ? $params['GroupBy'] : false;
$mainNodeOnly = ( isset( $params['MainNodeOnly'] ) ) ? $params['MainNodeOnly'] : false;
$ignoreVisibility = ( isset( $params['IgnoreVisibility'] ) ) ? $params['IgnoreVisibility'] : false;
$objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false;
if ( $offset < 0 )
{
$offset = abs( $offset );
}
if ( !isset( $params['SortBy'] ) )
$params['SortBy'] = false;
if ( !isset( $params['ClassFilterType'] ) )
$params['ClassFilterType'] = false;
$allowCustomSorting = false;
if ( isset( $params['ExtendedAttributeFilter'] ) && is_array ( $params['ExtendedAttributeFilter'] ) )
{
$allowCustomSorting = true;
}
$sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $params['SortBy'], 'ezcontentobject_tree', $allowCustomSorting );
$classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] );
if ( $classCondition === false )
{
eZDebug::writeNotice( "Class filter returned false" );
return null;
}
$attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo, $language );
if ( $attributeFilter === false )
{
return null;
}
$extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
$mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
$pathStringCond = '';
$notEqParentString = '';
// If the node(s) doesn't exist we return null.
if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
{
return null;
}
if ( $language )
{
if ( !is_array( $language ) )
{
$language = array( $language );
}
// This call must occur after eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings,
// because the parent node may not exist in Language
eZContentLanguage::setPrioritizedLanguages( $language );
}
$groupBySelectText = '';
$groupBySQL = $extendedAttributeFilter['group_by'];
if ( !$groupBySQL )
{
eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
}
else if ( $groupBy )
{
eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
}
$languageFilter = eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
$objectNameLanguageFilter = eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
if ( $language )
{
eZContentLanguage::clearPrioritizedLanguages();
}
$objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
$limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false;
$limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
$sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
// Determine whether we should show invisible nodes.
$showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
$query = "SELECT DISTINCT " .
"ezcontentobject.contentclass_id, ezcontentobject.current_version, ezcontentobject.id, ezcontentobject.initial_language_id, ezcontentobject.language_mask, " .
"ezcontentobject.modified, ezcontentobject.owner_id, ezcontentobject.published, ezcontentobject.remote_id AS object_remote_id, ezcontentobject.section_id, ezcontentobject.status, " .
"ezcontentobject_tree.contentobject_is_published, ezcontentobject_tree.contentobject_version, ezcontentobject_tree.depth, " .
"ezcontentobject_tree.is_hidden, ezcontentobject_tree.is_invisible, ezcontentobject_tree.main_node_id, ezcontentobject_tree.modified_subnode, ezcontentobject_tree.node_id, " .
"ezcontentobject_tree.parent_node_id, ezcontentobject_tree.path_identification_string, ezcontentobject_tree.path_string, ezcontentobject_tree.priority, " .
"ezcontentobject_tree.remote_id, ezcontentobject_tree.sort_field, ezcontentobject_tree.sort_order, ezcontentclass.serialized_name_list as class_serialized_name_list, " .
"ezcontentclass.identifier as class_identifier, ezcontentclass.is_container as is_container $groupBySelectText, ezcontentobject_name.name, ezcontentobject_name.real_translation " .
$sortingInfo["attributeTargetSQL"] . " " . $extendedAttributeFilter["columns"] . " " .
"FROM " .
"ezcontentobject_tree " .
"INNER JOIN ezcontentobject ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id) " .
"INNER JOIN ezcontentclass ON (ezcontentclass.version = 0 AND ezcontentclass.id = ezcontentobject.contentclass_id) " .
"INNER JOIN ezcontentobject_name ON ( " .
" ezcontentobject_tree.contentobject_id = ezcontentobject_name.contentobject_id AND " .
" ezcontentobject_tree.contentobject_version = ezcontentobject_name.content_version " .
") " .
"$sortingInfo[attributeFromSQL] $attributeFilter[from] $extendedAttributeFilter[tables] $sqlPermissionChecking[from] " .
"WHERE " .
"$pathStringCond " .
"$extendedAttributeFilter[joins] " .
"$sortingInfo[attributeWhereSQL] " .
"$attributeFilter[where] " .
"$notEqParentString " .
"$mainNodeOnlyCond " .
"$classCondition " .
"$objectNameLanguageFilter " .
"$showInvisibleNodesCond " .
"$sqlPermissionChecking[where] " .
"$objectNameFilterSQL AND " .
"$languageFilter " .
$groupBySQL;
if ( $sortingInfo['sortingFields'] )
$query .= " ORDER BY $sortingInfo[sortingFields]";
$db = eZDB::instance();
$server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
$nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
'limit' => $limit ),
$server );
if ( $asObject )
{
$retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray, true, null, $language );
if ( $loadDataMap === true )
eZContentObject::fillNodeListAttributes( $retNodeList );
else if ( $loadDataMap && is_numeric( $loadDataMap ) && $loadDataMap >= count( $retNodeList ) )
eZContentObject::fillNodeListAttributes( $retNodeList );
}
else
{
$retNodeList = $nodeListArray;
}
// cleanup temp tables
$db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
return $retNodeList;
}
/**
* @param array|bool $params
* @return array|null
*/
function subTree( $params = false )
{
return eZContentObjectTreeNode::subTreeByNodeID( $params, $this->attribute( 'node_id' ) );
}
/*!
Retrieve subtrees from multiple paths.
This method composes a list of objects retrieved from various node paths,
sorted by criteria that are globally applied to the whole list.
It is for example useful for an RSS feed that serves content from
several node paths. The respective subtrees need to be amalgated and
the resulting object listed sorted by publishing date to show the latest
entries in chronological order.
The first parameter is a multi-dimensional array containing the
node IDs and filter criteria assigned to each of the nodes:
array(
[node_1] => array(
'ClassFilterType' => [filter_type],
'ClassFilterArray' => [filter_array]
),
[node_2] => array(
'ClassFilterType' => [filter_type],
'ClassFilterArray' => [filter_array]
)
)
The second parameter is a single-dimensional array with criteria
applied to the list of objects retrieved from the various subtrees:
array(
'SortBy' => [sorting-criteria]
)
*/
static function subTreeMultiPaths( $nodesParams, $listParams = NULL )
{
if( !is_array( $nodesParams ) || !count( $nodesParams ) )
{
eZDebug::writeWarning( __METHOD__.': Nodes parameter must be an array with at least one key.' );
return null;
}
if( $listParams === null )
{
$listParams = array(
'SortBy' => false,
'Offset' => false,
'Limit' => false,
'GroupBy' => false );
}
$offset = ( isset( $listParams['Offset'] ) && is_numeric( $listParams['Offset'] ) ) ? $listParams['Offset'] : false;
$limit = ( isset( $listParams['Limit'] ) && is_numeric( $listParams['Limit'] ) ) ? $listParams['Limit'] : false;
$groupBy = ( isset( $listParams['GroupBy'] ) ) ? $listParams['GroupBy'] : false;
if ( !isset( $listParams['SortBy'] ) )
{
$listParams['SortBy'] = false;
}
$sortBy = $listParams['SortBy'];
$queryNodes = '';
foreach( $nodesParams as $nodeParams )
{
$nodeID = $nodeParams['ParentNodeID'];
if ( !is_numeric( $nodeID ) && !is_array( $nodeID ) )
{
eZDebug::writeWarning( __METHOD__.': Nodes parameter must be numeric or an array with numeric values.' );
$retValue = null;
return $retValue;
}
if ( $nodeParams === null )
{
$nodeParams = array(
'Depth' => false,
//'OnlyTranslated' => false,
'Language' => false,
'AttributeFilter' => false,
'ExtendedAttributeFilter' => false,
'ClassFilterType' => false,
'ClassFilterArray' => false );
}
//$onlyTranslated = ( isset( $nodeParams['OnlyTranslated'] ) ) ? $nodeParams['OnlyTranslated'] : false;
$language = ( isset( $nodeParams['Language'] ) ) ? $nodeParams['Language'] : false;
$depth = ( isset( $nodeParams['Depth'] ) && is_numeric( $nodeParams['Depth'] ) ) ? $nodeParams['Depth'] : false;
$depthOperator = ( isset( $nodeParams['DepthOperator'] ) ) ? $nodeParams['DepthOperator'] : false;
$asObject = ( isset( $nodeParams['AsObject'] ) ) ? $nodeParams['AsObject'] : true;
$mainNodeOnly = ( isset( $nodeParams['MainNodeOnly'] ) ) ? $nodeParams['MainNodeOnly'] : false;
$ignoreVisibility = ( isset( $nodeParams['IgnoreVisibility'] ) ) ? $nodeParams['IgnoreVisibility'] : false;
if ( !isset( $nodeParams['ClassFilterType'] ) )
{
$nodeParams['ClassFilterType'] = false;
}
$sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortBy );
$attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $nodeParams['AttributeFilter'], $sortingInfo, $language );
if ( $language )
{
if ( !is_array( $language ) )
{
$language = array( $language );
}
eZContentLanguage::setPrioritizedLanguages( $language );
}
$classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $nodeParams['ClassFilterType'], $nodeParams['ClassFilterArray'] );
$extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $nodeParams['ExtendedAttributeFilter'] );
$mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
$pathStringCond = '';
$notEqParentString = '';
// If the node(s) doesn't exist we return null.
if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
{
$retValue = null;
return $retValue;
}
$languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
if ( $language )
{
eZContentLanguage::clearPrioritizedLanguages();
}
$limitation = ( isset( $nodeParams['Limitation'] ) && is_array( $nodeParams['Limitation'] ) ) ? $nodeParams['Limitation']: false;
$limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
$sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
// Determine whether we should show invisible nodes.
$showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
$queryNodes .= " (
$pathStringCond
$extendedAttributeFilter[joins]
$sortingInfo[attributeWhereSQL]
$attributeFilter[where]
ezcontentclass.version=0 AND
$notEqParentString
$mainNodeOnlyCond
$classCondition
" . eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ) . "
$showInvisibleNodesCond
$sqlPermissionChecking[where]
$languageFilter
)
OR";
}
$groupBySelectText = '';
$groupBySQL = $extendedAttributeFilter['group_by'];
if ( !$groupBySQL )
{
eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
}
else if ( $groupBy )
{
eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
}
$query = "SELECT DISTINCT " .
"ezcontentobject.contentclass_id, ezcontentobject.current_version, ezcontentobject.id, ezcontentobject.initial_language_id, ezcontentobject.language_mask, " .
"ezcontentobject.modified, ezcontentobject.owner_id, ezcontentobject.published, ezcontentobject.remote_id AS object_remote_id, " .
"ezcontentobject.section_id, ezcontentobject.status, ezcontentobject_tree.contentobject_is_published, ezcontentobject_tree.contentobject_version, " .
"ezcontentobject_tree.depth, ezcontentobject_tree.is_hidden, ezcontentobject_tree.is_invisible, ezcontentobject_tree.main_node_id, ezcontentobject_tree.modified_subnode, " .
"ezcontentobject_tree.node_id, ezcontentobject_tree.parent_node_id, ezcontentobject_tree.path_identification_string, ezcontentobject_tree.path_string, " .
"ezcontentobject_tree.priority, ezcontentobject_tree.remote_id, ezcontentobject_tree.sort_field, ezcontentobject_tree.sort_order, ezcontentclass.serialized_name_list as class_serialized_name_list, " .
"ezcontentclass.identifier as class_identifier, ezcontentclass.is_container $groupBySelectText, ezcontentobject_name.name, ezcontentobject_name.real_translation " .
"$sortingInfo[attributeTargetSQL], $nodeParams[ResultID] AS resultid " .
"FROM ezcontentobject_tree " .
"INNER JOIN ezcontentobject ON (ezcontentobject.id = ezcontentobject_tree.contentobject_id) " .
"INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id) " .
"INNER JOIN ezcontentobject_name ON ( " .
" ezcontentobject_name.contentobject_id = ezcontentobject_tree.contentobject_id AND " .
" ezcontentobject_name.content_version = ezcontentobject_tree.contentobject_version " .
") " .
"$sortingInfo[attributeFromSQL] " .
"$attributeFilter[from] " .
"$extendedAttributeFilter[tables] " .
"$sqlPermissionChecking[from] " .
"WHERE " .
substr( $queryNodes, 0, -2 ) . " " .
$groupBySQL;
if ( $sortingInfo['sortingFields'] )
{
$query .= " ORDER BY $sortingInfo[sortingFields]";
}
$db = eZDB::instance();
$server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
if ( !$offset && !$limit )
{
$nodeListArray = $db->arrayQuery( $query, array(), $server );
}
else
{
$nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
'limit' => $limit ),
$server );
}
if ( $asObject )
{
$retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray, true, null, $language );
}
else
{
$retNodeList = $nodeListArray;
}
// cleanup temp tables
$db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
return $retNodeList;
}
static function subTreeGroupByDateField( $field, $type )
{
$divisor = 0;
switch ( $type )
{
case 'year':
{
$divisor = 60*60*24*365;
} break;
case 'week':
{
$divisor = 60*60*24*7;
} break;
case 'day':
{
$divisor = 60*60*24;
} break;
case 'hour':
{
$divisor = 60*60;
} break;
case 'minute':
{
$divisor = 60;
} break;
case 'second':
{
$divisor = 0;
} break;
default:
{
eZDebug::writeError( "Unknown field type $type", __METHOD__ );
}
}
if ( $divisor > 0 )
$text = "( $field / $divisor ) AS groupbyfield";
else
$text = "$field AS groupbyfield";
return array( 'select' => $text,
'group_field' => "( $field / $divisor )" );
}
/*!
\sa subTreeCount
*/
static function subTreeCountByNodeID( $params = array(), $nodeID )
{
if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
{
return null;
}
$language = ( isset( $params['Language'] ) ) ? $params['Language'] : false;
if ( $language )
{
if ( !is_array( $language ) )
{
$language = array( $language );
}
eZContentLanguage::setPrioritizedLanguages( $language );
}
$depth = isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ? $params['Depth'] : false;
$depthOperator = isset( $params['DepthOperator'] ) ? $params['DepthOperator'] : false;
$pathStringCond = '';
$notEqParentString = '';
// If the node(s) doesn't exist we return null.
if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
{
return null;
}
$db = eZDB::instance();
$ini = eZINI::instance();
// Check for class filtering
$classCondition = '';
if ( isset( $params['ClassFilterType'] ) and isset( $params['ClassFilterArray'] ) and
( $params['ClassFilterType'] == 'include' or $params['ClassFilterType'] == 'exclude' )
and count( $params['ClassFilterArray'] ) > 0 )
{
$classCondition = ' ';
$i = 0;
$classCount = count( $params['ClassFilterArray'] );
$classIDArray = array();
foreach ( $params['ClassFilterArray'] as $classID )
{
$originalClassID = $classID;
// Check if classes are recerenced by identifier
if ( is_string( $classID ) && !is_numeric( $classID ) )
{
$classID = eZContentClass::classIDByIdentifier( $classID );
}
if ( is_numeric( $classID ) )
{
$classIDArray[] = $classID;
}
else
{
eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
}
}
if ( count( $classIDArray ) > 0 )
{
$classCondition .= " ezcontentobject.contentclass_id ";
if ( $params['ClassFilterType'] == 'include' )
$classCondition .= " IN ";
else
$classCondition .= " NOT IN ";
$classIDString = implode( ', ', $classIDArray );
$classCondition .= ' ( ' . $classIDString . ' ) AND';
}
}
// Main node check
$mainNodeOnlyCond = '';
if ( isset( $params['MainNodeOnly'] ) && $params['MainNodeOnly'] === true )
{
$mainNodeOnlyCond = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
}
$languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
$objectNameLanguageFilter = eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
if ( $language )
{
eZContentLanguage::clearPrioritizedLanguages();
}
$objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false;
$attributeFilterParam = isset( $params['AttributeFilter'] ) ? $params['AttributeFilter'] : false;
$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 );
$attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $attributeFilterParam, $sortingInfo, $language );
if ( $attributeFilter === false )
{
return null;
}
$objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
$extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
// Determine whether we should show invisible nodes.
$ignoreVisibility = isset( $params['IgnoreVisibility'] ) ? $params['IgnoreVisibility'] : false;
$showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
$limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false;
$limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
$sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
$query = "SELECT
count( DISTINCT ezcontentobject_tree.node_id ) as count
FROM
ezcontentobject_tree
INNER JOIN ezcontentobject ON (ezcontentobject.id = ezcontentobject_tree.contentobject_id)
INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id)
INNER JOIN ezcontentobject_name ON (
ezcontentobject_name.contentobject_id = ezcontentobject_tree.contentobject_id AND
ezcontentobject_name.content_version = ezcontentobject_tree.contentobject_version
)
$attributeFilter[from]
$extendedAttributeFilter[tables]
$sqlPermissionChecking[from]
WHERE $pathStringCond
$extendedAttributeFilter[joins]
$mainNodeOnlyCond
$classCondition
$attributeFilter[where]
ezcontentclass.version=0 AND
$notEqParentString
$objectNameLanguageFilter
$showInvisibleNodesCond
$sqlPermissionChecking[where]
$objectNameFilterSQL
$languageFilter ";
$server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
$nodeListArray = $db->arrayQuery( $query, array(), $server );
// cleanup temp tables
$db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
return $nodeListArray[0]['count'];
}
/*!
Count number of subnodes
\param params array
*/
function subTreeCount( $params = array() )
{
return eZContentObjectTreeNode::subTreeCountByNodeID( $params, $this->attribute( 'node_id' ) );
}
/*!
\return The date/time list when object were published
*/
static function calendar( $params = false, $nodeID = 0 )
{
if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
{
return array();
}
if ( $params === false )
{
$params = array( 'Depth' => false,
'Offset' => false,
'Limit' => false,
'AttributeFilter' => false,
'ExtendedAttributeFilter' => false,
'ClassFilterType' => false,
'ClassFilterArray' => false,
'GroupBy' => false );
}
$offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false;
$limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false;
$depth = ( isset( $params['Depth'] ) && is_numeric