|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZContentObjectTreeNode class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package kernel 00009 */ 00010 00011 /*! 00012 \class eZContentObjectTreeNode ezcontentobjecttreenode.php 00013 \brief The class eZContentObjectTreeNode does 00014 00015 \verbatim 00016 00017 Some algorithms 00018 ---------- 00019 1. Adding new Node 00020 Enter 1 - parent_node 00021 2 - contentobject_id, ( that is like a node value ) 00022 00023 (a) - get path_string, depth for parent node to built path_string and to count depth for new one 00024 (c) - calculating attributes for new node and inserting it 00025 Returns node_id for added node 00026 00027 00028 2. Deleting node ( or subtree ) 00029 Enter - node_id 00030 00031 3. Move subtree in tree 00032 Enter node_id,new_parent_id 00033 00034 00035 4. fetching subtree 00036 00037 \endverbatim 00038 00039 */ 00040 00041 class eZContentObjectTreeNode extends eZPersistentObject 00042 { 00043 const SORT_FIELD_PATH = 1; 00044 const SORT_FIELD_PUBLISHED = 2; 00045 const SORT_FIELD_MODIFIED = 3; 00046 const SORT_FIELD_SECTION = 4; 00047 const SORT_FIELD_DEPTH = 5; 00048 const SORT_FIELD_CLASS_IDENTIFIER = 6; 00049 const SORT_FIELD_CLASS_NAME = 7; 00050 const SORT_FIELD_PRIORITY = 8; 00051 const SORT_FIELD_NAME = 9; 00052 const SORT_FIELD_MODIFIED_SUBNODE = 10; 00053 const SORT_FIELD_NODE_ID = 11; 00054 const SORT_FIELD_CONTENTOBJECT_ID = 12; 00055 00056 const SORT_ORDER_DESC = 0; 00057 const SORT_ORDER_ASC = 1; 00058 00059 /*! 00060 Constructor 00061 */ 00062 function eZContentObjectTreeNode( $row = array() ) 00063 { 00064 $this->eZPersistentObject( $row ); 00065 } 00066 00067 static function definition() 00068 { 00069 static $definition = array( "fields" => array( "node_id" => array( 'name' => "NodeID", 00070 'datatype' => 'integer', 00071 'default' => 0, 00072 'required' => true ), 00073 "parent_node_id" => array( 'name' => "ParentNodeID", 00074 'datatype' => 'integer', 00075 'default' => 0, 00076 'required' => true, 00077 'foreign_class' => 'eZContentObjectTreeNode', 00078 'foreign_attribute' => 'node_id', 00079 'multiplicity' => '1..*' ), 00080 "main_node_id" => array( 'name' => "MainNodeID", 00081 'datatype' => 'integer', 00082 'default' => 0, 00083 'required' => true, 00084 'foreign_class' => 'eZContentObjectTreeNode', 00085 'foreign_attribute' => 'node_id', 00086 'multiplicity' => '1..*' ), 00087 "contentobject_id" => array( 'name' => "ContentObjectID", 00088 'datatype' => 'integer', 00089 'default' => 0, 00090 'required' => true, 00091 'foreign_class' => 'eZContentObject', 00092 'foreign_attribute' => 'id', 00093 'multiplicity' => '1..*' ), 00094 'contentobject_version' => array( 'name' => 'ContentObjectVersion', 00095 'datatype' => 'integer', 00096 'default' => 0, 00097 'required' => true ), 00098 'contentobject_is_published' => array( 'name' => 'ContentObjectIsPublished', 00099 'datatype' => 'integer', 00100 'default' => 0, 00101 'required' => true ), 00102 "depth" => array( 'name' => "Depth", 00103 'datatype' => 'integer', 00104 'default' => 0, 00105 'required' => true ), 00106 'sort_field' => array( 'name' => 'SortField', 00107 'datatype' => 'integer', 00108 'default' => self::SORT_FIELD_PATH, 00109 'required' => true ), 00110 'sort_order' => array( 'name' => 'SortOrder', 00111 'datatype' => 'integer', 00112 'default' => self::SORT_ORDER_ASC, 00113 'required' => true ), 00114 'priority' => array( 'name' => 'Priority', 00115 'datatype' => 'integer', 00116 'default' => 0, 00117 'required' => true ), 00118 'modified_subnode' => array( 'name' => 'ModifiedSubNode', 00119 'datatype' => 'integer', 00120 'default' => 0, 00121 'required' => true ), 00122 "path_string" => array( 'name' => "PathString", 00123 'datatype' => 'string', 00124 'default' => '', 00125 'required' => true ), 00126 "path_identification_string" => array( 'name' => "PathIdentificationString", 00127 'datatype' => 'text', 00128 'default' => '', 00129 'required' => true ), 00130 'remote_id' => array( 'name' => 'RemoteID', 00131 'datatype' => 'string', 00132 'default' => '', 00133 'required' => true ), 00134 "is_hidden" => array( 'name' => "IsHidden", 00135 'datatype' => 'integer', 00136 'default' => 0, 00137 'required' => true ), 00138 "is_invisible" => array( 'name' => "IsInvisible", 00139 'datatype' => 'integer', 00140 'default' => 0, 00141 'required' => true ) ), 00142 "keys" => array( "node_id" ), 00143 "function_attributes" => array( "name" => "getName", 00144 'data_map' => 'dataMap', 00145 'remote_id' => 'remoteID', // Note: This overrides remote_id field 00146 "object" => "object", 00147 "subtree" => "subTree", 00148 "children" => "children", 00149 "children_count" => "childrenCount", 00150 'view_count' => 'viewCount', 00151 'contentobject_version_object' => 'contentObjectVersionObject', 00152 'sort_array' => 'sortArray', 00153 'can_read' => 'canRead', 00154 'can_pdf' => 'canPdf', 00155 'can_create' => 'canCreate', 00156 'can_edit' => 'canEdit', 00157 'can_hide' => 'canHide', 00158 'can_remove' => 'canRemove', 00159 'can_move' => 'canMoveFrom', 00160 'can_move_from' => 'canMoveFrom', 00161 'can_add_location' => 'canAddLocation', 00162 'can_remove_location' => 'canRemoveLocation', 00163 'can_view_embed' => 'canViewEmbed', 00164 'is_main' => 'isMain', 00165 'creator' => 'creator', 00166 "path_with_names" => "pathWithNames", 00167 "path" => "fetchPath", 00168 'path_array' => 'pathArray', 00169 "parent" => "fetchParent", 00170 'url' => 'url', 00171 'url_alias' => 'urlAlias', 00172 'class_identifier' => 'classIdentifier', 00173 'class_name' => 'className', 00174 'hidden_invisible_string' => 'hiddenInvisibleString', 00175 'hidden_status_string' => 'hiddenStatusString', 00176 'classes_js_array' => 'availableClassesJsArray', 00177 'is_container' => 'classIsContainer' ), 00178 "increment_key" => "node_id", 00179 "class_name" => "eZContentObjectTreeNode", 00180 "name" => "ezcontentobject_tree" ); 00181 return $definition; 00182 } 00183 00184 /*! 00185 Creates a new tree node and returns it. 00186 \param $parentNodeID The ID of the parent or \c null if the node is not known yet. 00187 \param $contentObjectID The ID of the object it points to or \c null if it is not known yet. 00188 \param $contentObjectVersion The version of the object or \c 0 if not known yet. 00189 \param $sortField Number describing the field to sort by, or \c 0 if not known yet. 00190 \param $sortOrder Which way to sort, \c true means ascending while \c false is descending. 00191 \note The attribute \c remote_id will get an automatic and unique value. 00192 */ 00193 static function create( $parentNodeID = null, $contentObjectID = null, $contentObjectVersion = 0, 00194 $sortField = 0, $sortOrder = true ) 00195 { 00196 $row = array( 'node_id' => null, 00197 'main_node_id' => null, 00198 'parent_node_id' => $parentNodeID, 00199 'contentobject_id' => $contentObjectID, 00200 'contentobject_version' => $contentObjectVersion, 00201 'contentobject_is_published' => false, 00202 'depth' => 1, 00203 'path_string' => null, 00204 'path_identification_string' => null, 00205 'is_hidden' => false, 00206 'is_invisible' => false, 00207 'sort_field' => $sortField, 00208 'sort_order' => $sortOrder, 00209 'modified_subnode' => 0, 00210 'remote_id' => eZRemoteIdUtility::generate( 'node' ), 00211 'priority' => 0 ); 00212 $node = new eZContentObjectTreeNode( $row ); 00213 return $node; 00214 } 00215 00216 /*! 00217 \return a map with all the content object attributes where the keys are the 00218 attribute identifiers. 00219 \sa eZContentObject::fetchDataMap 00220 */ 00221 function dataMap() 00222 { 00223 return $this->object()->fetchDataMap( $this->attribute( 'contentobject_version' ) ); 00224 } 00225 00226 /*! 00227 Get remote id of content node, the remote ID is often used to synchronise imports and exports. 00228 If there is no remote ID a new unique one will be generated. 00229 */ 00230 function remoteID() 00231 { 00232 $remoteID = eZPersistentObject::attribute( 'remote_id', true ); 00233 if ( !$remoteID ) 00234 { 00235 $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'node' ) ); 00236 $this->sync( array( 'remote_id' ) ); 00237 $remoteID = eZPersistentObject::attribute( 'remote_id', true ); 00238 } 00239 00240 return $remoteID; 00241 } 00242 00243 /*! 00244 \return true if this node is the main node. 00245 */ 00246 function isMain() 00247 { 00248 return $this->NodeID == $this->MainNodeID; 00249 } 00250 00251 /*! 00252 \return the ID of the class attribute with the given ID. 00253 False is returned if no class/attribute by that identifier is found. 00254 If multiple classes have the same identifier, the first found is returned. 00255 */ 00256 static function classAttributeIDByIdentifier( $identifier ) 00257 { 00258 return eZContentClassAttribute::classAttributeIDByIdentifier( $identifier ); 00259 } 00260 00261 /*! 00262 \return the ID of the class with the given ID. 00263 False is returned if no class by that identifier is found. 00264 If multiple classes have the same identifier, the first found is returned. 00265 */ 00266 static function classIDByIdentifier( $identifier ) 00267 { 00268 return eZContentClass::classIDByIdentifier( $identifier ); 00269 } 00270 00271 /*! 00272 \return \c true if the node can be read by the current user. 00273 \sa checkAccess(). 00274 */ 00275 function canRead( ) 00276 { 00277 if ( !isset( $this->Permissions["can_read"] ) ) 00278 { 00279 $this->Permissions["can_read"] = $this->checkAccess( 'read' ); 00280 } 00281 return ( $this->Permissions["can_read"] == 1 ); 00282 } 00283 00284 /*! 00285 \return \c true if the current user can create a pdf of this content object. 00286 */ 00287 function canPdf( ) 00288 { 00289 if ( !isset( $this->Permissions["can_pdf"] ) ) 00290 { 00291 $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' ); 00292 } 00293 return ( $this->Permissions["can_pdf"] == 1 ); 00294 } 00295 00296 00297 /*! 00298 \return \c true if the node can be viewed as embeded object by the current user. 00299 \sa checkAccess(). 00300 */ 00301 function canViewEmbed( ) 00302 { 00303 if ( !isset( $this->Permissions["can_view_embed"] ) ) 00304 { 00305 $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' ); 00306 } 00307 return ( $this->Permissions["can_view_embed"] == 1 ); 00308 } 00309 00310 /*! 00311 \return \c true if the node can be edited by the current user. 00312 \sa checkAccess(). 00313 */ 00314 function canEdit( ) 00315 { 00316 if ( !isset( $this->Permissions["can_edit"] ) ) 00317 { 00318 $this->Permissions["can_edit"] = $this->checkAccess( 'edit' ); 00319 if ( $this->Permissions["can_edit"] != 1 ) 00320 { 00321 $user = eZUser::currentUser(); 00322 if ( $user->id() == $this->ContentObject->attribute( 'id' ) ) 00323 { 00324 $access = $user->hasAccessTo( 'user', 'selfedit' ); 00325 if ( $access['accessWord'] == 'yes' ) 00326 { 00327 $this->Permissions["can_edit"] = 1; 00328 } 00329 } 00330 } 00331 } 00332 return ( $this->Permissions["can_edit"] == 1 ); 00333 } 00334 00335 /*! 00336 \return \c true if the node can be hidden by the current user. 00337 \sa checkAccess(). 00338 */ 00339 function canHide( ) 00340 { 00341 if ( !isset( $this->Permissions["can_hide"] ) ) 00342 { 00343 $this->Permissions["can_hide"] = $this->checkAccess( 'hide' ); 00344 } 00345 return ( $this->Permissions["can_hide"] == 1 ); 00346 } 00347 00348 /*! 00349 \return \c true if the current user can create a new node as child of this node. 00350 \sa checkAccess(). 00351 */ 00352 function canCreate( ) 00353 { 00354 if ( !isset( $this->Permissions["can_create"] ) ) 00355 { 00356 $this->Permissions["can_create"] = $this->checkAccess( 'create' ); 00357 } 00358 return ( $this->Permissions["can_create"] == 1 ); 00359 } 00360 00361 /*! 00362 \return \c true if the node can be removed by the current user. 00363 \sa checkAccess(). 00364 */ 00365 function canRemove( ) 00366 { 00367 if ( !isset( $this->Permissions["can_remove"] ) ) 00368 { 00369 $this->Permissions["can_remove"] = $this->checkAccess( 'remove' ); 00370 } 00371 return ( $this->Permissions["can_remove"] == 1 ); 00372 } 00373 00374 /*! 00375 Check if the node can be moved. (actually checks 'edit' and 'remove' permissions) 00376 \return \c true if the node can be moved by the current user. 00377 \sa checkAccess(). 00378 \deprecated The function canMove() is preferred since its naming is clearer. 00379 */ 00380 function canMove() 00381 { 00382 return $this->canMoveFrom(); 00383 } 00384 00385 /*! 00386 Check if the node can be moved. (actually checks 'edit' and 'remove' permissions) 00387 \return \c true if the node can be moved by the current user. 00388 \sa checkAccess(). 00389 */ 00390 function canMoveFrom( ) 00391 { 00392 if ( !isset( $this->Permissions['can_move_from'] ) ) 00393 { 00394 $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' ); 00395 } 00396 return ( $this->Permissions['can_move_from'] == 1 ); 00397 } 00398 00399 /*! 00400 \return \c true if a node of class \a $classID can be moved to the current node by the current user. 00401 \sa checkAccess(). 00402 */ 00403 function canMoveTo( $classID = false ) 00404 { 00405 if ( !isset( $this->Permissions['can_move_to'] ) ) 00406 { 00407 $this->Permissions['can_move_to'] = $this->checkAccess( 'create', $classID ); 00408 } 00409 return ( $this->Permissions['can_move_to'] == 1 ); 00410 } 00411 00412 /*! 00413 \return \c true if a node can be swaped by the current user. 00414 \sa checkAccess(). 00415 */ 00416 function canSwap() 00417 { 00418 if ( !isset( $this->Permissions['can_swap'] ) ) 00419 { 00420 $this->Permissions['can_swap'] = $this->checkAccess( 'edit' ); 00421 } 00422 return ( $this->Permissions['can_swap'] == 1 ); 00423 } 00424 00425 /*! 00426 \return \c true if current user can add object locations to current node. 00427 \sa checkAccess() 00428 */ 00429 function canAddLocation() 00430 { 00431 if ( !isset( $this->Permissions['can_add_location'] ) ) 00432 { 00433 $this->Permissions['can_add_location'] = $this->checkAccess( 'can_add_location' ); 00434 } 00435 return ( $this->Permissions['can_add_location'] == 1 ); 00436 } 00437 00438 /*! 00439 \return \c true if current user can add object locations to current node. 00440 */ 00441 function canRemoveLocation() 00442 { 00443 if ( !isset( $this->Permissions['can_remove_location'] ) ) 00444 { 00445 $this->Permissions['can_remove_location'] = $this->checkAccess( 'can_remove_location' ); 00446 } 00447 return ( $this->Permissions['can_remove_location'] == 1 ); 00448 } 00449 00450 /*! 00451 \static 00452 \returns the sort key for the given classAttributeID. 00453 int|string is returend. False is returned if unsuccessful. 00454 */ 00455 static function sortKeyByClassAttributeID( $classAttributeID ) 00456 { 00457 return eZContentClassAttribute::sortKeyTypeByID( $classAttributeID ); 00458 } 00459 00460 /*! 00461 \static 00462 */ 00463 static function dataTypeByClassAttributeID( $classAttributeID ) 00464 { 00465 return eZContentClassAttribute::dataTypeByID( $classAttributeID ); 00466 } 00467 00468 00469 /*! 00470 Fetches the number of nodes which exists in the system. 00471 */ 00472 static function fetchListCount() 00473 { 00474 $sql = "SELECT count( node_id ) as count FROM ezcontentobject_tree"; 00475 $db = eZDB::instance(); 00476 $rows = $db->arrayQuery( $sql ); 00477 return $rows[0]['count']; 00478 } 00479 00480 00481 /*! 00482 Fetches a list of nodes and returns it. Offset and limitation can be set if needed. 00483 */ 00484 static function fetchList( $asObject = true, $offset = false, $limit = false ) 00485 { 00486 $sql = "SELECT * FROM ezcontentobject_tree"; 00487 $parameters = array(); 00488 if ( $offset !== false ) 00489 $parameters['offset'] = $offset; 00490 if ( $limit !== false ) 00491 $parameters['limit'] = $limit; 00492 $db = eZDB::instance(); 00493 $rows = $db->arrayQuery( $sql, $parameters ); 00494 $nodes = array(); 00495 if ( $asObject ) 00496 { 00497 foreach ( $rows as $row ) 00498 { 00499 $nodes[] = new eZContentObjectTreeNode( $row ); 00500 } 00501 return $nodes; 00502 } 00503 else 00504 return $rows; 00505 } 00506 00507 /*! 00508 \a static 00509 */ 00510 static function createSortingSQLStrings( $sortList, $treeTableName = 'ezcontentobject_tree', $allowCustomColumns = false ) 00511 { 00512 $sortingInfo = array( 'sortCount' => 0, 00513 'sortingFields' => " path_string ASC", 00514 'attributeJoinCount' => 0, 00515 'attributeFromSQL' => "", 00516 'attributeTargetSQL' => "", 00517 'attributeWhereSQL' => "" ); 00518 00519 if ( $sortList and is_array( $sortList ) and count( $sortList ) > 0 ) 00520 { 00521 if ( count( $sortList ) > 1 and !is_array( $sortList[0] ) ) 00522 { 00523 $sortList = array( $sortList ); 00524 } 00525 00526 $sortingFields = ''; 00527 $sortCount = 0; 00528 $attributeJoinCount = 0; 00529 $stateJoinCount = 0; 00530 $attributeFromSQL = ""; 00531 $attributeWhereSQL = ""; 00532 $datatypeSortingTargetSQL = ""; 00533 00534 foreach ( $sortList as $sortBy ) 00535 { 00536 if ( is_array( $sortBy ) and count( $sortBy ) > 0 ) 00537 { 00538 if ( $sortCount > 0 ) 00539 { 00540 $sortingFields .= ', '; 00541 } 00542 00543 $sortField = $sortBy[0]; 00544 switch ( $sortField ) 00545 { 00546 case 'path': 00547 { 00548 $sortingFields .= 'path_string'; 00549 } break; 00550 case 'path_string': 00551 { 00552 $sortingFields .= 'path_identification_string'; 00553 } break; 00554 case 'published': 00555 { 00556 $sortingFields .= 'ezcontentobject.published'; 00557 } break; 00558 case 'modified': 00559 { 00560 $sortingFields .= 'ezcontentobject.modified'; 00561 } break; 00562 case 'modified_subnode': 00563 { 00564 $sortingFields .= 'modified_subnode'; 00565 } break; 00566 case 'section': 00567 { 00568 $sortingFields .= 'ezcontentobject.section_id'; 00569 } break; 00570 case 'node_id': 00571 { 00572 $sortingFields .= $treeTableName . '.node_id'; 00573 } break; 00574 case 'contentobject_id': 00575 { 00576 $sortingFields .= $treeTableName . '.contentobject_id'; 00577 } break; 00578 case 'depth': 00579 { 00580 $sortingFields .= 'depth'; 00581 } break; 00582 case 'class_identifier': 00583 { 00584 $sortingFields .= 'ezcontentclass.identifier'; 00585 } break; 00586 case 'class_name': 00587 { 00588 $classNameFilter = eZContentClassName::sqlFilter(); 00589 $sortingFields .= 'contentclass_name'; 00590 $datatypeSortingTargetSQL .= ", $classNameFilter[nameField] AS contentclass_name"; 00591 $attributeFromSQL .= ", $classNameFilter[from]"; 00592 $attributeWhereSQL .= "$classNameFilter[where] AND "; 00593 } break; 00594 case 'priority': 00595 { 00596 $sortingFields .= $treeTableName . '.priority'; 00597 } break; 00598 case 'name': 00599 { 00600 $sortingFields .= 'ezcontentobject_name.name'; 00601 } break; 00602 case 'attribute': 00603 { 00604 $classAttributeID = $sortBy[2]; 00605 if ( !is_numeric( $classAttributeID ) ) 00606 $classAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $classAttributeID ); 00607 00608 00609 $contentAttributeTableAlias = "a$attributeJoinCount"; 00610 $datatypeFromSQL = "ezcontentobject_attribute $contentAttributeTableAlias"; 00611 $datatypeWhereSQL = " 00612 $contentAttributeTableAlias.contentobject_id = ezcontentobject.id AND 00613 $contentAttributeTableAlias.contentclassattribute_id = $classAttributeID AND 00614 $contentAttributeTableAlias.version = ezcontentobject_name.content_version AND"; 00615 $datatypeWhereSQL .= eZContentLanguage::sqlFilter( $contentAttributeTableAlias, 'ezcontentobject' ); 00616 00617 $dataType = eZDataType::create( eZContentObjectTreeNode::dataTypeByClassAttributeID( $classAttributeID ) ); 00618 if( is_object( $dataType ) && $dataType->customSorting() ) 00619 { 00620 $params = array(); 00621 $params['contentobject_attr_id'] = "$contentAttributeTableAlias.id"; 00622 $params['contentobject_attr_version'] = "$contentAttributeTableAlias.version"; 00623 $params['table_alias_suffix'] = "$attributeJoinCount"; 00624 00625 $sql = $dataType->customSortingSQL( $params ); 00626 00627 $datatypeFromSQL .= ", {$sql['from']}"; 00628 $datatypeWhereSQL .= " AND {$sql['where']}"; 00629 $datatypeSortingFieldSQL = $sql['sorting_field']; 00630 $datatypeSortingTargetSQL .= ', ' . $sql['sorting_field']; 00631 } 00632 else 00633 { 00634 // Look up datatype for standard sorting 00635 $sortKeyType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $classAttributeID ); 00636 switch ( $sortKeyType ) 00637 { 00638 case 'string': 00639 { 00640 $sortKey = 'sort_key_string'; 00641 } break; 00642 00643 case 'int': 00644 default: 00645 { 00646 $sortKey = 'sort_key_int'; 00647 } break; 00648 } 00649 00650 $datatypeSortingFieldSQL = "a$attributeJoinCount.$sortKey"; 00651 $datatypeSortingTargetSQL .= ', ' . $datatypeSortingFieldSQL; 00652 } 00653 00654 $sortingFields .= "$datatypeSortingFieldSQL"; 00655 $attributeFromSQL .= ", $datatypeFromSQL"; 00656 $attributeWhereSQL .= "$datatypeWhereSQL AND "; 00657 00658 $attributeJoinCount++; 00659 }break; 00660 00661 case 'state': 00662 { 00663 $stateGroupID = $sortBy[2]; 00664 if ( !is_numeric( $stateGroupID ) ) 00665 { 00666 $stateGroup = eZContentObjectStateGroup::fetchByIdentifier( $stateGroupID ); 00667 if ( $stateGroup ) 00668 { 00669 $stateGroupID = $stateGroup->attribute( 'id' ); 00670 } 00671 else 00672 { 00673 eZDebug::writeError( "Unknown content object state group '$stateGroupID'" ); 00674 continue 2; 00675 } 00676 } 00677 00678 $stateAlias = "s$stateJoinCount"; 00679 $stateLinkAlias = "sl$stateJoinCount"; 00680 $sortingFields .= "$stateAlias.priority"; 00681 $datatypeSortingTargetSQL .= ", $stateAlias.priority"; 00682 $attributeFromSQL .= ", ezcobj_state $stateAlias, ezcobj_state_link $stateLinkAlias"; 00683 $attributeWhereSQL .= "$stateLinkAlias.contentobject_id=$treeTableName.contentobject_id AND 00684 $stateLinkAlias.contentobject_state_id=$stateAlias.id AND 00685 $stateAlias.group_id=$stateGroupID AND "; 00686 } break; 00687 00688 default: 00689 { 00690 if ( $allowCustomColumns ) 00691 { 00692 $sortingFields .= $sortField; 00693 } 00694 else 00695 { 00696 eZDebug::writeWarning( 'Unknown sort field: ' . $sortField, __METHOD__ ); 00697 continue; 00698 } 00699 } 00700 } 00701 $sortOrder = true; // true is ascending 00702 if ( isset( $sortBy[1] ) ) 00703 $sortOrder = $sortBy[1]; 00704 $sortingFields .= $sortOrder ? " ASC" : " DESC"; 00705 ++$sortCount; 00706 } 00707 } 00708 00709 $sortingInfo['sortCount'] = $sortCount; 00710 $sortingInfo['sortingFields'] = $sortingFields; 00711 $sortingInfo['attributeTargetSQL'] = $datatypeSortingTargetSQL; 00712 $sortingInfo['attributeJoinCount'] = $attributeJoinCount; 00713 $sortingInfo['attributeFromSQL'] = $attributeFromSQL; 00714 $sortingInfo['attributeWhereSQL'] = $attributeWhereSQL; 00715 } 00716 else if ( $sortList === array() ) 00717 { 00718 $sortingInfo['sortingFields'] = ''; 00719 } 00720 00721 return $sortingInfo; 00722 } 00723 00724 /*! 00725 \a static 00726 */ 00727 static function createClassFilteringSQLString( $classFilterType, &$classFilterArray ) 00728 { 00729 // Check for class filtering 00730 $classCondition = ''; 00731 00732 if ( isset( $classFilterType ) && 00733 ( $classFilterType == 'include' || $classFilterType == 'exclude' ) && 00734 count( $classFilterArray ) > 0 ) 00735 { 00736 $classCondition = ' '; 00737 $i = 0; 00738 $classCount = count( $classFilterArray ); 00739 $classIDArray = array(); 00740 foreach ( $classFilterArray as $classID ) 00741 { 00742 $originalClassID = $classID; 00743 // Check if classes are recerenced by identifier 00744 if ( is_string( $classID ) && !is_numeric( $classID ) ) 00745 { 00746 $classID = eZContentClass::classIDByIdentifier( $classID ); 00747 } 00748 if ( is_numeric( $classID ) ) 00749 { 00750 $classIDArray[] = $classID; 00751 } 00752 else 00753 { 00754 eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID ); 00755 } 00756 } 00757 00758 if ( count( $classIDArray ) > 0 ) 00759 { 00760 $classCondition .= " ezcontentobject.contentclass_id "; 00761 if ( $classFilterType == 'include' ) 00762 $classCondition .= " IN "; 00763 else 00764 $classCondition .= " NOT IN "; 00765 00766 $classIDString = implode( ', ', $classIDArray ); 00767 $classCondition .= ' ( ' . $classIDString . ' ) AND'; 00768 } 00769 else 00770 { 00771 if ( count( $classIDArray ) == 0 and count( $classFilterArray ) > 0 and $classFilterType == 'include' ) 00772 { 00773 $classCondition = false; 00774 } 00775 } 00776 } 00777 00778 return $classCondition; 00779 } 00780 00781 /*! 00782 \a static 00783 */ 00784 static function createExtendedAttributeFilterSQLStrings( &$extendedAttributeFilter ) 00785 { 00786 $filter = array( 'tables' => '', 00787 'joins' => '', 00788 'columns' => '', 00789 'group_by' => '' ); 00790 00791 if ( $extendedAttributeFilter and count( $extendedAttributeFilter ) > 1 ) 00792 { 00793 $extendedAttributeFilterID = $extendedAttributeFilter['id']; 00794 $extendedAttributeFilterParams = $extendedAttributeFilter['params']; 00795 $filterINI = eZINI::instance( 'extendedattributefilter.ini' ); 00796 00797 if ( !$filterINI->hasGroup( $extendedAttributeFilterID ) ) 00798 { 00799 eZDebug::writeError( "Unable to find configuration for the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ ); 00800 return $filter; 00801 } 00802 00803 $filterClassName = $filterINI->variable( $extendedAttributeFilterID, 'ClassName' ); 00804 $filterMethodName = $filterINI->variable( $extendedAttributeFilterID, 'MethodName' ); 00805 00806 if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'FileName' ) ) 00807 { 00808 $filterFile = $filterINI->variable( $extendedAttributeFilterID, 'FileName' ); 00809 00810 if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'ExtensionName' ) ) 00811 { 00812 $extensionName = $filterINI->variable( $extendedAttributeFilterID, 'ExtensionName' ); 00813 include_once( eZExtension::baseDirectory() . "/$extensionName/$filterFile" ); 00814 } 00815 else 00816 { 00817 include_once( $filterFile ); 00818 } 00819 } 00820 00821 if ( !class_exists( $filterClassName, true ) ) 00822 { 00823 eZDebug::writeError( "Unable to find the PHP class '$filterClassName' associated with the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ ); 00824 return $filter; 00825 } 00826 00827 $classObject = new $filterClassName(); 00828 $parameterArray = array( $extendedAttributeFilterParams ); 00829 00830 $sqlResult = call_user_func_array( array( $classObject, $filterMethodName ), $parameterArray ); 00831 00832 $filter['tables'] = $sqlResult['tables']; 00833 $filter['joins'] = $sqlResult['joins']; 00834 $filter['columns'] = isset( $sqlResult['columns'] ) ? $sqlResult['columns'] : ''; 00835 00836 if( isset( $sqlResult['group_by'] ) ) 00837 $filter['group_by'] = $sqlResult['group_by']; 00838 } 00839 00840 return $filter; 00841 } 00842 00843 /*! 00844 \a static 00845 */ 00846 static function createMainNodeConditionSQLString( $mainNodeOnly ) 00847 { 00848 // Main node check 00849 $mainNodeCondition = ''; 00850 if ( isset( $mainNodeOnly ) && $mainNodeOnly === true ) 00851 { 00852 $mainNodeCondition = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND'; 00853 } 00854 00855 return $mainNodeCondition; 00856 } 00857 00858 /*! 00859 \a static 00860 */ 00861 static function createObjectNameFilterConditionSQLString( $filter ) 00862 { 00863 if ( !$filter ) 00864 return ''; 00865 00866 $db = eZDB::instance(); 00867 if ( $filter == 'others' ) 00868 { 00869 $alphabet = eZAlphabetOperator::fetchAlphabet(); 00870 $sql = ''; 00871 foreach ( $alphabet as $letter ) 00872 { 00873 $sql .= " AND ezcontentobject_name.name NOT LIKE '". $db->escapeString( $letter ) . "%' "; 00874 } 00875 return $sql; 00876 } 00877 $objectNameFilterSQL = " AND ezcontentobject_name.name LIKE '" . $db->escapeString( $filter ) ."%'"; 00878 return $objectNameFilterSQL; 00879 } 00880 00881 00882 /*! 00883 \a static 00884 */ 00885 static function createAttributeFilterSQLStrings( &$attributeFilter, &$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 ) ) 00886 { 00887 // Check for attribute filtering 00888 00889 $filterSQL = array( 'from' => '', 00890 'where' => '' ); 00891 00892 $totalAttributesFiltersCount = 0; 00893 $invalidAttributesFiltersCount = 0; 00894 00895 if ( isset( $attributeFilter ) && $attributeFilter !== false ) 00896 { 00897 if ( !is_array( $attributeFilter ) ) 00898 { 00899 eZDebug::writeError( "\$attributeFilter needs to be an array", __METHOD__ ); 00900 return $filterSQL; 00901 } 00902 00903 $filterArray = $attributeFilter; 00904 00905 // Check if first value of array is a string. 00906 // To check for and/or filtering 00907 $filterJoinType = 'AND'; 00908 if ( is_string( $filterArray[0] ) ) 00909 { 00910 if ( strtolower( $filterArray[0] ) == 'or' ) 00911 { 00912 $filterJoinType = 'OR'; 00913 } 00914 else if ( strtolower( $filterArray[0] ) == 'and' ) 00915 { 00916 $filterJoinType = 'AND'; 00917 } 00918 unset( $filterArray[0] ); 00919 } 00920 00921 $attibuteFilterJoinSQL = ""; 00922 $filterCount = $sortingInfo['sortCount']; 00923 $justFilterCount = 0; 00924 00925 $db = eZDB::instance(); 00926 if ( is_array( $filterArray ) ) 00927 { 00928 // Handle attribute filters and generate SQL 00929 $totalAttributesFiltersCount = count( $filterArray ); 00930 00931 foreach ( $filterArray as $filter ) 00932 { 00933 $isFilterValid = true; // by default assumes that filter is valid 00934 00935 $filterAttributeID = $filter[0]; 00936 $filterType = $filter[1]; 00937 $filterValue = is_array( $filter[2] ) ? '' : $db->escapeString( $filter[2] ); 00938 00939 $useAttributeFilter = false; 00940 switch ( $filterAttributeID ) 00941 { 00942 case 'path': 00943 { 00944 $filterField = 'path_string'; 00945 } break; 00946 case 'published': 00947 { 00948 $filterField = 'ezcontentobject.published'; 00949 } break; 00950 case 'modified': 00951 { 00952 $filterField = 'ezcontentobject.modified'; 00953 } break; 00954 case 'modified_subnode': 00955 { 00956 $filterField = 'modified_subnode'; 00957 } break; 00958 case 'node_id': 00959 { 00960 $filterField = 'ezcontentobject_tree.node_id'; 00961 } break; 00962 case 'contentobject_id': 00963 { 00964 $filterField = 'ezcontentobject_tree.contentobject_id'; 00965 } break; 00966 case 'section': 00967 { 00968 $filterField = 'ezcontentobject.section_id'; 00969 } break; 00970 case 'state': 00971 { 00972 // state only supports =, !=, in, and not_in 00973 // other operators do not make any sense in this context 00974 $hasFilterOperator = true; 00975 00976 switch ( $filterType ) 00977 { 00978 case '=' : 00979 case '!=': 00980 { 00981 $subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2]; 00982 $filterOperator = ( $filterType == '=' ? 'IN' : 'NOT IN' ); 00983 } break; 00984 00985 case 'in': 00986 case 'not_in' : 00987 { 00988 if ( is_array( $filter[2] ) ) 00989 { 00990 $subQueryCondition = $db->generateSQLINStatement( $filter[2], 'contentobject_state_id', false, false, 'int' ); 00991 $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' ); 00992 } 00993 else 00994 { 00995 $hasFilterOperator = false; 00996 } 00997 } break; 00998 00999 default : 01000 { 01001 $hasFilterOperator = false; 01002 eZDebug::writeError( "Unknown attribute filter type for state: $filterType", __METHOD__ ); 01003 } break; 01004 } 01005 01006 if ( $hasFilterOperator ) 01007 { 01008 if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 ) 01009 $attibuteFilterJoinSQL .= " $filterJoinType "; 01010 01011 $attibuteFilterJoinSQL .= "ezcontentobject.id $filterOperator (SELECT contentobject_id FROM ezcobj_state_link WHERE $subQueryCondition)"; 01012 01013 $filterCount++; 01014 $justFilterCount++; 01015 } 01016 01017 continue 2; 01018 } break; 01019 case 'depth': 01020 { 01021 $filterField = 'depth'; 01022 } break; 01023 case 'class_identifier': 01024 { 01025 $filterField = 'ezcontentclass.identifier'; 01026 } break; 01027 case 'class_name': 01028 { 01029 $classNameFilter = eZContentClassName::sqlFilter(); 01030 $filterField = $classNameFilter['nameField']; 01031 $filterSQL['from'] .= ", $classNameFilter[from]"; 01032 $filterSQL['where'] .= "$classNameFilter[where] AND "; 01033 } break; 01034 case 'priority': 01035 { 01036 $filterField = 'ezcontentobject_tree.priority'; 01037 } break; 01038 case 'name': 01039 { 01040 $filterField = 'ezcontentobject_name.name'; 01041 } break; 01042 case 'owner': 01043 { 01044 $filterField = 'ezcontentobject.owner_id'; 01045 } break; 01046 case 'visibility': 01047 { 01048 $filterValue = ( $filterValue == '1' ) ? 0 : 1; 01049 $filterField = 'ezcontentobject_tree.is_invisible'; 01050 } break; 01051 default: 01052 { 01053 $useAttributeFilter = true; 01054 } break; 01055 } 01056 01057 if ( $useAttributeFilter ) 01058 { 01059 if ( !is_numeric( $filterAttributeID ) ) 01060 $filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $filterAttributeID ); 01061 01062 if ( $filterAttributeID === false ) 01063 { 01064 $isFilterValid = false; 01065 if( $filterJoinType === 'AND' ) 01066 { 01067 // go out 01068 $invalidAttributesFiltersCount = $totalAttributesFiltersCount; 01069 break; 01070 } 01071 01072 ++$invalidAttributesFiltersCount; 01073 } 01074 else 01075 { 01076 // Check datatype for filtering 01077 $filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $filterAttributeID ); 01078 if ( $filterDataType === false ) 01079 { 01080 $isFilterValid = false; 01081 if( $filterJoinType === 'AND' ) 01082 { 01083 // go out 01084 $invalidAttributesFiltersCount = $totalAttributesFiltersCount; 01085 break; 01086 } 01087 01088 // check next filter 01089 ++$invalidAttributesFiltersCount; 01090 } 01091 else 01092 { 01093 $sortKey = false; 01094 if ( $filterDataType == 'string' ) 01095 { 01096 $sortKey = 'sort_key_string'; 01097 } 01098 else 01099 { 01100 $sortKey = 'sort_key_int'; 01101 } 01102 01103 $filterField = "a$filterCount.$sortKey"; 01104 01105 // Use the same joins as we do when sorting, 01106 // if more attributes are filtered by we will append them 01107 if ( $filterCount >= $sortingInfo['attributeJoinCount'] ) 01108 { 01109 $filterSQL['from'] .= ", ezcontentobject_attribute a$filterCount "; 01110 } 01111 01112 $filterSQL['where'] .= " 01113 a$filterCount.contentobject_id = ezcontentobject.id AND 01114 a$filterCount.contentclassattribute_id = $filterAttributeID AND 01115 a$filterCount.version = ezcontentobject_name.content_version AND "; 01116 $filterSQL['where'] .= eZContentLanguage::sqlFilter( "a$filterCount", 'ezcontentobject' ). ' AND '; 01117 } 01118 } 01119 } 01120 01121 if ( $isFilterValid ) 01122 { 01123 $hasFilterOperator = true; 01124 // Controls quotes around filter value, some filters do this manually 01125 $noQuotes = false; 01126 // Controls if $filterValue or $filter[2] is used, $filterValue is already escaped 01127 $unEscape = false; 01128 01129 switch ( $filterType ) 01130 { 01131 case '=' : 01132 { 01133 $filterOperator = '='; 01134 }break; 01135 01136 case '!=' : 01137 { 01138 $filterOperator = '<>'; 01139 }break; 01140 01141 case '>' : 01142 { 01143 $filterOperator = '>'; 01144 }break; 01145 01146 case '<' : 01147 { 01148 $filterOperator = '<'; 01149 }break; 01150 01151 case '<=' : 01152 { 01153 $filterOperator = '<='; 01154 }break; 01155 01156 case '>=' : 01157 { 01158 $filterOperator = '>='; 01159 }break; 01160 01161 case 'like': 01162 case 'not_like': 01163 { 01164 $filterOperator = ( $filterType == 'like' ? 'LIKE' : 'NOT LIKE' ); 01165 // We escape the string ourselves, this MUST be done before wildcard replace 01166 $filter[2] = $db->escapeString( $filter[2] ); 01167 $unEscape = true; 01168 // Since * is used as wildcard we need to transform the string to 01169 // use % as wildcard. The following rules apply: 01170 // - % -> \% 01171 // - * -> % 01172 // - \* -> * 01173 // - \\ -> \ 01174 01175 $filter[2] = preg_replace( array( '#%#m', 01176 '#(?<!\\\\)\\*#m', 01177 '#(?<!\\\\)\\\\\\*#m', 01178 '#\\\\\\\\#m' ), 01179 array( '\\%', 01180 '%', 01181 '*', 01182 '\\\\' ), 01183 $filter[2] ); 01184 } break; 01185 01186 case 'in': 01187 case 'not_in' : 01188 { 01189 $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' ); 01190 // Turn off quotes for value, we do this ourselves 01191 $noQuotes = true; 01192 if ( is_array( $filter[2] ) ) 01193 { 01194 reset( $filter[2] ); 01195 while ( list( $key, $value ) = each( $filter[2] ) ) 01196 { 01197 // Non-numerics must be escaped to avoid SQL injection 01198 $filter[2][$key] = is_numeric( $value ) ? $value : "'" . $db->escapeString( $value ) . "'"; 01199 } 01200 $filterValue = '(' . implode( ",", $filter[2] ) . ')'; 01201 } 01202 else 01203 { 01204 $hasFilterOperator = false; 01205 } 01206 } break; 01207 01208 case 'between': 01209 case 'not_between' : 01210 { 01211 $filterOperator = ( $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN' ); 01212 // Turn off quotes for value, we do this ourselves 01213 $noQuotes = true; 01214 if ( is_array( $filter[2] ) ) 01215 { 01216 // Check for non-numerics to avoid SQL injection 01217 if ( !is_numeric( $filter[2][0] ) ) 01218 $filter[2][0] = "'" . $db->escapeString( $filter[2][0] ) . "'"; 01219 if ( !is_numeric( $filter[2][1] ) ) 01220 $filter[2][1] = "'" . $db->escapeString( $filter[2][1] ) . "'"; 01221 01222 $filterValue = $filter[2][0] . ' AND ' . $filter[2][1]; 01223 } 01224 } break; 01225 01226 default : 01227 { 01228 $hasFilterOperator = false; 01229 eZDebug::writeError( "Unknown attribute filter type: $filterType", __METHOD__ ); 01230 }break; 01231 01232 } 01233 if ( $hasFilterOperator ) 01234 { 01235 if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 ) 01236 $attibuteFilterJoinSQL .= " $filterJoinType "; 01237 01238 // If $unEscape is true we get the filter value from the 2nd element instead 01239 // which must have been escaped by filter type 01240 $filterValue = $unEscape ? $filter[2] : $filterValue; 01241 01242 $attibuteFilterJoinSQL .= "$filterField $filterOperator "; 01243 $attibuteFilterJoinSQL .= $noQuotes ? "$filterValue " : "'$filterValue' "; 01244 01245 $filterCount++; 01246 $justFilterCount++; 01247 } 01248 } 01249 } // end of 'foreach ( $filterArray as $filter )' 01250 01251 if ( $totalAttributesFiltersCount == $invalidAttributesFiltersCount ) 01252 { 01253 eZDebug::writeNotice( "Attribute filter returned false" ); 01254 $filterSQL = false; 01255 } 01256 else 01257 { 01258 if ( $justFilterCount > 0 ) 01259 $filterSQL['where'] .= " ( " . $attibuteFilterJoinSQL . " ) AND "; 01260 } 01261 } // end of 'if ( is_array( $filterArray ) )' 01262 } 01263 01264 return $filterSQL; 01265 } 01266 01267 /*! 01268 \a static 01269 */ 01270 static function createNotEqParentSQLString( $nodeID, $depth = false, $depthOperator = 'le' ) 01271 { 01272 $notEqParentString = ''; 01273 if( !$depth || $depthOperator == 'le' || $depthOperator == 'lt' ) 01274 { 01275 $notEqParentString = "ezcontentobject_tree.node_id != $nodeID AND"; 01276 } 01277 01278 return $notEqParentString; 01279 } 01280 01281 /*! 01282 \a static 01283 */ 01284 static function createPathConditionSQLString( $nodePath, $nodeDepth, $depth = false, $depthOperator = 'le' ) 01285 { 01286 $pathCondition = ''; 01287 $depthCondition = ''; 01288 01289 if ( $depth ) 01290 { 01291 $sqlDepthOperator = '<='; 01292 if ( $depthOperator ) 01293 { 01294 if ( $depthOperator == 'lt' ) 01295 { 01296 $sqlDepthOperator = '<'; 01297 } 01298 else if ( $depthOperator == 'gt' ) 01299 { 01300 $sqlDepthOperator = '>'; 01301 } 01302 else if ( $depthOperator == 'le' ) 01303 { 01304 $sqlDepthOperator = '<='; 01305 } 01306 else if ( $depthOperator == 'ge' ) 01307 { 01308 $sqlDepthOperator = '>='; 01309 } 01310 else if ( $depthOperator == 'eq' ) 01311 { 01312 $sqlDepthOperator = '='; 01313 } 01314 } 01315 01316 $nodeDepth += $depth; 01317 $depthCondition = ' ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' and '; 01318 } 01319 01320 $pathCondition = " ezcontentobject_tree.path_string like '$nodePath%' and $depthCondition "; 01321 return $pathCondition; 01322 } 01323 01324 /*! 01325 \a static 01326 */ 01327 static function createPathConditionAndNotEqParentSQLStrings( &$outPathConditionStr, &$outNotEqParentStr, $nodeID, $depth = false, $depthOperator = 'le' ) 01328 { 01329 if ( !$depthOperator ) 01330 { 01331 $depthOperator = 'le'; 01332 } 01333 01334 // check if we are only fetching children 01335 // - depth (lower than or) eqaul to 1 01336 // - depth lower than 2 = depth equal to 1 01337 $onlyChildren = ( $depth === 1 && ( $depthOperator === 'le' || $depthOperator === 'eq' ) ) || 01338 ( $depth === 2 && $depthOperator === 'lt' ); 01339 01340 if ( is_array( $nodeID ) && count( $nodeID ) == 1 ) 01341 { 01342 $nodeID = $nodeID[0]; 01343 } 01344 01345 if ( is_array( $nodeID ) ) 01346 { 01347 $outNotEqParentStr = ''; 01348 01349 // a parent_node_id condition suffits when only fetching children 01350 if ( $onlyChildren ) 01351 { 01352 $db = eZDB::instance(); 01353 $outPathConditionStr = $db->generateSQLINStatement( $nodeID, 'ezcontentobject_tree.parent_node_id', false, true, 'int' ) . ' and'; 01354 } 01355 else 01356 { 01357 $nodeIDList = $nodeID; 01358 $sqlPartForOneNodeList = array(); 01359 01360 foreach ( $nodeIDList as $nodeID ) 01361 { 01362 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 01363 if ( !is_array( $node ) ) 01364 return false; 01365 01366 $nodePath = $node['path_string']; 01367 $nodeDepth = $node['depth']; 01368 $depthCond = ''; 01369 if ( $depth ) 01370 { 01371 $sqlDepthOperator = '<='; 01372 if ( $depthOperator ) 01373 { 01374 if ( $depthOperator == 'lt' ) 01375 { 01376 $sqlDepthOperator = '<'; 01377 } 01378 else if ( $depthOperator == 'gt' ) 01379 { 01380 $sqlDepthOperator = '>'; 01381 } 01382 else if ( $depthOperator == 'le' ) 01383 { 01384 $sqlDepthOperator = '<='; 01385 } 01386 else if ( $depthOperator == 'ge' ) 01387 { 01388 $sqlDepthOperator = '>='; 01389 } 01390 else if ( $depthOperator == 'eq' ) 01391 { 01392 $sqlDepthOperator = '='; 01393 } 01394 } 01395 $nodeDepth += $depth; 01396 $depthCond = ' and ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' '; 01397 } 01398 01399 $requireNotEqParentStr = !$depth || $depthOperator == 'le' || $depthOperator == 'lt'; 01400 $notEqParentStr = $requireNotEqParentStr ? " and ezcontentobject_tree.node_id != $nodeID " : ''; 01401 $sqlPartForOneNodeList[] = " ( ezcontentobject_tree.path_string like '$nodePath%' $depthCond $notEqParentStr ) "; 01402 } 01403 $outPathConditionStr = implode( ' or ', $sqlPartForOneNodeList ); 01404 $outPathConditionStr = ' (' . $outPathConditionStr . ') and'; 01405 } 01406 } 01407 else 01408 { 01409 if ( $nodeID == 0 ) 01410 { 01411 return false; 01412 } 01413 01414 // a parent_node_id condition suffits when only fetching children 01415 if ( $onlyChildren ) 01416 { 01417 $outNotEqParentStr = ''; 01418 $outPathConditionStr = 'ezcontentobject_tree.parent_node_id = ' . (int) $nodeID . ' and'; 01419 } 01420 else 01421 { 01422 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 01423 if ( !is_array( $node ) ) 01424 return false; 01425 01426 $nodePath = $node['path_string']; 01427 $nodeDepth = $node['depth']; 01428 01429 $outNotEqParentStr = eZContentObjectTreeNode::createNotEqParentSQLString( $nodeID, $depth, $depthOperator ); 01430 $outPathConditionStr = eZContentObjectTreeNode::createPathConditionSQLString( $nodePath, $nodeDepth, $depth, $depthOperator ); 01431 } 01432 } 01433 01434 return true; 01435 } 01436 01437 /*! 01438 \a static 01439 */ 01440 static function createGroupBySQLStrings( &$outGroupBySelectText, &$outGroupByText, $groupBy ) 01441 { 01442 if ( $groupBy ) 01443 { 01444 if ( isset( $groupBy['field'] ) and isset( $groupBy['type'] ) ) 01445 { 01446 $groupByField = $groupBy['field']; 01447 $groupByFieldType = $groupBy['type']; 01448 01449 switch ( $groupByField ) 01450 { 01451 case 'published': 01452 { 01453 $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType ); 01454 $groupBySelect['field'] = "ezcontentobject." . $groupByField; 01455 } break; 01456 case 'modified': 01457 { 01458 $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType ); 01459 $groupBySelect['field'] = "ezcontentobject." . $groupByField; 01460 } break; 01461 } 01462 01463 $outGroupBySelectText = ", " . $groupBySelect['select']; 01464 $outGroupByText = "GROUP BY " . $groupBySelect['group_field']; 01465 } 01466 } 01467 } 01468 01469 /*! 01470 \a static 01471 */ 01472 static function createVersionNameTablesSQLString( $useVersionName ) 01473 { 01474 $versionNameTables = ''; 01475 01476 if ( $useVersionName ) 01477 { 01478 $versionNameTables = ', ezcontentobject_name '; 01479 } 01480 01481 return $versionNameTables; 01482 } 01483 01484 /*! 01485 \a static 01486 */ 01487 static function createVersionNameTargetsSQLString( $useVersionName ) 01488 { 01489 $versionNameTargets = ''; 01490 01491 if ( $useVersionName ) 01492 { 01493 $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation '; 01494 } 01495 01496 01497 return $versionNameTargets; 01498 } 01499 01500 /*! 01501 \a static 01502 */ 01503 static function createVersionNameJoinsSQLString( $useVersionName, $includeAnd = true, $onlyTranslated = false, $lang = false, $treeTableName = 'ezcontentobject_tree' ) 01504 { 01505 $versionNameJoins = ''; 01506 if ( $useVersionName ) 01507 { 01508 if ( $includeAnd ) 01509 { 01510 $versionNameJoins .= ' AND '; 01511 } 01512 $versionNameJoins .= " $treeTableName.contentobject_id = ezcontentobject_name.contentobject_id and 01513 $treeTableName.contentobject_version = ezcontentobject_name.content_version and "; 01514 $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ); 01515 } 01516 return $versionNameJoins; 01517 } 01518 01519 /*! 01520 \a static 01521 Deprecated. Use 'createPermissionCheckingSQL' instead. 01522 */ 01523 static function createPermissionCheckingSQLString( $limitationList ) 01524 { 01525 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 01526 return $sqlPermissionChecking['where']; 01527 } 01528 01529 /*! 01530 \a static 01531 */ 01532 static function createPermissionCheckingSQL( $limitationList, $treeTableName = 'ezcontentobject_tree', $tableAliasName = 'ezcontentobject_tree' ) 01533 { 01534 $db = eZDB::instance(); 01535 01536 $sqlPermissionCheckingFrom = ''; 01537 $sqlPermissionCheckingWhere = ''; 01538 $sqlPermissionTempTables = array(); 01539 $groupPermTempTable = false; 01540 $createdStateAliases = array(); 01541 01542 if ( is_array( $limitationList ) && count( $limitationList ) > 0 ) 01543 { 01544 $sqlParts = array(); 01545 foreach( $limitationList as $limitationArray ) 01546 { 01547 $sqlPartPart = array(); 01548 $sqlPartPartPart = array(); 01549 $sqlPlacementPart = array(); 01550 01551 foreach ( array_keys( $limitationArray ) as $ident ) 01552 { 01553 switch( $ident ) 01554 { 01555 case 'Class': 01556 { 01557 $sqlPartPart[] = 'ezcontentobject.contentclass_id in (' . implode( ', ', $limitationArray[$ident] ) . ')'; 01558 } break; 01559 01560 case 'Section': 01561 case 'User_Section': 01562 { 01563 $sqlPartPart[] = 'ezcontentobject.section_id in (' . implode( ', ', $limitationArray[$ident] ) . ')'; 01564 } break; 01565 01566 case 'Owner': 01567 { 01568 $user = eZUser::currentUser(); 01569 $userID = $user->attribute( 'contentobject_id' ); 01570 $sqlPartPart[] = "ezcontentobject.owner_id = '" . $db->escapeString( $userID ) . "'"; 01571 } break; 01572 01573 case 'Group': 01574 { 01575 if ( !$groupPermTempTable ) 01576 { 01577 $user = eZUser::currentUser(); 01578 $userContentObject = $user->attribute( 'contentobject' ); 01579 $parentList = $userContentObject->attribute( 'parent_nodes' ); 01580 01581 $groupPermTempTable = $db->generateUniqueTempTableName( 'ezgroup_perm_tmp_%' ); 01582 $sqlPermissionTempTables[] = $groupPermTempTable; 01583 01584 $db->createTempTable( "CREATE TEMPORARY TABLE $groupPermTempTable ( user_id int NOT NULL PRIMARY KEY )" ); 01585 $db->query( "INSERT INTO $groupPermTempTable 01586 SELECT DISTINCT contentobject_id AS user_id 01587 FROM ezcontentobject_tree 01588 WHERE parent_node_id IN (" . implode( ', ', $parentList ) . ')', 01589 eZDBInterface::SERVER_SLAVE ); 01590 01591 $sqlPermissionCheckingFrom .= ', ' . $groupPermTempTable; 01592 } 01593 $sqlPartPart[] = "ezcontentobject.owner_id = $groupPermTempTable.user_id"; 01594 } break; 01595 01596 case 'Node': 01597 { 01598 $sqlPlacementPart[] = $tableAliasName . '.node_id in (' . implode( ', ', $limitationArray[$ident] ) . ')'; 01599 } break; 01600 01601 case 'Subtree': 01602 { 01603 $sqlSubtreePart = array(); 01604 foreach ( $limitationArray[$ident] as $limitationPathString ) 01605 { 01606 $sqlSubtreePart[] = "$tableAliasName.path_string like '$limitationPathString%'"; 01607 } 01608 $sqlPlacementPart[] = implode( ' OR ', $sqlSubtreePart ); 01609 } break; 01610 01611 case 'User_Subtree': 01612 { 01613 $sqlPartUserSubtree = array(); 01614 foreach ( $limitationArray[$ident] as $limitationPathString ) 01615 { 01616 $sqlPartUserSubtree[] = "$tableAliasName.path_string like '$limitationPathString%'"; 01617 } 01618 $sqlPartPart[] = implode( ' OR ', $sqlPartUserSubtree ); 01619 } break; 01620 01621 default: 01622 { 01623 if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 ) 01624 { 01625 $stateIdentifier = substr( $ident, 11 ); 01626 01627 if ( !isset( $createdStateAliases[$stateIdentifier] ) ) 01628 { 01629 $stateIndex = count( $createdStateAliases ); 01630 } 01631 else 01632 { 01633 $stateIndex = $createdStateAliases[$stateIdentifier]; 01634 } 01635 $stateTable = "ezcobj_state_{$stateIndex}_perm"; 01636 01637 if ( !isset( $createdStateAliases[$stateIdentifier] ) ) 01638 { 01639 $createdStateAliases[$stateIdentifier] = $stateIndex; 01640 $stateLinkTable = "ezcobj_state_lnk_{$stateIndex}_perm"; 01641 $stateGroupTable = "ezcobj_state_grp_{$stateIndex}_perm"; 01642 $stateAliasTables[$stateIdentifier] = $stateTable; 01643 01644 $sqlPermissionCheckingFrom .= ", ezcobj_state_link $stateLinkTable "; 01645 $sqlPermissionCheckingFrom .= ", ezcobj_state_group $stateGroupTable "; 01646 $sqlPermissionCheckingFrom .= ", ezcobj_state $stateTable "; 01647 01648 $sqlPermissionCheckingWhere .= "AND $stateLinkTable.contentobject_id = ezcontentobject.id " . 01649 "AND $stateTable.id = $stateLinkTable.contentobject_state_id " . 01650 "AND $stateTable.group_id = $stateGroupTable.id " . 01651 "AND $stateGroupTable.identifier='" . $db->escapeString( $stateIdentifier ) . "' "; 01652 } 01653 01654 if ( count( $limitationArray[$ident] ) > 1 ) 01655 { 01656 $sqlPartPart[] = $db->generateSQLINStatement( $limitationArray[$ident], "$stateTable.id" ); 01657 } 01658 else 01659 { 01660 $sqlPartPart[] = "$stateTable.id = " . $limitationArray[$ident][0]; 01661 } 01662 } 01663 } 01664 } 01665 } 01666 if ( $sqlPlacementPart ) 01667 { 01668 $sqlPartPart[] = '( ( ' . implode( ' ) OR ( ', $sqlPlacementPart ) . ' ) )'; 01669 } 01670 if ( $sqlPartPartPart ) 01671 { 01672 $sqlPartPart[] = '( ' . implode( ' ) OR ( ', $sqlPartPartPart ) . ' )'; 01673 } 01674 $sqlParts[] = implode( ' AND ', $sqlPartPart ); 01675 } 01676 $sqlPermissionCheckingWhere .= ' AND ((' . implode( ") OR (", $sqlParts ) . ')) '; 01677 } 01678 01679 $sqlPermissionChecking = array( 'from' => $sqlPermissionCheckingFrom, 01680 'where' => $sqlPermissionCheckingWhere, 01681 'temp_tables' => $sqlPermissionTempTables ); 01682 01683 return $sqlPermissionChecking; 01684 } 01685 01686 01687 /*! 01688 \a static 01689 01690 \param $limit maximum number of nodes in the path to use, starting from last node 01691 */ 01692 static function createNodesConditionSQLStringFromPath( $nodePath, $includingLastNodeInThePath, $limit = false ) 01693 { 01694 $pathString = false; 01695 $pathArray = explode( '/', trim( $nodePath, '/' ) ); 01696 01697 $pathArrayCount = count( $pathArray ); 01698 01699 if ( $limit && $includingLastNodeInThePath == false ) 01700 { 01701 $limit++; 01702 } 01703 01704 $sliceOffset = $limit && $pathArrayCount > $limit ? $pathArrayCount - $limit : 0; 01705 $sliceLength = $includingLastNodeInThePath ? $pathArrayCount - $sliceOffset : $pathArrayCount - ( $sliceOffset + 1 ); 01706 01707 // only take a slice when necessary 01708 if ( ( $sliceOffset + $sliceLength ) < $pathArrayCount ) 01709 { 01710 $pathArray = array_slice( $pathArray, $sliceOffset, $sliceLength ); 01711 } 01712 01713 if ( $sliceLength == 1 ) 01714 { 01715 $pathString = ' node_id = ' . implode( '', $pathArray ) . ' and '; 01716 } 01717 else if ( $sliceLength > 0 ) 01718 { 01719 $db = eZDB::instance(); 01720 $pathString = ' ' . $db->generateSQLINStatement( $pathArray, 'node_id' ) . ' and '; 01721 } 01722 01723 return $pathString; 01724 } 01725 01726 /*! 01727 \a static 01728 If \a $useSettings is true \a $fetchHidden will be ignored. 01729 If \a $useSettings is false \a $fetchHidden will be used. 01730 */ 01731 static function createShowInvisibleSQLString( $useSettings, $fetchHidden = true ) 01732 { 01733 $showInvisibleNodesCond = ''; 01734 $showInvisible = $fetchHidden; 01735 01736 if ( $useSettings ) 01737 $showInvisible = eZContentObjectTreeNode::showInvisibleNodes(); 01738 01739 if ( !$showInvisible ) 01740 $showInvisibleNodesCond = 'AND ezcontentobject_tree.is_invisible = 0'; 01741 01742 return $showInvisibleNodesCond; 01743 } 01744 01745 /*! 01746 \a static 01747 \returns true if we should show invisible nodes (determined by ini setting), false otherwise. 01748 */ 01749 static function showInvisibleNodes() 01750 { 01751 static $cachedResult; 01752 01753 if ( !isset( $cachedResult ) ) 01754 { 01755 $ini = eZINI::instance( 'site.ini' ); 01756 $cachedResult = $ini->hasVariable( 'SiteAccessSettings', 'ShowHiddenNodes' ) ? 01757 $ini->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) == 'true' : 01758 true; 01759 } 01760 01761 return $cachedResult; 01762 } 01763 01764 /*! 01765 \a static 01766 */ 01767 static function getLimitationList( &$limitation ) 01768 { 01769 $currentUser = eZUser::currentUser(); 01770 $currentUserID = $currentUser->attribute( 'contentobject_id' ); 01771 $limitationList = array(); 01772 01773 if ( $limitation !== false ) 01774 { 01775 $limitationList = $limitation; 01776 } 01777 else if ( isset( $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] ) ) 01778 { 01779 $limitationList =& $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read']; 01780 eZDebugSetting::writeDebug( 'kernel-content-treenode', $limitationList, "limitation list" ); 01781 } 01782 else 01783 { 01784 $accessResult = $currentUser->hasAccessTo( 'content', 'read' ); 01785 01786 if ( $accessResult['accessWord'] == 'no' ) 01787 { 01788 $limitationList = false; 01789 $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = false; 01790 } 01791 else if ( $accessResult['accessWord'] == 'limited' ) 01792 { 01793 $limitationList = $accessResult['policies']; 01794 $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = $accessResult['policies']; 01795 } 01796 } 01797 01798 return $limitationList; 01799 } 01800 01801 /*! 01802 \sa subTree 01803 */ 01804 static function subTreeByNodeID( $params = false, $nodeID = 0 ) 01805 { 01806 if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) ) 01807 { 01808 return null; 01809 } 01810 01811 if ( $params === false ) 01812 { 01813 $params = array( 'Depth' => false, 01814 'Offset' => false, 01815 //'OnlyTranslated' => false, 01816 'Language' => false, 01817 'Limit' => false, 01818 'SortBy' => false, 01819 'AttributeFilter' => false, 01820 'ExtendedAttributeFilter' => false, 01821 'ClassFilterType' => false, 01822 'ClassFilterArray' => false, 01823 'GroupBy' => false ); 01824 } 01825 01826 $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false; 01827 //$onlyTranslated = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated'] : false; 01828 $language = ( isset( $params['Language'] ) ) ? $params['Language'] : false; 01829 $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false; 01830 $depth = ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) ? $params['Depth'] : false; 01831 $depthOperator = ( isset( $params['DepthOperator'] ) ) ? $params['DepthOperator'] : false; 01832 $asObject = ( isset( $params['AsObject'] ) ) ? $params['AsObject'] : true; 01833 $loadDataMap = ( isset( $params['LoadDataMap'] ) ) ? $params['LoadDataMap'] : false; 01834 $groupBy = ( isset( $params['GroupBy'] ) ) ? $params['GroupBy'] : false; 01835 $mainNodeOnly = ( isset( $params['MainNodeOnly'] ) ) ? $params['MainNodeOnly'] : false; 01836 $ignoreVisibility = ( isset( $params['IgnoreVisibility'] ) ) ? $params['IgnoreVisibility'] : false; 01837 $objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false; 01838 01839 if ( $offset < 0 ) 01840 { 01841 $offset = abs( $offset ); 01842 } 01843 01844 if ( !isset( $params['SortBy'] ) ) 01845 $params['SortBy'] = false; 01846 if ( !isset( $params['ClassFilterType'] ) ) 01847 $params['ClassFilterType'] = false; 01848 01849 $allowCustomSorting = false; 01850 if ( isset( $params['ExtendedAttributeFilter'] ) && is_array ( $params['ExtendedAttributeFilter'] ) ) 01851 { 01852 $allowCustomSorting = true; 01853 } 01854 01855 $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $params['SortBy'], 'ezcontentobject_tree', $allowCustomSorting ); 01856 $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] ); 01857 if ( $classCondition === false ) 01858 { 01859 eZDebug::writeNotice( "Class filter returned false" ); 01860 return null; 01861 } 01862 01863 $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo ); 01864 if ( $attributeFilter === false ) 01865 { 01866 return null; 01867 } 01868 $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] ); 01869 $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly ); 01870 01871 $pathStringCond = ''; 01872 $notEqParentString = ''; 01873 // If the node(s) doesn't exist we return null. 01874 if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) ) 01875 { 01876 return null; 01877 } 01878 01879 if ( $language ) 01880 { 01881 if ( !is_array( $language ) ) 01882 { 01883 $language = array( $language ); 01884 } 01885 // This call must occur after eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings, 01886 // because the parent node may not exist in Language 01887 eZContentLanguage::setPrioritizedLanguages( $language ); 01888 } 01889 01890 $groupBySelectText = ''; 01891 $groupBySQL = $extendedAttributeFilter['group_by']; 01892 if ( !$groupBySQL ) 01893 { 01894 eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy ); 01895 } 01896 else if ( $groupBy ) 01897 { 01898 eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ ); 01899 } 01900 01901 01902 $useVersionName = true; 01903 01904 $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName ); 01905 $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName ); 01906 $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false ); 01907 01908 $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' ); 01909 01910 if ( $language ) 01911 { 01912 eZContentLanguage::clearPrioritizedLanguages(); 01913 } 01914 $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter ); 01915 01916 $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false; 01917 $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation ); 01918 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 01919 01920 // Determine whether we should show invisible nodes. 01921 $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility ); 01922 01923 $query = "SELECT DISTINCT 01924 ezcontentobject.*, 01925 ezcontentobject_tree.*, 01926 ezcontentclass.serialized_name_list as class_serialized_name_list, 01927 ezcontentclass.identifier as class_identifier, 01928 ezcontentclass.is_container as is_container 01929 $groupBySelectText 01930 $versionNameTargets 01931 $sortingInfo[attributeTargetSQL] 01932 $extendedAttributeFilter[columns] 01933 FROM 01934 ezcontentobject_tree, 01935 ezcontentobject,ezcontentclass 01936 $versionNameTables 01937 $sortingInfo[attributeFromSQL] 01938 $attributeFilter[from] 01939 $extendedAttributeFilter[tables] 01940 $sqlPermissionChecking[from] 01941 WHERE 01942 $pathStringCond 01943 $extendedAttributeFilter[joins] 01944 $sortingInfo[attributeWhereSQL] 01945 $attributeFilter[where] 01946 ezcontentclass.version=0 AND 01947 $notEqParentString 01948 ezcontentobject_tree.contentobject_id = ezcontentobject.id AND 01949 ezcontentclass.id = ezcontentobject.contentclass_id AND 01950 $mainNodeOnlyCond 01951 $classCondition 01952 $versionNameJoins 01953 $showInvisibleNodesCond 01954 $sqlPermissionChecking[where] 01955 $objectNameFilterSQL 01956 $languageFilter 01957 $groupBySQL"; 01958 01959 if ( $sortingInfo['sortingFields'] ) 01960 $query .= " ORDER BY $sortingInfo[sortingFields]"; 01961 01962 $db = eZDB::instance(); 01963 01964 $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false; 01965 01966 $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset, 01967 'limit' => $limit ), 01968 $server ); 01969 01970 if ( $asObject ) 01971 { 01972 $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 01973 if ( $loadDataMap === true ) 01974 eZContentObject::fillNodeListAttributes( $retNodeList ); 01975 else if ( $loadDataMap && is_numeric( $loadDataMap ) && $loadDataMap >= count( $retNodeList ) ) 01976 eZContentObject::fillNodeListAttributes( $retNodeList ); 01977 } 01978 else 01979 { 01980 $retNodeList = $nodeListArray; 01981 } 01982 01983 // cleanup temp tables 01984 $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] ); 01985 01986 return $retNodeList; 01987 } 01988 01989 function subTree( $params = false ) 01990 { 01991 return eZContentObjectTreeNode::subTreeByNodeID( $params, $this->attribute( 'node_id' ) ); 01992 } 01993 01994 /*! 01995 Retrieve subtrees from multiple paths. 01996 01997 This method composes a list of objects retrieved from various node paths, 01998 sorted by criteria that are globally applied to the whole list. 01999 02000 It is for example useful for an RSS feed that serves content from 02001 several node paths. The respective subtrees need to be amalgated and 02002 the resulting object listed sorted by publishing date to show the latest 02003 entries in chronological order. 02004 02005 The first parameter is a multi-dimensional array containing the 02006 node IDs and filter criteria assigned to each of the nodes: 02007 02008 array( 02009 [node_1] => array( 02010 'ClassFilterType' => [filter_type], 02011 'ClassFilterArray' => [filter_array] 02012 ), 02013 [node_2] => array( 02014 'ClassFilterType' => [filter_type], 02015 'ClassFilterArray' => [filter_array] 02016 ) 02017 ) 02018 02019 The second parameter is a single-dimensional array with criteria 02020 applied to the list of objects retrieved from the various subtrees: 02021 02022 array( 02023 'SortBy' => [sorting-criteria] 02024 ) 02025 */ 02026 static function subTreeMultiPaths( $nodesParams, $listParams = NULL ) 02027 { 02028 if( !is_array( $nodesParams ) || !count( $nodesParams ) ) 02029 { 02030 eZDebug::writeWarning( __METHOD__.': Nodes parameter must be an array with at least one key.' ); 02031 return null; 02032 } 02033 02034 if( $listParams === null ) 02035 { 02036 $listParams = array( 02037 'SortBy' => false, 02038 'Offset' => false, 02039 'Limit' => false, 02040 'SortBy' => false, 02041 'GroupBy' => false ); 02042 } 02043 02044 $offset = ( isset( $listParams['Offset'] ) && is_numeric( $listParams['Offset'] ) ) ? $listParams['Offset'] : false; 02045 $limit = ( isset( $listParams['Limit'] ) && is_numeric( $listParams['Limit'] ) ) ? $listParams['Limit'] : false; 02046 $groupBy = ( isset( $listParams['GroupBy'] ) ) ? $listParams['GroupBy'] : false; 02047 if ( !isset( $listParams['SortBy'] ) ) 02048 { 02049 $listParams['SortBy'] = false; 02050 } 02051 $sortBy = $listParams['SortBy']; 02052 02053 $queryNodes = ''; 02054 02055 foreach( $nodesParams as $nodeParams ) 02056 { 02057 $nodeID = $nodeParams['ParentNodeID']; 02058 02059 if ( !is_numeric( $nodeID ) && !is_array( $nodeID ) ) 02060 { 02061 eZDebug::writeWarning( __METHOD__.': Nodes parameter must be numeric or an array with numeric values.' ); 02062 $retValue = null; 02063 return $retValue; 02064 } 02065 02066 if ( $nodeParams === null ) 02067 { 02068 $nodeParams = array( 02069 'Depth' => false, 02070 //'OnlyTranslated' => false, 02071 'Language' => false, 02072 'AttributeFilter' => false, 02073 'ExtendedAttributeFilter' => false, 02074 'ClassFilterType' => false, 02075 'ClassFilterArray' => false ); 02076 } 02077 02078 //$onlyTranslated = ( isset( $nodeParams['OnlyTranslated'] ) ) ? $nodeParams['OnlyTranslated'] : false; 02079 $language = ( isset( $nodeParams['Language'] ) ) ? $nodeParams['Language'] : false; 02080 $depth = ( isset( $nodeParams['Depth'] ) && is_numeric( $nodeParams['Depth'] ) ) ? $nodeParams['Depth'] : false; 02081 $depthOperator = ( isset( $nodeParams['DepthOperator'] ) ) ? $nodeParams['DepthOperator'] : false; 02082 $asObject = ( isset( $nodeParams['AsObject'] ) ) ? $nodeParams['AsObject'] : true; 02083 $mainNodeOnly = ( isset( $nodeParams['MainNodeOnly'] ) ) ? $nodeParams['MainNodeOnly'] : false; 02084 $ignoreVisibility = ( isset( $nodeParams['IgnoreVisibility'] ) ) ? $nodeParams['IgnoreVisibility'] : false; 02085 if ( !isset( $nodeParams['ClassFilterType'] ) ) 02086 { 02087 $nodeParams['ClassFilterType'] = false; 02088 } 02089 02090 if ( $language ) 02091 { 02092 if ( !is_array( $language ) ) 02093 { 02094 $language = array( $language ); 02095 } 02096 eZContentLanguage::setPrioritizedLanguages( $language ); 02097 } 02098 02099 $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortBy ); 02100 $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $nodeParams['ClassFilterType'], $nodeParams['ClassFilterArray'] ); 02101 $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $nodeParams['AttributeFilter'], $sortingInfo ); 02102 $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $nodeParams['ExtendedAttributeFilter'] ); 02103 $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly ); 02104 02105 $pathStringCond = ''; 02106 $notEqParentString = ''; 02107 // If the node(s) doesn't exist we return null. 02108 02109 if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) ) 02110 { 02111 $retValue = null; 02112 return $retValue; 02113 } 02114 02115 $useVersionName = true; 02116 $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName ); 02117 $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName ); 02118 $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false ); 02119 02120 $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' ); 02121 02122 if ( $language ) 02123 { 02124 eZContentLanguage::clearPrioritizedLanguages(); 02125 } 02126 02127 $limitation = ( isset( $nodeParams['Limitation'] ) && is_array( $nodeParams['Limitation'] ) ) ? $nodeParams['Limitation']: false; 02128 $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation ); 02129 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 02130 02131 // Determine whether we should show invisible nodes. 02132 $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility ); 02133 02134 $queryNodes .= " ( 02135 $pathStringCond 02136 $extendedAttributeFilter[joins] 02137 $sortingInfo[attributeWhereSQL] 02138 $attributeFilter[where] 02139 ezcontentclass.version=0 AND 02140 $notEqParentString 02141 ezcontentobject_tree.contentobject_id = ezcontentobject.id AND 02142 ezcontentclass.id = ezcontentobject.contentclass_id AND 02143 $mainNodeOnlyCond 02144 $classCondition 02145 $versionNameJoins 02146 $showInvisibleNodesCond 02147 $sqlPermissionChecking[where] 02148 $languageFilter 02149 ) 02150 OR"; 02151 } 02152 02153 $groupBySelectText = ''; 02154 $groupBySQL = $extendedAttributeFilter['group_by']; 02155 if ( !$groupBySQL ) 02156 { 02157 eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy ); 02158 } 02159 else if ( $groupBy ) 02160 { 02161 eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ ); 02162 } 02163 02164 $query = "SELECT DISTINCT 02165 ezcontentobject.*, 02166 ezcontentobject_tree.*, 02167 ezcontentclass.serialized_name_list as class_serialized_name_list, 02168 ezcontentclass.identifier as class_identifier, 02169 ezcontentclass.is_container as is_container 02170 $groupBySelectText 02171 $versionNameTargets 02172 $sortingInfo[attributeTargetSQL] 02173 , ".$nodeParams['ResultID']." AS resultid 02174 FROM 02175 ezcontentobject_tree, 02176 ezcontentobject,ezcontentclass 02177 $versionNameTables 02178 $sortingInfo[attributeFromSQL] 02179 $attributeFilter[from] 02180 $extendedAttributeFilter[tables] 02181 $sqlPermissionChecking[from] 02182 WHERE 02183 ".substr($queryNodes, 0, -2)." 02184 $groupBySQL"; 02185 02186 if ( $sortingInfo['sortingFields'] ) 02187 { 02188 $query .= " ORDER BY $sortingInfo[sortingFields]"; 02189 } 02190 02191 $db = eZDB::instance(); 02192 02193 $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false; 02194 02195 if ( !$offset && !$limit ) 02196 { 02197 $nodeListArray = $db->arrayQuery( $query, array(), $server ); 02198 } 02199 else 02200 { 02201 $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset, 02202 'limit' => $limit ), 02203 $server ); 02204 } 02205 02206 if ( $asObject ) 02207 { 02208 $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 02209 } 02210 else 02211 { 02212 $retNodeList = $nodeListArray; 02213 } 02214 02215 // cleanup temp tables 02216 $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] ); 02217 02218 return $retNodeList; 02219 } 02220 02221 static function subTreeGroupByDateField( $field, $type ) 02222 { 02223 $divisor = 0; 02224 switch ( $type ) 02225 { 02226 case 'year': 02227 { 02228 $divisor = 60*60*24*365; 02229 } break; 02230 case 'week': 02231 { 02232 $divisor = 60*60*24*7; 02233 } break; 02234 case 'day': 02235 { 02236 $divisor = 60*60*24; 02237 } break; 02238 case 'hour': 02239 { 02240 $divisor = 60*60; 02241 } break; 02242 case 'minute': 02243 { 02244 $divisor = 60; 02245 } break; 02246 case 'second': 02247 { 02248 $divisor = 0; 02249 } break; 02250 default: 02251 { 02252 eZDebug::writeError( "Unknown field type $type", __METHOD__ ); 02253 } 02254 } 02255 if ( $divisor > 0 ) 02256 $text = "( $field / $divisor ) AS groupbyfield"; 02257 else 02258 $text = "$field AS groupbyfield"; 02259 return array( 'select' => $text, 02260 'group_field' => "( $field / $divisor )" ); 02261 } 02262 02263 02264 /*! 02265 \sa subTreeCount 02266 */ 02267 static function subTreeCountByNodeID( $params = array(), $nodeID ) 02268 { 02269 if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) ) 02270 { 02271 return null; 02272 } 02273 02274 $language = ( isset( $params['Language'] ) ) ? $params['Language'] : false; 02275 02276 if ( $language ) 02277 { 02278 if ( !is_array( $language ) ) 02279 { 02280 $language = array( $language ); 02281 } 02282 eZContentLanguage::setPrioritizedLanguages( $language ); 02283 } 02284 02285 $depth = isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ? $params['Depth'] : false; 02286 $depthOperator = isset( $params['DepthOperator'] ) ? $params['DepthOperator'] : false; 02287 02288 $pathStringCond = ''; 02289 $notEqParentString = ''; 02290 // If the node(s) doesn't exist we return null. 02291 if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) ) 02292 { 02293 return null; 02294 } 02295 02296 $db = eZDB::instance(); 02297 $ini = eZINI::instance(); 02298 02299 // Check for class filtering 02300 $classCondition = ''; 02301 02302 if ( isset( $params['ClassFilterType'] ) and isset( $params['ClassFilterArray'] ) and 02303 ( $params['ClassFilterType'] == 'include' or $params['ClassFilterType'] == 'exclude' ) 02304 and count( $params['ClassFilterArray'] ) > 0 ) 02305 { 02306 $classCondition = ' '; 02307 $i = 0; 02308 $classCount = count( $params['ClassFilterArray'] ); 02309 $classIDArray = array(); 02310 foreach ( $params['ClassFilterArray'] as $classID ) 02311 { 02312 $originalClassID = $classID; 02313 // Check if classes are recerenced by identifier 02314 if ( is_string( $classID ) && !is_numeric( $classID ) ) 02315 { 02316 $classID = eZContentClass::classIDByIdentifier( $classID ); 02317 } 02318 if ( is_numeric( $classID ) ) 02319 { 02320 $classIDArray[] = $classID; 02321 } 02322 else 02323 { 02324 eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID ); 02325 } 02326 } 02327 if ( count( $classIDArray ) > 0 ) 02328 { 02329 $classCondition .= " ezcontentobject.contentclass_id "; 02330 if ( $params['ClassFilterType'] == 'include' ) 02331 $classCondition .= " IN "; 02332 else 02333 $classCondition .= " NOT IN "; 02334 02335 $classIDString = implode( ', ', $classIDArray ); 02336 $classCondition .= ' ( ' . $classIDString . ' ) AND'; 02337 } 02338 } 02339 02340 02341 // Main node check 02342 $mainNodeOnlyCond = ''; 02343 if ( isset( $params['MainNodeOnly'] ) && $params['MainNodeOnly'] === true ) 02344 { 02345 $mainNodeOnlyCond = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND'; 02346 } 02347 02348 $attributeFilterParam = isset( $params['AttributeFilter'] ) ? $params['AttributeFilter'] : false; 02349 $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $attributeFilterParam ); 02350 if ( $attributeFilter === false ) 02351 { 02352 return null; 02353 } 02354 02355 //$onlyTranslated = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated'] : false; 02356 02357 $useVersionName = true; 02358 02359 $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName ); 02360 $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName ); 02361 $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false ); 02362 02363 $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' ); 02364 02365 if ( $language ) 02366 { 02367 eZContentLanguage::clearPrioritizedLanguages(); 02368 } 02369 $objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false; 02370 $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter ); 02371 02372 $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] ); 02373 02374 // Determine whether we should show invisible nodes. 02375 $ignoreVisibility = isset( $params['IgnoreVisibility'] ) ? $params['IgnoreVisibility'] : false; 02376 $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility ); 02377 02378 $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false; 02379 $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation ); 02380 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 02381 02382 $query = "SELECT 02383 count( DISTINCT ezcontentobject_tree.node_id ) as count 02384 FROM 02385 ezcontentobject_tree, 02386 ezcontentobject,ezcontentclass 02387 $versionNameTables 02388 $attributeFilter[from] 02389 $extendedAttributeFilter[tables] 02390 $sqlPermissionChecking[from] 02391 WHERE $pathStringCond 02392 $extendedAttributeFilter[joins] 02393 $mainNodeOnlyCond 02394 $classCondition 02395 $attributeFilter[where] 02396 ezcontentclass.version=0 AND 02397 $notEqParentString 02398 ezcontentobject_tree.contentobject_id = ezcontentobject.id AND 02399 ezcontentclass.id = ezcontentobject.contentclass_id AND 02400 $versionNameJoins 02401 $showInvisibleNodesCond 02402 $sqlPermissionChecking[where] 02403 $objectNameFilterSQL 02404 $languageFilter "; 02405 02406 $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false; 02407 02408 $nodeListArray = $db->arrayQuery( $query, array(), $server ); 02409 02410 // cleanup temp tables 02411 $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] ); 02412 02413 return $nodeListArray[0]['count']; 02414 } 02415 02416 /*! 02417 Count number of subnodes 02418 02419 \param params array 02420 */ 02421 function subTreeCount( $params = array() ) 02422 { 02423 return eZContentObjectTreeNode::subTreeCountByNodeID( $params, $this->attribute( 'node_id' ) ); 02424 } 02425 02426 /*! 02427 \return The date/time list when object were published 02428 */ 02429 static function calendar( $params = false, $nodeID = 0 ) 02430 { 02431 if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) ) 02432 { 02433 return array(); 02434 } 02435 02436 if ( $params === false ) 02437 { 02438 $params = array( 'Depth' => false, 02439 'Offset' => false, 02440 'Limit' => false, 02441 'AttributeFilter' => false, 02442 'ExtendedAttributeFilter' => false, 02443 'ClassFilterType' => false, 02444 'ClassFilterArray' => false, 02445 'GroupBy' => false ); 02446 } 02447 02448 $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false; 02449 $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false; 02450 $depth = ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) ? $params['Depth'] : false; 02451 $depthOperator = ( isset( $params['DepthOperator'] ) ) ? $params['DepthOperator'] : false; 02452 $groupBy = ( isset( $params['GroupBy'] ) ) ? $params['GroupBy'] : false; 02453 $mainNodeOnly = ( isset( $params['MainNodeOnly'] ) ) ? $params['MainNodeOnly'] : false; 02454 $ignoreVisibility = ( isset( $params['IgnoreVisibility'] ) ) ? $params['IgnoreVisibility'] : false; 02455 if ( !isset( $params['ClassFilterType'] ) ) 02456 $params['ClassFilterType'] = false; 02457 02458 $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] ); 02459 $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo ); 02460 $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] ); 02461 $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly ); 02462 02463 $pathStringCond = ''; 02464 $notEqParentString = ''; 02465 eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ); 02466 02467 $groupBySelectText = ''; 02468 $groupBySQL = $extendedAttributeFilter['group_by']; 02469 if ( !$groupBySQL ) 02470 { 02471 eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy ); 02472 } 02473 else if ( $groupBy ) 02474 { 02475 eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ ); 02476 } 02477 02478 $useVersionName = true; 02479 $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString( $useVersionName ); 02480 $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString( $useVersionName, false ); 02481 02482 $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false; 02483 $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation ); 02484 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 02485 02486 // Determine whether we should show invisible nodes. 02487 $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility ); 02488 02489 $query = "SELECT DISTINCT 02490 ezcontentobject.published as published 02491 $groupBySelectText 02492 FROM 02493 ezcontentobject_tree, 02494 ezcontentobject,ezcontentclass 02495 $versionNameTables 02496 $attributeFilter[from] 02497 $extendedAttributeFilter[tables] 02498 $sqlPermissionChecking[from] 02499 WHERE 02500 $pathStringCond 02501 $extendedAttributeFilter[joins] 02502 $attributeFilter[where] 02503 ezcontentclass.version=0 02504 AND 02505 $notEqParentString 02506 $mainNodeOnlyCond 02507 ezcontentobject_tree.contentobject_id = ezcontentobject.id AND 02508 ezcontentclass.id = ezcontentobject.contentclass_id AND 02509 $classCondition 02510 $versionNameJoins 02511 $showInvisibleNodesCond 02512 $sqlPermissionChecking[where] 02513 $groupBySQL"; 02514 02515 02516 $db = eZDB::instance(); 02517 02518 $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false; 02519 02520 if ( !$offset && !$limit ) 02521 { 02522 $nodeListArray = $db->arrayQuery( $query, array(), $server ); 02523 } 02524 else 02525 { 02526 $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset, 02527 'limit' => $limit ), 02528 $server ); 02529 } 02530 02531 // cleanup temp tables 02532 $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] ); 02533 02534 return $nodeListArray; 02535 } 02536 /*! 02537 \return the children(s) of the current node as an array of eZContentObjectTreeNode objects 02538 */ 02539 function childrenByName( $name ) 02540 { 02541 $nodeID = $this->attribute( 'node_id' ); 02542 02543 $fromNode = $nodeID ; 02544 02545 $nodePath = $this->attribute( 'path_string' ); 02546 $nodeDepth = $this->attribute( 'depth' ); 02547 02548 $childrensPath = $nodePath ; 02549 02550 $db = eZDB::instance(); 02551 $pathString = " path_string like '$childrensPath%' and "; 02552 $depthCond = ''; 02553 $nodeDepth = $this->Depth + 1; 02554 $depthCond = ' depth <= ' . $nodeDepth . ' and '; 02555 02556 $ini = eZINI::instance(); 02557 $db = eZDB::instance(); 02558 02559 $name = $db->escapeString( $name ); 02560 $query = "SELECT ezcontentobject.*, 02561 ezcontentobject_tree.*, 02562 ezcontentclass.serialized_name_list as class_serialized_name_list, 02563 ezcontentclass.identifier as class_identifier, 02564 ezcontentclass.is_container as is_container 02565 FROM 02566 ezcontentobject_tree, 02567 ezcontentobject,ezcontentclass 02568 WHERE $pathString 02569 $depthCond 02570 ezcontentobject.name = '$name' AND 02571 ezcontentclass.version=0 AND 02572 node_id != $fromNode AND 02573 ezcontentobject_tree.contentobject_id = ezcontentobject.id AND 02574 ezcontentclass.id = ezcontentobject.contentclass_id"; 02575 02576 $nodeListArray = $db->arrayQuery( $query ); 02577 02578 return eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 02579 } 02580 02581 /*! 02582 Returns the first level children in sorted order. 02583 */ 02584 function children() 02585 { 02586 return $this->subTree( array( 'Depth' => 1, 02587 'DepthOperator' => 'eq', 02588 'SortBy' => $this->sortArray() ) ); 02589 } 02590 02591 /*! 02592 Returns the number of children for the current node. 02593 \params $checkPolicies If \c true it will only include nodes which can be read using the current policies, 02594 if \c false all nodes are included in count. 02595 */ 02596 function childrenCount( $checkPolicies = true ) 02597 { 02598 $params = array( 'Depth' => 1, 02599 'DepthOperator' => 'eq' ); 02600 if ( !$checkPolicies ) 02601 $params['Limitation'] = array(); 02602 return $this->subTreeCount( $params ); 02603 } 02604 02605 /*! 02606 Get amount views of content node. 02607 */ 02608 function viewCount() 02609 { 02610 $count = eZViewCounter::fetch( $this->attribute( 'node_id' ), false ); 02611 return (int) $count['count']; 02612 } 02613 02614 /*! 02615 \return the sort field name for the numeric sort field ID \a $sortFieldID. 02616 Gives a warning if the ID is unknown and returns \c 'path'. 02617 */ 02618 static function sortFieldName( $sortFieldID ) 02619 { 02620 switch ( $sortFieldID ) 02621 { 02622 default: 02623 eZDebug::writeWarning( 'Unknown sort field ID: ' . $sortFieldID, __METHOD__ ); 02624 case self::SORT_FIELD_PATH: 02625 return 'path'; 02626 case self::SORT_FIELD_PUBLISHED: 02627 return 'published'; 02628 case self::SORT_FIELD_MODIFIED: 02629 return 'modified'; 02630 case self::SORT_FIELD_SECTION: 02631 return 'section'; 02632 case self::SORT_FIELD_DEPTH: 02633 return 'depth'; 02634 case self::SORT_FIELD_CLASS_IDENTIFIER: 02635 return 'class_identifier'; 02636 case self::SORT_FIELD_CLASS_NAME: 02637 return 'class_name'; 02638 case self::SORT_FIELD_PRIORITY: 02639 return 'priority'; 02640 case self::SORT_FIELD_NAME: 02641 return 'name'; 02642 case self::SORT_FIELD_MODIFIED_SUBNODE: 02643 return 'modified_subnode'; 02644 case self::SORT_FIELD_NODE_ID: 02645 return 'node_id'; 02646 case self::SORT_FIELD_CONTENTOBJECT_ID: 02647 return 'contentobject_id'; 02648 } 02649 } 02650 02651 /*! 02652 \return the numeric sort field ID for the sort field name \a $sortFieldName. 02653 Gives a warning if the name is unknown and returns self::SORT_FIELD_PATH. 02654 */ 02655 static function sortFieldID( $sortFieldName ) 02656 { 02657 switch ( $sortFieldName ) 02658 { 02659 default: 02660 eZDebug::writeWarning( 'Unknown sort field name: ' . $sortFieldName, __METHOD__ ); 02661 case 'path': 02662 return self::SORT_FIELD_PATH; 02663 case 'published': 02664 return self::SORT_FIELD_PUBLISHED; 02665 case 'modified': 02666 return self::SORT_FIELD_MODIFIED; 02667 case 'section': 02668 return self::SORT_FIELD_SECTION; 02669 case 'depth': 02670 return self::SORT_FIELD_DEPTH; 02671 case 'class_identifier': 02672 return self::SORT_FIELD_CLASS_IDENTIFIER; 02673 case 'class_name': 02674 return self::SORT_FIELD_CLASS_NAME; 02675 case 'priority': 02676 return self::SORT_FIELD_PRIORITY; 02677 case 'name': 02678 return self::SORT_FIELD_NAME; 02679 case 'modified_subnode': 02680 return self::SORT_FIELD_MODIFIED_SUBNODE; 02681 case 'node_id': 02682 return self::SORT_FIELD_NODE_ID; 02683 case 'contentobject_id': 02684 return self::SORT_FIELD_CONTENTOBJECT_ID; 02685 } 02686 } 02687 02688 /*! 02689 \return an array which defines the sorting method for this node. 02690 The array will contain one element which is an array with sort field 02691 and sort order. 02692 */ 02693 function sortArray() 02694 { 02695 return eZContentObjectTreeNode::sortArrayBySortFieldAndSortOrder( $this->attribute( 'sort_field' ), 02696 $this->attribute( 'sort_order' ) ); 02697 } 02698 02699 /*! 02700 \static 02701 \return an array which defines the sorting method for this node. 02702 The array will contain one element which is an array with sort field 02703 and sort order. 02704 */ 02705 static function sortArrayBySortFieldAndSortOrder( $sortField, $sortOrder ) 02706 { 02707 return array( array( eZContentObjectTreeNode::sortFieldName( $sortField ), 02708 $sortOrder ) ); 02709 } 02710 02711 /*! 02712 Will assign a section to the current node and all child objects. 02713 Only main node assignments will be updated. 02714 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02715 the calls within a db transaction; thus within db->begin and db->commit. 02716 */ 02717 static function assignSectionToSubTree( $nodeID, $sectionID, $oldSectionID = false ) 02718 { 02719 $db = eZDB::instance(); 02720 02721 $node = eZContentObjectTreeNode::fetch( $nodeID ); 02722 $nodePath = $node->attribute( 'path_string' ); 02723 02724 $sectionID =(int) $sectionID; 02725 02726 $pathString = " path_string like '$nodePath%' AND "; 02727 02728 // fetch the object id's which needs to be updated 02729 $objectIDArray = $db->arrayQuery( "SELECT 02730 ezcontentobject.id 02731 FROM 02732 ezcontentobject_tree, ezcontentobject 02733 WHERE 02734 $pathString 02735 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 02736 ezcontentobject_tree.main_node_id=ezcontentobject_tree.node_id" ); 02737 if ( count( $objectIDArray ) == 0 ) 02738 return; 02739 02740 // Who assigns which section at which node should be logged. 02741 $section = eZSection::fetch( $sectionID ); 02742 $object = $node->object(); 02743 eZAudit::writeAudit( 'section-assign', array( 'Section ID' => $sectionID, 'Section name' => $section->attribute( 'name' ), 02744 'Node ID' => $nodeID, 02745 'Content object ID' => $object->attribute( 'id' ), 02746 'Content object name' => $object->attribute( 'name' ), 02747 'Comment' => 'Assigned a section to the current node and all child objects: eZContentObjectTreeNode::assignSectionToSubTree()' ) ); 02748 02749 $objectSimpleIDArray = array(); 02750 foreach ( $objectIDArray as $objectID ) 02751 { 02752 $objectSimpleIDArray[] = $objectID['id']; 02753 } 02754 02755 $filterPart = ''; 02756 if ( $oldSectionID !== false ) 02757 { 02758 $oldSectionID =(int) $oldSectionID; 02759 $filterPart = " section_id = '$oldSectionID' and "; 02760 } 02761 02762 $db->begin(); 02763 foreach ( array_chunk( $objectSimpleIDArray, 100 ) as $pagedObjectIDs ) 02764 { 02765 $db->query( "UPDATE ezcontentobject SET section_id='$sectionID' WHERE $filterPart " . $db->generateSQLINStatement( $pagedObjectIDs, 'id', false, true, 'int' ) ); 02766 eZSearch::updateObjectsSection( $pagedObjectIDs, $sectionID ); 02767 } 02768 $db->commit(); 02769 02770 // clear caches for updated objects 02771 eZContentObject::clearCache( $objectSimpleIDArray ); 02772 } 02773 02774 /*! 02775 \static 02776 Updates the main node selection for the content object \a $objectID. 02777 02778 \param $mainNodeID The ID of the node that should be that main node 02779 \param $objectID The ID of the object that all nodes belong to 02780 \param $version The version of the object to update node assignments, use \c false for currently published version. 02781 \param $parentMainNodeID The ID of the parent node of the new main placement 02782 02783 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02784 the calls within a db transaction; thus within db->begin and db->commit. 02785 */ 02786 static function updateMainNodeID( $mainNodeID, $objectID, $version = false, $parentMainNodeID, $updateSection = true ) 02787 { 02788 $mainNodeID = (int)$mainNodeID; 02789 $parentMainNodeID = (int)$parentMainNodeID; 02790 $objectID = (int)$objectID; 02791 $version = (int)$version; 02792 02793 $db = eZDB::instance(); 02794 $db->begin(); 02795 $db->query( "UPDATE ezcontentobject_tree SET main_node_id=$mainNodeID WHERE contentobject_id=$objectID" ); 02796 if ( !$version ) 02797 { 02798 $rows = $db->arrayQuery( "SELECT current_version FROM ezcontentobject WHERE id=$objectID" ); 02799 $version = $rows[0]['current_version']; 02800 } 02801 $db->query( "UPDATE eznode_assignment SET is_main=1 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node=$parentMainNodeID" ); 02802 $db->query( "UPDATE eznode_assignment SET is_main=0 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node!=$parentMainNodeID" ); 02803 02804 $contentObject = eZContentObject::fetch( $objectID ); 02805 $parentContentObject = eZContentObject::fetchByNodeID( $parentMainNodeID ); 02806 if ( $updateSection && $contentObject->attribute( 'section_id' ) != $parentContentObject->attribute( 'section_id' ) ) 02807 { 02808 $newSectionID = $parentContentObject->attribute( 'section_id' ); 02809 eZContentObjectTreeNode::assignSectionToSubTree( $mainNodeID, $newSectionID ); 02810 } 02811 02812 $db->commit(); 02813 02814 } 02815 02816 function fetchByCRC( $pathStr ) 02817 { 02818 eZDebug::writeWarning( "Obsolete: use eZURLAlias instead", __METHOD__ ); 02819 return null; 02820 } 02821 02822 static function fetchByContentObjectID( $contentObjectID, $asObject = true, $contentObjectVersion = false ) 02823 { 02824 $conds = array( 'contentobject_id' => $contentObjectID ); 02825 if ( $contentObjectVersion !== false ) 02826 { 02827 $conds['contentobject_version'] = $contentObjectVersion; 02828 } 02829 return eZPersistentObject::fetchObjectList( eZContentObjectTreeNode::definition(), 02830 null, 02831 $conds, 02832 null, 02833 null, 02834 $asObject ); 02835 } 02836 02837 static function fetchByRemoteID( $remoteID, $asObject = true ) 02838 { 02839 return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "remote_id" => $remoteID ) ); 02840 } 02841 02842 static function fetchByPath( $pathString, $asObject = true ) 02843 { 02844 return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_string" => $pathString ) ); 02845 } 02846 02847 static function fetchByURLPath( $pathString, $asObject = true ) 02848 { 02849 if ( $pathString == "" ) 02850 { 02851 eZDebug::writeWarning( 'Can not fetch, given URLPath is empty', __METHOD__ ); 02852 return null; 02853 } 02854 02855 return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_identification_string" => $pathString ) ); 02856 } 02857 02858 /** 02859 * Fetches path_identification_string for a list of nodes 02860 * 02861 * @param array(int) $nodeList 02862 * 02863 * @return array Associative array 02864 */ 02865 static function fetchAliasesFromNodeList( $nodeList ) 02866 { 02867 if ( !is_array( $nodeList ) || count( $nodeList ) < 1 ) 02868 return array(); 02869 02870 $db = eZDB::instance(); 02871 $where = $db->generateSQLINStatement( $nodeList, 'node_id', false, false, 'int' ); 02872 $query = "SELECT node_id, path_identification_string FROM ezcontentobject_tree WHERE $where"; 02873 $pathListArray = $db->arrayQuery( $query ); 02874 return $pathListArray; 02875 } 02876 02877 /** 02878 * Get Main Node Id ( or Main Node if $asObject = true ) by Content Object Id. 02879 * 02880 * @param int $objectID 02881 * @param boolean $asObject 02882 * 02883 * @return int|null 02884 */ 02885 static function findMainNode( $objectID, $asObject = false ) 02886 { 02887 $objectID = (int)$objectID; 02888 $query = "SELECT node_id 02889 FROM ezcontentobject_tree 02890 WHERE contentobject_id=$objectID AND 02891 main_node_id = node_id"; 02892 $db = eZDB::instance(); 02893 $nodeListArray = $db->arrayQuery( $query ); 02894 if ( count( $nodeListArray ) == 1 ) 02895 { 02896 if ( $asObject ) 02897 { 02898 return eZContentObjectTreeNode::fetch( $nodeListArray[0]['node_id'] ); 02899 } 02900 else 02901 { 02902 return $nodeListArray[0]['node_id']; 02903 } 02904 02905 } 02906 else if ( count( $nodeListArray ) > 1 ) 02907 { 02908 eZDebug::writeError( $nodeListArray , "There are more then one main_node for objectID: $objectID" ); 02909 } 02910 02911 return null; 02912 } 02913 02914 /** 02915 * Fetches the main nodes for an array of object id's 02916 * @param array(int) $objectIDArray an array of object IDs 02917 * @param bool $asObject 02918 * Wether to return the result as an array of eZContentObjectTreeNode 02919 * (true) or as an array of associative arrays (false) 02920 * @return array(array|eZContentObjectTreeNode) 02921 */ 02922 static function findMainNodeArray( $objectIDArray, $asObject = true ) 02923 { 02924 if ( count( $objectIDArray ) ) 02925 { 02926 $db = eZDB::instance(); 02927 $objectIDINSQL = $db->generateSQLINStatement( $objectIDArray, 'ezcontentobject_tree.contentobject_id', false, false, 'int' ); 02928 $query="SELECT ezcontentobject.*, 02929 ezcontentobject_tree.*, 02930 ezcontentclass.serialized_name_list as class_serialized_name_list, 02931 ezcontentclass.identifier as class_identifier, 02932 ezcontentclass.is_container as is_container 02933 FROM ezcontentobject_tree, 02934 ezcontentobject, 02935 ezcontentclass 02936 WHERE $objectIDINSQL AND 02937 ezcontentobject_tree.main_node_id = ezcontentobject_tree.node_id AND 02938 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 02939 ezcontentclass.version=0 AND 02940 ezcontentclass.id = ezcontentobject.contentclass_id"; 02941 02942 $nodeListArray = $db->arrayQuery( $query ); 02943 if ( $asObject ) 02944 { 02945 $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 02946 return $retNodeArray; 02947 } 02948 else 02949 { 02950 return $nodeListArray; 02951 } 02952 } 02953 return null; 02954 } 02955 02956 02957 /** 02958 * Fetches a node by ID 02959 * 02960 * @param int|array $nodeID Either a node ID or array of node IDs 02961 * @param string $lang language code to fetch the node in. If not provided, the prioritized language list is used 02962 * @param bool $asObject True to fetch the node as an eZContentObjectTreeNode, false to fetch its attributes as an array 02963 * @param array $conditions An associative array (field => value) of fetch conditions. Will be applied as is to the SQL query 02964 * 02965 * @return eZContentObjectTreeNode 02966 */ 02967 static function fetch( $nodeID = false, $lang = false, $asObject = true, $conditions = false ) 02968 { 02969 $returnValue = null; 02970 $db = eZDB::instance(); 02971 if ( ( is_numeric( $nodeID ) && $nodeID == 1 ) || 02972 ( is_array( $nodeID ) && count( $nodeID ) === 1 && $nodeID[0] == 1 ) ) 02973 { 02974 $query = "SELECT * 02975 FROM ezcontentobject_tree 02976 WHERE node_id = 1"; 02977 } 02978 else 02979 { 02980 $versionNameTables = ', ezcontentobject_name '; 02981 $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation '; 02982 $versionNameJoins = " and ezcontentobject_tree.contentobject_id = ezcontentobject_name.contentobject_id and 02983 ezcontentobject_tree.contentobject_version = ezcontentobject_name.content_version and "; 02984 if ( $lang ) 02985 { 02986 $lang = $db->escapeString( $lang ); 02987 $versionNameJoins .= " ezcontentobject_name.content_translation = '$lang' "; 02988 } 02989 else 02990 { 02991 $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ); 02992 } 02993 02994 $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' ); 02995 02996 $sqlCondition = ''; 02997 02998 if ( $nodeID !== false ) 02999 { 03000 if ( is_array( $nodeID ) ) 03001 { 03002 if( count( $nodeID ) === 1 ) 03003 { 03004 $sqlCondition = 'node_id = ' . (int) $nodeID[0] . ' AND '; 03005 } 03006 else 03007 { 03008 $sqlCondition = $db->generateSQLINStatement( $nodeID, 'node_id', false, true, 'int' ) . ' AND '; 03009 } 03010 } 03011 else 03012 { 03013 $sqlCondition = 'node_id = ' . (int) $nodeID . ' AND '; 03014 } 03015 } 03016 03017 if ( is_array( $conditions ) ) 03018 { 03019 foreach( $conditions as $key => $condition ) 03020 { 03021 if ( is_string( $condition ) ) 03022 { 03023 $condition = $db->escapeString( $condition ); 03024 $condition = "'$condition'"; 03025 } 03026 03027 $sqlCondition .= "ezcontentobject_tree." . $db->escapeString( $key ) . "=$condition AND "; 03028 } 03029 } 03030 03031 if ( $sqlCondition == '' ) 03032 { 03033 eZDebug::writeWarning( 'Cannot fetch node, emtpy ID or no conditions given', __METHOD__ ); 03034 return $returnValue; 03035 } 03036 03037 $query="SELECT ezcontentobject.*, 03038 ezcontentobject_tree.*, 03039 ezcontentclass.serialized_name_list as class_serialized_name_list, 03040 ezcontentclass.identifier as class_identifier, 03041 ezcontentclass.is_container as is_container 03042 $versionNameTargets 03043 FROM ezcontentobject_tree, 03044 ezcontentobject, 03045 ezcontentclass 03046 $versionNameTables 03047 WHERE $sqlCondition 03048 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 03049 ezcontentclass.version=0 AND 03050 ezcontentclass.id = ezcontentobject.contentclass_id 03051 $languageFilter 03052 $versionNameJoins"; 03053 } 03054 $nodeListArray = $db->arrayQuery( $query ); 03055 03056 if ( count( $nodeListArray ) > 0 ) 03057 { 03058 if ( $asObject ) 03059 { 03060 $returnValue = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 03061 if ( count( $returnValue ) === 1 ) 03062 $returnValue = $returnValue[0]; 03063 } 03064 else 03065 { 03066 if ( count( $nodeListArray ) === 1 ) 03067 $returnValue = $nodeListArray[0]; 03068 else 03069 $returnValue = $nodeListArray; 03070 } 03071 } 03072 03073 return $returnValue; 03074 } 03075 03076 /*! 03077 \static 03078 Finds the node for the object \a $contentObjectID which placed as child of node \a $parentNodeID. 03079 \return An eZContentObjectTreeNode object or \c null if no node was found. 03080 */ 03081 static function fetchNode( $contentObjectID, $parentNodeID ) 03082 { 03083 $returnValue = null; 03084 $ini = eZINI::instance(); 03085 $db = eZDB::instance(); 03086 $contentObjectID =(int) $contentObjectID; 03087 $parentNodeID =(int) $parentNodeID; 03088 $query = "SELECT ezcontentobject_tree.*, 03089 ezcontentclass.serialized_name_list as class_serialized_name_list, 03090 ezcontentclass.identifier as class_identifier, 03091 ezcontentclass.is_container as is_container 03092 FROM ezcontentobject_tree, 03093 ezcontentobject, 03094 ezcontentclass 03095 WHERE ezcontentobject_tree.contentobject_id = '$contentObjectID' AND 03096 ezcontentobject.id = '$contentObjectID' AND 03097 ezcontentobject_tree.parent_node_id = '$parentNodeID' AND 03098 ezcontentclass.version=0 AND 03099 ezcontentclass.id = ezcontentobject.contentclass_id AND ". 03100 eZContentLanguage::languagesSQLFilter( 'ezcontentobject' ); 03101 03102 $nodeListArray = $db->arrayQuery( $query ); 03103 if ( count( $nodeListArray ) == 1 ) 03104 { 03105 $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray, false ); 03106 $returnValue = $retNodeArray[0]; 03107 } 03108 return $returnValue; 03109 } 03110 03111 function fetchParent() 03112 { 03113 return $this->fetch( $this->attribute( 'parent_node_id' ) ); 03114 } 03115 03116 function pathArray() 03117 { 03118 $pathString = $this->attribute( 'path_string' ); 03119 $pathItems = explode( '/', $pathString ); 03120 $pathArray = array(); 03121 foreach ( $pathItems as $pathItem ) 03122 { 03123 if ( $pathItem != '' ) 03124 $pathArray[] = (int) $pathItem; 03125 } 03126 return $pathArray; 03127 } 03128 03129 03130 function fetchPath() 03131 { 03132 $nodePath = $this->attribute( 'path_string' ); 03133 03134 return eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, false, true ); 03135 } 03136 03137 /*! 03138 \static 03139 \return An array with content node objects that is present in the node path \a $nodePath. 03140 \param $withLastNode If \c true the last node in the path is included in the list. 03141 The last node is the node which the path was fetched from. 03142 \param $asObjects If \c true then return PHP objects, if not return raw row data. 03143 \param $limit maximum number of nodes in the path to use, starting from last node 03144 */ 03145 static function fetchNodesByPathString( $nodePath, $withLastNode = false, $asObjects = true, $limit = false ) 03146 { 03147 $nodesListArray = array(); 03148 $pathString = eZContentObjectTreeNode::createNodesConditionSQLStringFromPath( $nodePath, $withLastNode, $limit ); 03149 03150 if ( $pathString ) 03151 { 03152 $useVersionName = true; 03153 $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName ); 03154 $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName ); 03155 $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName ); 03156 03157 $query = "SELECT ezcontentobject.*, 03158 ezcontentobject_tree.*, 03159 ezcontentclass.serialized_name_list as class_serialized_name_list, 03160 ezcontentclass.identifier as class_identifier, 03161 ezcontentclass.is_container as is_container 03162 $versionNameTargets 03163 FROM ezcontentobject_tree, 03164 ezcontentobject, 03165 ezcontentclass 03166 $versionNameTables 03167 WHERE $pathString 03168 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 03169 ezcontentclass.version=0 AND 03170 ezcontentclass.id = ezcontentobject.contentclass_id 03171 $versionNameJoins 03172 ORDER BY path_string"; 03173 03174 $db = eZDB::instance(); 03175 $nodesListArray = $db->arrayQuery( $query ); 03176 } 03177 03178 if ( $asObjects ) 03179 { 03180 return eZContentObjectTreeNode::makeObjectsArray( $nodesListArray ); 03181 } 03182 return $nodesListArray; 03183 } 03184 03185 03186 /*! 03187 \static 03188 Extracts each node that in the path from db and returns an array of class identifiers 03189 \param $nodePath A string containing the path of the node, it consists of 03190 node IDs starting from the root and delimited by / (slash). 03191 \param $withLastNode If \c true the last node in the path is included in the list. 03192 The last node is the node which the path was fetched from. 03193 \param $limit maximum number of nodes in the path to use, starting from last node 03194 \return An array with class identifier and node ID. 03195 03196 Example 03197 \code 03198 $list = fetchClassIdentifierListByPathString( '/2/10/', false ); 03199 \endcode 03200 */ 03201 static function fetchClassIdentifierListByPathString( $nodePath, $withLastNode, $limit = false ) 03202 { 03203 $itemList = array(); 03204 $nodes = eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, $withLastNode, false, $limit ); 03205 03206 foreach ( $nodes as $node ) 03207 { 03208 $itemList[] = array( 'node_id' => $node['node_id'], 03209 'class_identifier' => $node['class_identifier'] ); 03210 } 03211 03212 return $itemList; 03213 } 03214 03215 /*! 03216 \deprecated This function should no longer be used, use the eZContentClass::instantiate and eZNodeAssignment::create instead. 03217 */ 03218 function createObject( $contentClassID, $parentNodeID = 2 ) 03219 { 03220 $class = eZContentClass::fetch( $contentClassID ); 03221 $parentNode = eZContentObjectTreeNode::fetch( $parentNodeID ); 03222 $parentContentObject = $parentNode->attribute( 'object' ); 03223 $sectionID = $parentContentObject->attribute( 'section_id' ); 03224 $object = $class->instantiate( false, $sectionID ); 03225 03226 // $parentContentObject = $parentNode->attribute( 'contentobject' ); 03227 03228 $node = eZContentObjectTreeNode::addChildTo( $object->attribute( "id" ), $parentNodeID, true ); 03229 // $object->setAttribute( "main_node_id", $node->attribute( 'node_id' ) ); 03230 $node->setAttribute( 'main_node_id', $node->attribute( 'node_id' ) ); 03231 $object->store(); 03232 $node->store(); 03233 03234 return $object; 03235 } 03236 03237 /*! 03238 Add a child for this node to the object tree. 03239 \param $contentobjectID The ID of the contentobject the child-node should point to. 03240 \param $asObject If true it will return the new child-node as an object, if not it returns the ID. 03241 \param $contentObjectVersion The version to use on the newly created child-node, if 03242 false it uses the current_version of the specified object. 03243 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03244 the calls within a db transaction; thus within db->begin and db->commit. 03245 */ 03246 function addChild( $contentobjectID, $asObject = false, $contentObjectVersion = false ) 03247 { 03248 return self::addChildTo( $contentobjectID, $this->attribute( 'node_id' ), $asObject, $contentObjectVersion ); 03249 } 03250 03251 /*! 03252 Add a child to the object tree. 03253 \param $contentobjectID The ID of the contentobject the child-node should point to. 03254 \param $nodeID The ID of the parent-node to add child-node to. 03255 \param $asObject If true it will return the new child-node as an object, if not it returns the ID. 03256 \param $contentObjectVersion The version to use on the newly created child-node, if 03257 false it uses the current_version of the specified object. 03258 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03259 the calls within a db transaction; thus within db->begin and db->commit. 03260 */ 03261 static function addChildTo( $contentobjectID, $nodeID, $asObject = false, $contentObjectVersion = false ) 03262 { 03263 $node = eZContentObjectTreeNode::fetch( $nodeID ); 03264 $contentObject = eZContentObject::fetch( $contentobjectID ); 03265 if ( !$contentObject ) 03266 { 03267 return false; 03268 } 03269 03270 if ( !$contentObjectVersion ) 03271 { 03272 $contentObjectVersion = $contentObject->attribute( 'current_version' ); 03273 } 03274 03275 $db = eZDB::instance(); 03276 $parentMainNodeID = $node->attribute( 'node_id' ); //$parent->attribute( 'main_node_id' ); 03277 $parentPath = $node->attribute( 'path_string' ); 03278 $parentDepth = $node->attribute( 'depth' ); 03279 $isInvinsible = $node->attribute( 'is_invisible' ); 03280 03281 $nodeDepth = $parentDepth + 1 ; 03282 03283 $insertedNode = eZContentObjectTreeNode::create( $parentMainNodeID, $contentobjectID ); 03284 03285 // set default sorting from content class 03286 $contentClass = $contentObject->attribute( 'content_class' ); 03287 $insertedNode->setAttribute( 'sort_field', $contentClass->attribute( 'sort_field' ) ); 03288 $insertedNode->setAttribute( 'sort_order', $contentClass->attribute( 'sort_order' ) ); 03289 03290 $insertedNode->setAttribute( 'depth', $nodeDepth ); 03291 $insertedNode->setAttribute( 'path_string', '/TEMPPATH' ); 03292 03293 $insertedNode->setAttribute( 'contentobject_version', $contentObjectVersion ); 03294 03295 // If the parent node is invisible, the new created node should be invisible as well. 03296 $insertedNode->setAttribute( 'is_invisible', $isInvinsible ); 03297 03298 $db->begin(); 03299 $insertedNode->store(); 03300 $insertedID = $insertedNode->attribute( 'node_id' ); 03301 $newNodePath = $parentPath . $insertedID . '/'; 03302 $insertedNode->setAttribute( 'path_string', $newNodePath ); 03303 $insertedNode->store(); 03304 $db->commit(); 03305 03306 if ( $asObject ) 03307 { 03308 return $insertedNode; 03309 } 03310 else 03311 { 03312 return $insertedID; 03313 } 03314 } 03315 03316 /*! 03317 \return an url alias for the current node. It will generate a unique alias. 03318 */ 03319 function pathWithNames( $regenerateCurrent = false ) 03320 { 03321 // Only set name if current node is not the content root 03322 $ini = eZINI::instance( 'content.ini' ); 03323 $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' ); 03324 if ( $this->attribute( 'node_id' ) != $contentRootID ) 03325 { 03326 $pathArray = $this->pathArray(); 03327 // Get rid of node with ID 1 (a special node) 03328 array_shift( $pathArray ); 03329 if ( $regenerateCurrent ) 03330 { 03331 // Get rid of current node, path element for this will be calculated 03332 array_pop( $pathArray ); 03333 } 03334 if ( count( $pathArray ) == 0 ) 03335 { 03336 $path = ''; 03337 } 03338 else 03339 { 03340 $path = eZURLAliasML::fetchPathByActionList( "eznode", $pathArray, $this->CurrentLanguage ); 03341 } 03342 03343 // Fallback in case fetchPathByActionList() fails, 03344 // then we ask for the path from the parent and generate the current 03345 // entry ourselves. 03346 if ( $path === null ) 03347 { 03348 if ( $this->attribute( 'depth' ) == 0 ) // Top node should just return empty string 03349 { 03350 return ''; 03351 } 03352 03353 eZDebug::writeError( __METHOD__ . "() failed to fetch path of node " . $this->attribute( 'node_id' ) . ", falling back to generated url entries. Run updateniceurls.php to fix the problem." ); 03354 03355 // Return a perma-link when the path lookup failed, this link will always work 03356 $path = 'content/view/full/' . $this->attribute( 'node_id' ); 03357 return $path; 03358 } 03359 03360 if ( $regenerateCurrent ) 03361 { 03362 $nodeName = $this->attribute( 'name' ); 03363 $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $this->attribute( 'node_id' ) ); 03364 03365 if ( $path != '' ) 03366 { 03367 $path .= '/' . $nodeName ; 03368 } 03369 else 03370 { 03371 $path = $nodeName ; 03372 } 03373 } 03374 } 03375 else 03376 { 03377 $path = ''; 03378 } 03379 03380 if ( $regenerateCurrent ) 03381 { 03382 $path = $this->checkPath( $path ); 03383 } 03384 return $path; 03385 } 03386 03387 /*! 03388 Check if a node with the same name already exists. If so create a $name + __x value. 03389 */ 03390 function checkPath( $path ) 03391 { 03392 $path = eZURLAliasML::cleanURL( $path ); 03393 $elements = explode( "/", $path ); 03394 $element = array_pop( $elements ); 03395 return $this->adjustPathElement( $element ); 03396 } 03397 03398 /*! 03399 Checks the path element $element against reserved words and existing elements. 03400 If the path element is already used, it will append a number and try again. 03401 03402 The adjusted path element is returned. 03403 03404 \param $element The desired url element name 03405 \param $useParentFromNodeObject Use the parent from node object as a base 03406 for checking name collisions. This is needed 03407 when moving nodes, and the url entries are 03408 not updated yet. 03409 03410 \code 03411 echo $node->adjustPathElement( 'Content' ); // outputs Content1 03412 \endcode 03413 */ 03414 function adjustPathElement( $element, $useParentFromNodeObject = false ) 03415 { 03416 $nodeID = (int)$this->attribute( 'node_id' ); 03417 $parentNodeID = (int)$this->attribute( 'parent_node_id' ); 03418 $action = "eznode:" . $nodeID; 03419 03420 $elements = eZURLAliasML::fetchByAction( 'eznode', $nodeID ); 03421 if ( count( $elements ) > 0 and !$useParentFromNodeObject ) 03422 { 03423 $parentElementID = (int)$elements[0]->attribute( 'parent' ); 03424 return eZURLAliasML::findUniqueText( $parentElementID, $element, $action ); 03425 } 03426 else 03427 { 03428 $parentElements = eZURLAliasML::fetchByAction( 'eznode', $parentNodeID ); 03429 if ( count( $parentElements ) > 0 && $parentElements[0]->attribute( 'text' ) != '' ) 03430 { 03431 // Pick one of the parents and get the ID 03432 $parentElementID = (int)$parentElements[0]->attribute( 'id' ); 03433 return eZURLAliasML::findUniqueText( $parentElementID, $element, $action ); 03434 } 03435 } 03436 return eZURLAliasML::findUniqueText( 0, $element, $action ); 03437 } 03438 03439 /*! 03440 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03441 the calls within a db transaction; thus within db->begin and db->commit. 03442 \deprecated Use updateSubTreePath() instead. 03443 */ 03444 function updateURLAlias() 03445 { 03446 eZDebug::writeWarning( __METHOD__ . " is deprecated, use updateSubTreePath() instead" ); 03447 return $this->updateSubTreePath(); 03448 } 03449 03450 /*! 03451 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03452 the calls within a db transaction; thus within db->begin and db->commit. 03453 */ 03454 function updateSubTreePath( $updateParent = true, $nodeMove = false ) 03455 { 03456 $changeCount = 0; 03457 03458 $nodeID = $this->attribute( 'node_id' ); 03459 $parentNodeID = $this->attribute( 'parent_node_id' ); 03460 03461 // Avoid recursion due to database inconsistencies 03462 if ( $nodeID === $parentNodeID ) 03463 { 03464 eZDebug::writeError( "Parent node ID equals node ID for node: $nodeID. The node cannot be a parent of itself!", __METHOD__ ); 03465 return false; 03466 } 03467 03468 // Only set name if current node is not the content root 03469 $ini = eZINI::instance( 'content.ini' ); 03470 $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' ); 03471 $obj = $this->object(); 03472 $alwaysMask = ( $obj->attribute( 'language_mask' ) & 1 ); 03473 $languages = $obj->allLanguages(); 03474 $nameList = array(); 03475 03476 $initialLanguageID = $obj->attribute( 'initial_language_id' ); 03477 $pathIdentificationName = false; 03478 foreach ( $languages as $language ) 03479 { 03480 $nodeName = ''; 03481 if ( $nodeID != $contentRootID ) 03482 { 03483 $objClass = $obj->attribute( 'content_class' ); 03484 $nodeName = $objClass->urlAliasName( $obj, false, $language->attribute( 'locale' ) ); 03485 $nodeName = eZURLAliasFilter::processFilters( $nodeName, $language, $this ); 03486 $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $nodeID ); 03487 $nodeName = $this->adjustPathElement( $nodeName, $nodeMove ); 03488 03489 // Compatibility mode: 03490 // Store name for the 'path_identification_string' column. 03491 if ( $initialLanguageID == $language->attribute( 'id' ) ) 03492 { 03493 $pathIdentificationName = eZURLAliasML::convertToAliasCompat( $nodeName, 'node_' . $nodeID ); 03494 } 03495 } 03496 $nameList[] = array( 'text' => $nodeName, 03497 'language' => $language ); 03498 } 03499 03500 $parentActionName = "eznode"; 03501 $parentActionValue = $parentNodeID; 03502 03503 $parentElementID = false; 03504 $existingElements = eZURLAliasML::fetchByAction( "eznode", $nodeID ); 03505 $existingElementID = null; 03506 if ( count( $existingElements ) > 0 ) 03507 { 03508 $existingElementID = $existingElements[0]->attribute( 'id' ); 03509 $parentElementID = $existingElements[0]->attribute( 'parent' ); 03510 } 03511 03512 // If we have parent element it means the node is already published 03513 // and we have to see if it has been moved 03514 if ( $parentNodeID != 1 and $updateParent ) 03515 { 03516 $parents = eZURLAliasML::fetchByAction( "eznode", $parentNodeID ); 03517 if ( count( $parents ) == 0 ) 03518 { 03519 $parentNode = $this->fetchParent(); 03520 03521 if ( !$parentNode ) 03522 { 03523 return false; 03524 } 03525 03526 $result = $parentNode->updateSubTreePath(); 03527 if ( !$result ) 03528 { 03529 return false; 03530 } 03531 $parents = eZURLAliasML::fetchByAction( $parentActionName, $parentActionValue ); 03532 if ( count( $parents ) == 0 ) 03533 { 03534 return false; 03535 } 03536 $oldParentElementID = $parentElementID; 03537 foreach ( $parents as $paren ) 03538 { 03539 $parentElementID = 0; 03540 if ( $paren->attribute( 'text' ) != '' ) 03541 { 03542 $parentElementID = (int)$paren->attribute( 'link' ); 03543 break; 03544 } 03545 } 03546 } 03547 else 03548 { 03549 $oldParentElementID = $parentElementID; 03550 $parentElementID = 0; 03551 foreach ( $parents as $paren ) 03552 { 03553 if ( $paren->attribute( 'text' ) != '' ) 03554 { 03555 $parentElementID = (int)$paren->attribute( 'link' ); 03556 break; 03557 } 03558 } 03559 } 03560 } 03561 else // Parent is ID 1, ie. this node is top-level 03562 { 03563 } 03564 03565 $this->updatePathIdentificationString( $pathIdentificationName ); 03566 03567 $languageID = $obj->attribute( 'initial_language_id' ); 03568 $cleanup = false; 03569 foreach ( $nameList as $nameEntry ) 03570 { 03571 $text = $nameEntry['text']; 03572 $language = $nameEntry['language']; 03573 $result = eZURLAliasML::storePath( $text, 'eznode:' . $nodeID, $language, false, $alwaysMask, $parentElementID, $cleanup ); 03574 if ( $result['status'] === true ) 03575 $changeCount++; 03576 } 03577 return $changeCount; 03578 } 03579 03580 /*! 03581 \private 03582 Updates the path_identification_string field in ezcontentobject_tree by 03583 fetching the value from the parent and appending $pathIdentificationName. 03584 03585 \note This stores the current object to the database 03586 */ 03587 function updatePathIdentificationString( $pathIdentificationName ) 03588 { 03589 // Update the path_identification_string column for the node 03590 $pathIdentificationString = ''; 03591 if ( $this->attribute( 'parent_node_id' ) != 1 ) 03592 { 03593 if ( !isset( $parentNode ) ) 03594 $parentNode = $this->fetchParent(); 03595 // Avoid crashes due to database inconsistencies 03596 if ( $parentNode instanceof eZContentObjectTreeNode ) 03597 { 03598 $pathIdentificationString = $parentNode->attribute( 'path_identification_string' ); 03599 } 03600 else 03601 { 03602 eZDebug::writeError( 'Failed to fetch parent node for pathIdentificationName "' . $pathIdentificationName . 03603 '" and parent_node_id ' . $this->attribute( 'parent_node_id' ), __METHOD__ ); 03604 } 03605 } 03606 if ( strlen( $pathIdentificationString ) > 0 ) 03607 $pathIdentificationString .= '/' . $pathIdentificationName; 03608 else 03609 $pathIdentificationString = $pathIdentificationName; 03610 if ( $this->attribute( 'path_identification_string' ) != $pathIdentificationString ) 03611 { 03612 $this->setAttribute( 'path_identification_string', $pathIdentificationString ); 03613 $this->sync(); 03614 } 03615 } 03616 03617 /*! 03618 \static 03619 \sa removeThis 03620 */ 03621 static function removeNode( $nodeID = 0 ) 03622 { 03623 $node = eZContentObjectTreeNode::fetch( $nodeID ); 03624 if ( !is_object( $node ) ) 03625 { 03626 return; 03627 } 03628 03629 return $node->removeThis(); 03630 } 03631 03632 /*! 03633 Removes the current node. 03634 Use ->removeNodeFromTree() if you need to handle main node change + remove object if needed 03635 03636 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03637 the calls within a db transaction; thus within db->begin and db->commit. 03638 */ 03639 function removeThis() 03640 { 03641 $ini = eZINI::instance(); 03642 03643 $object = $this->object(); 03644 $nodeID = $this->attribute( 'node_id' ); 03645 if ( eZAudit::isAuditEnabled() ) 03646 { 03647 // Set audit params. 03648 $objectID = $object->attribute( 'id' ); 03649 $objectName = $object->attribute( 'name' ); 03650 03651 eZAudit::writeAudit( 'content-delete', array( 'Node ID' => $nodeID, 'Object ID' => $objectID, 'Content Name' => $objectName, 03652 'Comment' => 'Removed the current node: eZContentObjectTreeNode::removeNode()' ) ); 03653 } 03654 03655 $db = eZDB::instance(); 03656 $db->begin(); 03657 03658 $nodePath = $this->attribute( 'path_string' ); 03659 $childrensPath = $nodePath ; 03660 03661 $pathString = " path_string like '$childrensPath%' "; 03662 03663 $urlAlias = $this->attribute( 'url_alias' ); 03664 03665 // Remove static cache 03666 if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' ) 03667 { 03668 $optionArray = array( 'iniFile' => 'site.ini', 03669 'iniSection' => 'ContentSettings', 03670 'iniVariable' => 'StaticCacheHandler' ); 03671 03672 $options = new ezpExtensionOptions( $optionArray ); 03673 03674 $staticCacheHandler = eZExtension::getHandlerClass( $options ); 03675 03676 $staticCacheHandler->removeURL( "/" . $urlAlias ); 03677 $staticCacheHandler->generateAlwaysUpdatedCache(); 03678 03679 $parent = $this->fetchParent(); 03680 } 03681 03682 $db->query( "DELETE FROM ezcontentobject_tree 03683 WHERE $pathString OR 03684 path_string = '$nodePath'" ); 03685 03686 // Re-cache parent node 03687 if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' ) 03688 { 03689 if ( $parent ) 03690 { 03691 $staticCacheHandler->cacheURL( "/" . $parent->urlAlias() ); 03692 } 03693 } 03694 03695 // Clean up URL alias entries 03696 eZURLAliasML::removeByAction( 'eznode', $nodeID ); 03697 03698 // Clean up content cache 03699 eZContentCacheManager::clearContentCacheIfNeeded( $this->attribute( 'contentobject_id' ) ); 03700 03701 // clean up user cache 03702 if ( in_array( $object->attribute( 'contentclass_id' ), eZUser::contentClassIDs() ) ) 03703 { 03704 eZUser::removeSessionData( $object->attribute( 'id' ) ); 03705 eZUser::purgeUserCacheByUserId( $object->attribute( 'id' ) ); 03706 } 03707 03708 $parentNode = $this->attribute( 'parent' ); 03709 if ( is_object( $parentNode ) ) 03710 { 03711 eZContentCacheManager::clearContentCacheIfNeeded( $parentNode->attribute( 'contentobject_id' ) ); 03712 $parentNode->updateAndStoreModified(); 03713 } 03714 03715 // Clean up policies and limitations 03716 eZRole::cleanupByNode( $this ); 03717 03718 // Clean up recent items 03719 eZContentBrowseRecent::removeRecentByNodeID( $nodeID ); 03720 03721 // Clean up bookmarks 03722 eZContentBrowseBookmark::removeByNodeID( $nodeID ); 03723 03724 // Clean up tip-a-friend counter 03725 eZTipafriendCounter::removeForNode( $nodeID ); 03726 03727 // Clean up view counter 03728 eZViewCounter::removeCounter( $nodeID ); 03729 03730 $db->commit(); 03731 } 03732 03733 /*! 03734 \static 03735 Returns information on what will happen if all subtrees in \a $deleteIDArray 03736 is removed. The returned structure is: 03737 - move_to_trash - \c true if removed objects can be moved to trash, 03738 some objects are not allowed to be in trash (e.g user). 03739 - total_child_count - The total number of children for all delete items 03740 - can_remove_all - Will be set to \c true if all selected items can be removed, \c false otherwise 03741 - delete_list - A list of all subtrees that should be removed, structure: 03742 -- node - The content node 03743 -- object - The content object 03744 -- class - The content class 03745 -- node_name - The name of the node 03746 -- child_count - Total number of child items below the node 03747 -- can_remove - Boolean which tells if the user has permission to remove the node 03748 -- can_remove_subtree - Boolean which tells if the user has permission to remove items in the subtree 03749 -- new_main_node_id - The new main node ID for the node if it needs to be moved, or \c false if not 03750 -- object_node_count - The number of nodes the object has (before removal) 03751 -- sole_node_count - The number of nodes in the subtree (excluding current) that does 03752 not have multiple locations. 03753 03754 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03755 the calls within a db transaction; thus within db->begin and db->commit. 03756 */ 03757 static function subtreeRemovalInformation( $deleteIDArray ) 03758 { 03759 return eZContentObjectTreeNode::removeSubtrees( $deleteIDArray, true, true ); 03760 } 03761 03762 /*! 03763 \static 03764 Will remove the nodes in the subtrees defined in \a $deleteIDArray, 03765 it will only remove the nodes unless there are no more nodes for 03766 an object in which case the object is removed too. 03767 03768 \param $moveToTrash If \c true it will move the object to trash, if \c false 03769 the object will be purged from the system. 03770 \param $infoOnly If set to \c true then it will not remove the subtree 03771 but instead return information on what will happen 03772 if it is removed. See subtreeRemovalInformation() for the 03773 returned structure. 03774 03775 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03776 the calls within a db transaction; thus within db->begin and db->commit. 03777 */ 03778 static function removeSubtrees( $deleteIDArray, $moveToTrash = true, $infoOnly = false ) 03779 { 03780 $moveToTrashAllowed = true; 03781 $deleteResult = array(); 03782 $totalChildCount = 0; 03783 $totalLoneNodeCount = 0; 03784 $canRemoveAll = true; 03785 $hasPendingObject = false; 03786 03787 $db = eZDB::instance(); 03788 $db->begin(); 03789 03790 $userClassIDArray = eZUser::contentClassIDs(); 03791 03792 foreach ( $deleteIDArray as $deleteID ) 03793 { 03794 $node = eZContentObjectTreeNode::fetch( $deleteID ); 03795 if ( $node === null ) 03796 continue; 03797 03798 $object = $node->attribute( 'object' ); 03799 if ( $object === null ) 03800 continue; 03801 03802 $class = $object->attribute( 'content_class' ); 03803 $canRemove = $node->attribute( 'can_remove' ); 03804 $canRemoveSubtree = true; 03805 03806 $nodeID = $node->attribute( 'node_id' ); 03807 $nodeName = $object->attribute( 'name' ); 03808 03809 $childCount = 0; 03810 $newMainNodeID = false; 03811 $objectNodeCount = 0; 03812 $readableChildCount = 0; 03813 03814 if ( $canRemove ) 03815 { 03816 $isUserClass = in_array( $class->attribute( 'id' ), $userClassIDArray ); 03817 03818 if ( $moveToTrashAllowed and $isUserClass ) 03819 { 03820 $moveToTrashAllowed = false; 03821 } 03822 $readableChildCount = $node->subTreeCount( array( 'Limitation' => array() ) ); 03823 $childCount = $node->subTreeCount( array( 'IgnoreVisibility' => true ) ); 03824 $totalChildCount += $childCount; 03825 03826 $allAssignedNodes = $object->attribute( 'assigned_nodes' ); 03827 $objectNodeCount = count( $allAssignedNodes ); 03828 // We need to find a new main node ID if we are trying 03829 // to remove the current main node. 03830 if ( $node->attribute( 'main_node_id' ) == $nodeID ) 03831 { 03832 if ( count( $allAssignedNodes ) > 1 ) 03833 { 03834 foreach( $allAssignedNodes as $assignedNode ) 03835 { 03836 $assignedNodeID = $assignedNode->attribute( 'node_id' ); 03837 if ( $assignedNodeID == $nodeID ) 03838 continue; 03839 $newMainNodeID = $assignedNodeID; 03840 break; 03841 } 03842 } 03843 } 03844 03845 if ( $infoOnly ) 03846 { 03847 // Find the number of items in the subtree we are allowed to remove 03848 // if this differs from the total count it means we have items we cannot remove 03849 // We do this by fetching the limitation list for content/remove 03850 // and passing it to the subtree count function. 03851 $currentUser = eZUser::currentUser(); 03852 $accessResult = $currentUser->hasAccessTo( 'content', 'remove' ); 03853 if ( $accessResult['accessWord'] == 'limited' ) 03854 { 03855 $limitationList = $accessResult['policies']; 03856 $removeableChildCount = $node->subTreeCount( array( 'Limitation' => $limitationList, 'IgnoreVisibility' => true ) ); 03857 $canRemoveSubtree = ( $removeableChildCount == $childCount ); 03858 $canRemove = $canRemoveSubtree; 03859 } 03860 //check if there is sub object in pending status 03861 $limitCount = 100; 03862 $offset = 0; 03863 while( 1 ) 03864 { 03865 $children = $node->subTree( array( 'Limitation' => array(), 03866 'SortBy' => array( 'path' , false ), 03867 'Offset' => $offset, 03868 'Limit' => $limitCount, 03869 'IgnoreVisibility' => true, 03870 'AsObject' => false ) ); 03871 // fetch pending node assignment(pending object) 03872 $idList = array(); 03873 //add node itself into idList 03874 if( $offset === 0 ) 03875 { 03876 $idList[] = $nodeID; 03877 } 03878 foreach( $children as $child ) 03879 { 03880 $idList[] = $child['node_id']; 03881 } 03882 03883 if( count( $idList ) === 0 ) 03884 { 03885 break; 03886 } 03887 $pendingChildCount = eZNodeAssignment::fetchChildCountByVersionStatus( $idList, 03888 eZContentObjectVersion::STATUS_PENDING ); 03889 if( $pendingChildCount !== 0 ) 03890 { 03891 // there is pending object 03892 $hasPendingObject = true; 03893 break; 03894 } 03895 $offset += $limitCount; 03896 } 03897 } 03898 03899 // We will only remove the subtree if are allowed 03900 // and are told to do so. 03901 if ( $canRemove and !$infoOnly ) 03902 { 03903 $moveToTrashTemp = $moveToTrash; 03904 if ( !$moveToTrashAllowed ) 03905 $moveToTrashTemp = false; 03906 03907 // Remove children, fetching them by 100 to avoid memory overflow. 03908 // removeNodeFromTree -> removeThis handles cache clearing 03909 while ( 1 ) 03910 { 03911 // We should remove the latest subitems first, 03912 // so we should fetch subitems sorted by 'path_string' DESC 03913 $children = $node->subTree( array( 'Limitation' => array(), 03914 'SortBy' => array( 'path' , false ), 03915 'Limit' => 100, 03916 'IgnoreVisibility' => true ) ); 03917 if ( !$children ) 03918 break; 03919 03920 foreach ( $children as $child ) 03921 { 03922 $childObject = $child->attribute( 'object' ); 03923 $child->removeNodeFromTree( $moveToTrashTemp ); 03924 eZContentObject::clearCache(); 03925 } 03926 } 03927 03928 $node->removeNodeFromTree( $moveToTrashTemp ); 03929 } 03930 } 03931 if ( !$canRemove ) 03932 $canRemoveAll = false; 03933 03934 // Do not create info list if we are removing subtrees 03935 if ( !$infoOnly ) 03936 continue; 03937 03938 $soleNodeCount = $node->subtreeSoleNodeCount(); 03939 $totalLoneNodeCount += $soleNodeCount; 03940 if ( $objectNodeCount <= 1 ) 03941 ++$totalLoneNodeCount; 03942 03943 $item = array( "nodeName" => $nodeName, // Backwards compatibility 03944 "childCount" => $childCount, // Backwards compatibility 03945 "additionalWarning" => '', // Backwards compatibility, this will always be empty 03946 'node' => $node, 03947 'object' => $object, 03948 'class' => $class, 03949 'node_name' => $nodeName, 03950 'child_count' => $childCount, 03951 'object_node_count' => $objectNodeCount, 03952 'sole_node_count' => $soleNodeCount, 03953 'can_remove' => $canRemove, 03954 'can_remove_subtree' => $canRemoveSubtree, 03955 'real_child_count' => $readableChildCount, 03956 'new_main_node_id' => $newMainNodeID ); 03957 $deleteResult[] = $item; 03958 } 03959 03960 $db->commit(); 03961 03962 if ( !$infoOnly ) 03963 return true; 03964 03965 if ( $moveToTrashAllowed and $totalLoneNodeCount == 0 ) 03966 $moveToTrashAllowed = false; 03967 03968 return array( 'move_to_trash' => $moveToTrashAllowed, 03969 'total_child_count' => $totalChildCount, 03970 'can_remove_all' => $canRemoveAll, 03971 'delete_list' => $deleteResult, 03972 'has_pending_object' => $hasPendingObject, 03973 'reverse_related_count' => eZContentObjectTreeNode::reverseRelatedCount( $deleteIDArray ) ); 03974 } 03975 03976 /*! 03977 \private 03978 \static 03979 Return reverse related count for specified node 03980 03981 \param $nodeIDArray, array of node id's 03982 03983 \return reverse related count. 03984 */ 03985 static function reverseRelatedCount( $nodeIDArray ) 03986 { 03987 // Select count of all elements having reverse relations. And ignore those items that don't relate to objects other than being removed. 03988 if ( $nodeIDArray === array() ) 03989 { 03990 return 0; 03991 } 03992 03993 foreach( $nodeIDArray as $nodeID ) 03994 { 03995 $contentObjectTreeNode = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 03996 $tempPathString = $contentObjectTreeNode['path_string']; 03997 03998 // Create WHERE section 03999 $pathStringArray[] = "tree.path_string like '$tempPathString%'"; 04000 $path2StringArray[] = "tree2.path_string like '$tempPathString%'"; 04001 } 04002 $path_strings = '( ' . implode( ' OR ', $pathStringArray ) . ' ) '; 04003 $path_strings_where = '( ' . implode( ' OR ', $path2StringArray ) . ' ) '; 04004 04005 // Total count of sub items 04006 $db = eZDB::instance(); 04007 $countOfItems = $db->arrayQuery( "SELECT COUNT( DISTINCT( tree.node_id ) ) as count 04008 FROM ezcontentobject_tree tree, ezcontentobject obj, 04009 ezcontentobject_link link LEFT JOIN ezcontentobject_tree tree2 04010 ON link.from_contentobject_id = tree2.contentobject_id 04011 WHERE $path_strings 04012 and link.to_contentobject_id = tree.contentobject_id 04013 and obj.id = link.from_contentobject_id 04014 and obj.current_version = link.from_contentobject_version 04015 and not $path_strings_where" ); 04016 04017 if ( $countOfItems ) 04018 { 04019 return $countOfItems[0]['count']; 04020 } 04021 } 04022 04023 /*! 04024 Will check if you are removing the main node in which case it relocates 04025 the main node before removing it. It will also remove the object if there 04026 no more node assignments for it. 04027 \param $moveToTrash If \c true it will move the object to trash, if \c false 04028 the object will be purged from the system. 04029 04030 \note This uses remove() to do the actual node removal but has some extra logic 04031 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 04032 the calls within a db transaction; thus within db->begin and db->commit. 04033 */ 04034 function removeNodeFromTree( $moveToTrash = true ) 04035 { 04036 $nodeID = $this->attribute( 'node_id' ); 04037 $object = $this->object(); 04038 $assignedNodes = $object->attribute( 'assigned_nodes' ); 04039 if ( $nodeID == $this->attribute( 'main_node_id' ) ) 04040 { 04041 if ( count( $assignedNodes ) > 1 ) 04042 { 04043 $newMainNode = false; 04044 foreach ( $assignedNodes as $assignedNode ) 04045 { 04046 $assignedNodeID = $assignedNode->attribute( 'node_id' ); 04047 if ( $assignedNodeID == $nodeID ) 04048 continue; 04049 $newMainNode = $assignedNode; 04050 break; 04051 } 04052 04053 // We need to change the main node ID before we remove the current node 04054 $db = eZDB::instance(); 04055 $db->begin(); 04056 eZContentObjectTreeNode::updateMainNodeID( $newMainNode->attribute( 'node_id' ), 04057 $object->attribute( 'id' ), 04058 $object->attribute( 'current_version' ), 04059 $newMainNode->attribute( 'parent_node_id' ) ); 04060 $this->removeThis(); 04061 eZSearch::addObject( $object ); 04062 $db->commit(); 04063 } 04064 else 04065 { 04066 // This is the last assignment so we remove the object too 04067 $db = eZDB::instance(); 04068 $db->begin(); 04069 $this->removeThis(); 04070 04071 if ( $moveToTrash ) 04072 { 04073 // saving information about this node in ..trash_node table 04074 $trashNode = eZContentObjectTrashNode::createFromNode( $this ); 04075 $db = eZDB::instance(); 04076 $db->begin(); 04077 $trashNode->storeToTrash(); 04078 $db->commit(); 04079 $object->removeThis(); 04080 } 04081 else 04082 { 04083 $object->purge(); 04084 } 04085 $db->commit(); 04086 } 04087 } 04088 else 04089 { 04090 $this->removeThis(); 04091 if ( count( $assignedNodes ) > 1 ) 04092 { 04093 eZSearch::addObject( $object ); 04094 } 04095 } 04096 } 04097 04098 /*! 04099 \return The number of nodes in the current subtree that have no other placements. 04100 */ 04101 function subtreeSoleNodeCount( $params = array() ) 04102 { 04103 $nodeID = $this->attribute( 'node_id' ); 04104 $node = $this; 04105 04106 $depth = false; 04107 if ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) 04108 { 04109 $depth = $params['Depth']; 04110 04111 } 04112 04113 $fromNode = $nodeID; 04114 04115 $nodePath = null; 04116 $nodeDepth = 0; 04117 if ( count( $node ) != 0 ) 04118 { 04119 $nodePath = $node->attribute( 'path_string' ); 04120 $nodeDepth = $node->attribute( 'depth' ); 04121 } 04122 04123 $childPath = $nodePath; 04124 04125 $db = eZDB::instance(); 04126 $pathString = " ezcot.path_string like '$childPath%' and "; 04127 04128 $notEqParentString = "ezcot.node_id != $fromNode"; 04129 $depthCond = ''; 04130 if ( $depth ) 04131 { 04132 04133 $nodeDepth += $depth; 04134 if ( isset( $params[ 'DepthOperator' ] ) && $params[ 'DepthOperator' ] == 'eq' ) 04135 { 04136 $depthCond = ' ezcot.depth = ' . $nodeDepth . ''; 04137 $notEqParentString = ''; 04138 } 04139 else 04140 $depthCond = ' ezcot.depth <= ' . $nodeDepth . ' and '; 04141 } 04142 04143 $tmpTableName = $db->generateUniqueTempTableName( 'eznode_count_%' ); 04144 $db->createTempTable( "CREATE TEMPORARY TABLE $tmpTableName ( count int )" ); 04145 $query = "INSERT INTO $tmpTableName 04146 SELECT 04147 count( ezcot.main_node_id ) AS count 04148 FROM 04149 ezcontentobject_tree ezcot, 04150 ezcontentobject_tree ezcot_all 04151 WHERE 04152 $pathString 04153 $depthCond 04154 $notEqParentString 04155 and ezcot.contentobject_id = ezcot_all.contentobject_id 04156 GROUP BY ezcot_all.main_node_id 04157 HAVING count( ezcot.main_node_id ) <= 1"; 04158 04159 $db->query( $query, eZDBInterface::SERVER_SLAVE ); 04160 $query = "SELECT count( * ) AS count 04161 FROM $tmpTableName"; 04162 04163 $rows = $db->arrayQuery( $query, array(), eZDBInterface::SERVER_SLAVE ); 04164 $db->dropTempTable( "DROP TABLE $tmpTableName" ); 04165 return $rows[0]['count']; 04166 } 04167 04168 /*! 04169 Moves the node to the given node. 04170 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 04171 the calls within a db transaction; thus within db->begin and db->commit. 04172 */ 04173 function move( $newParentNodeID, $nodeID = 0 ) 04174 { 04175 if ( $nodeID == 0 ) 04176 { 04177 $node = $this; 04178 $nodeID = $node->attribute( 'node_id' ); 04179 } 04180 else 04181 { 04182 $node = eZContentObjectTreeNode::fetch( $nodeID ); 04183 } 04184 04185 $oldPath = $node->attribute( 'path_string' ); 04186 $oldParentNodeID = $node->attribute( 'parent_node_id' ); 04187 $newParentNodeID =(int) $newParentNodeID; 04188 if ( $oldParentNodeID != $newParentNodeID ) 04189 { 04190 $node->updateAndStoreModified(); 04191 // Who moves which content should be logged. 04192 $object = $node->object(); 04193 eZAudit::writeAudit( 'content-move', array( 'Node ID' => $node->attribute( 'node_id' ), 04194 'Old parent node ID' => $oldParentNodeID, 'New parent node ID' => $newParentNodeID, 04195 'Object ID' => $object->attribute( 'id' ), 'Content Name' => $object->attribute( 'name' ), 04196 'Comment' => 'Moved the node to the given node: eZContentObjectTreeNode::move()' ) ); 04197 04198 $newParentNode = eZContentObjectTreeNode::fetch( $newParentNodeID ); 04199 $newParentPath = $newParentNode->attribute( 'path_string' ); 04200 $newParentDepth = $newParentNode->attribute( 'depth' ); 04201 $newPath = $newParentPath . $nodeID; 04202 $oldDepth = $node->attribute( 'depth' ); 04203 04204 $oldPathLength = strlen( $oldPath ); 04205 $moveQuery = "UPDATE 04206 ezcontentobject_tree 04207 SET 04208 parent_node_id = $newParentNodeID 04209 WHERE 04210 node_id = $nodeID"; 04211 $db = eZDB::instance(); 04212 $subStringString = $db->subString( 'path_string', $oldPathLength ); 04213 $newPathString = $db->concatString( array( "'$newPath'", $subStringString ) ); 04214 $moveQuery1 = "UPDATE 04215 ezcontentobject_tree 04216 SET 04217 path_string = $newPathString, 04218 depth = depth + $newParentDepth - $oldDepth + 1 04219 WHERE 04220 path_string LIKE '$oldPath%'"; 04221 $db->begin(); 04222 $db->query( $moveQuery ); 04223 $db->query( $moveQuery1 ); 04224 04225 /// role system clean up 04226 // Clean up policies and limitations 04227 04228 $expireRoleCache = false; 04229 04230 $limitationsToFix = eZPolicyLimitation::findByType( 'SubTree', $node->attribute( 'path_string' ), false ); 04231 if ( count( $limitationsToFix ) > 0 ) 04232 { 04233 $limitationIDString = $db->generateSQLINStatement( $limitationsToFix, 'limitation_id' ); 04234 $subStringString = $db->subString( 'value', $oldPathLength ); 04235 $newValue = $db->concatString( array( "'$newPath'", $subStringString ) ); 04236 04237 $query = "UPDATE 04238 ezpolicy_limitation_value 04239 SET 04240 value = $newValue 04241 WHERE 04242 value LIKE '$oldPath%' AND $limitationIDString"; 04243 04244 $db->query( $query ); 04245 04246 $expireRoleCache = true; 04247 } 04248 04249 // clean up limitations on role assignment level 04250 $countRows = $db->arrayQuery( "SELECT COUNT(*) AS row_count FROM ezuser_role WHERE limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'" ); 04251 $assignmentsToFixCount = $countRows[0]['row_count']; 04252 04253 if ( $assignmentsToFixCount > 0 ) 04254 { 04255 $subStringString = $db->subString( 'limit_value', $oldPathLength ); 04256 $newValue = $db->concatString( array( "'$newPath'", $subStringString ) ); 04257 04258 $db->query( "UPDATE 04259 ezuser_role 04260 SET 04261 limit_value = $newValue 04262 WHERE 04263 limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'" 04264 ); 04265 04266 $expireRoleCache = true; 04267 } 04268 04269 if ( $expireRoleCache ) 04270 { 04271 eZRole::expireCache(); 04272 } 04273 04274 // Update "is_invisible" node attribute. 04275 $newNode = eZContentObjectTreeNode::fetch( $nodeID ); 04276 eZContentObjectTreeNode::updateNodeVisibility( $newNode, $newParentNode ); 04277 $db->commit(); 04278 } 04279 } 04280 04281 function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false ) 04282 { 04283 $classID = $originalClassID; 04284 $user = eZUser::currentUser(); 04285 $userID = $user->attribute( 'contentobject_id' ); 04286 04287 // Fetch the ID of the language if we get a string with a language code 04288 // e.g. 'eng-GB' 04289 $originalLanguage = $language; 04290 if ( is_string( $language ) && strlen( $language ) > 0 ) 04291 { 04292 $language = eZContentLanguage::idByLocale( $language ); 04293 } 04294 else 04295 { 04296 $language = false; 04297 } 04298 04299 // This will be filled in with the available languages of the object 04300 // if a Language check is performed. 04301 $languageList = false; 04302 04303 // This will be filled if parent object is needed. 04304 $parentObject = false; 04305 04306 $origFunctionName = $functionName; 04307 // The 'move' function simply reuses 'edit' for generic access 04308 // but adds another top-level check below 04309 // The original function is still available in $origFunctionName 04310 if ( $functionName == 'move' ) 04311 $functionName = 'edit'; 04312 04313 // Manage locations depends if it's removal or not. 04314 if ( $functionName == 'can_add_location' || 04315 $functionName == 'can_remove_location' ) 04316 { 04317 $functionName = 'manage_locations'; 04318 } 04319 04320 $accessResult = $user->hasAccessTo( 'content' , $functionName ); 04321 $accessWord = $accessResult['accessWord']; 04322 if ( $origFunctionName == 'can_remove_location' ) 04323 { 04324 if ( $this->ParentNodeID <= 1 ) 04325 { 04326 return 0; 04327 } 04328 $currentNode = eZContentObjectTreeNode::fetch( $this->ParentNodeID ); 04329 if ( !$currentNode instanceof eZContentObjectTreeNode ) 04330 { 04331 return 0; 04332 } 04333 $contentObject = $currentNode->attribute( 'object' ); 04334 } 04335 else 04336 { 04337 $currentNode = $this; 04338 $contentObject = $this->attribute( 'object' ); 04339 } 04340 04341 /* 04342 // Uncomment this part if 'create' permissions should become implied 'edit'. 04343 // Merges in 'create' policies with 'edit' 04344 if ( $functionName == 'edit' && 04345 !in_array( $accessWord, array( 'yes', 'no' ) ) ) 04346 { 04347 // Add in create policies. 04348 $accessExtraResult = $user->hasAccessTo( 'content', 'create' ); 04349 if ( $accessExtraResult['accessWord'] != 'no' ) 04350 { 04351 $accessWord = $accessExtraResult['accessWord']; 04352 if ( isset( $accessExtraResult['policies'] ) ) 04353 { 04354 $accessResult['policies'] = array_merge( $accessResult['policies'], 04355 $accessExtraResult['policies'] ); 04356 } 04357 if ( isset( $accessExtraResult['accessList'] ) ) 04358 { 04359 $accessResult['accessList'] = array_merge( $accessResult['accessList'], 04360 $accessExtraResult['accessList'] ); 04361 } 04362 } 04363 } 04364 */ 04365 04366 if ( $origFunctionName == 'remove' or 04367 $origFunctionName == 'move' or 04368 $origFunctionName == 'can_remove_location' ) 04369 { 04370 // We do not allow these actions on top-level nodes 04371 // - remove 04372 // - move 04373 if ( $this->ParentNodeID <= 1 ) 04374 { 04375 return 0; 04376 } 04377 } 04378 04379 if ( $classID === false ) 04380 { 04381 $classID = $contentObject->attribute( 'contentclass_id' ); 04382 } 04383 if ( $accessWord == 'yes' ) 04384 { 04385 return 1; 04386 } 04387 else if ( $accessWord == 'no' ) 04388 { 04389 if ( $functionName == 'edit' ) 04390 { 04391 // Check if we have 'create' access under the main parent 04392 $object = $currentNode->object(); 04393 if ( $object && $object->attribute( 'current_version' ) == 1 && !$object->attribute( 'status' ) ) 04394 { 04395 $mainNode = eZNodeAssignment::fetchForObject( $object->attribute( 'id' ), $object->attribute( 'current_version' ) ); 04396 $parentObj = $mainNode[0]->attribute( 'parent_contentobject' ); 04397 $result = $parentObj->checkAccess( 'create', $object->attribute( 'contentclass_id' ), 04398 $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage ); 04399 return $result; 04400 } 04401 else 04402 { 04403 return 0; 04404 } 04405 } 04406 04407 return 0; 04408 } 04409 else 04410 { 04411 $policies = $accessResult['policies']; 04412 $access = 'denied'; 04413 04414 foreach ( $policies as $pkey => $limitationArray ) 04415 { 04416 if ( $access == 'allowed' ) 04417 { 04418 break; 04419 } 04420 04421 $limitationList = array(); 04422 if ( isset( $limitationArray['Subtree' ] ) ) 04423 { 04424 $checkedSubtree = false; 04425 } 04426 else 04427 { 04428 $checkedSubtree = true; 04429 $accessSubtree = false; 04430 } 04431 if ( isset( $limitationArray['Node'] ) ) 04432 { 04433 $checkedNode = false; 04434 } 04435 else 04436 { 04437 $checkedNode = true; 04438 $accessNode = false; 04439 } 04440 foreach ( $limitationArray as $key => $valueList ) 04441 { 04442 $access = 'denied'; 04443 switch( $key ) 04444 { 04445 case 'Class': 04446 { 04447 if ( $functionName == 'create' and 04448 !$originalClassID ) 04449 { 04450 $access = 'allowed'; 04451 } 04452 else if ( $functionName == 'create' and 04453 in_array( $classID, $valueList ) ) 04454 { 04455 $access = 'allowed'; 04456 } 04457 else if ( $functionName != 'create' and 04458 in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) ) 04459 { 04460 $access = 'allowed'; 04461 } 04462 else 04463 { 04464 $access = 'denied'; 04465 $limitationList = array( 'Limitation' => $key, 04466 'Required' => $valueList ); 04467 } 04468 } break; 04469 04470 case 'ParentClass': 04471 { 04472 if ( in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) ) 04473 { 04474 $access = 'allowed'; 04475 } 04476 else 04477 { 04478 $access = 'denied'; 04479 $limitationList = array( 'Limitation' => $key, 04480 'Required' => $valueList ); 04481 } 04482 } break; 04483 04484 case 'Section': 04485 case 'User_Section': 04486 { 04487 if ( in_array( $contentObject->attribute( 'section_id' ), $valueList ) ) 04488 { 04489 $access = 'allowed'; 04490 } 04491 else 04492 { 04493 $access = 'denied'; 04494 $limitationList = array( 'Limitation' => $key, 04495 'Required' => $valueList ); 04496 } 04497 } break; 04498 04499 case 'Language': 04500 { 04501 $languageMask = 0; 04502 // If we don't have a language list yet we need to fetch it 04503 // and optionally filter out based on $language. 04504 if ( $functionName == 'create' ) 04505 { 04506 // If the function is 'create' we do not use the language_mask for matching. 04507 if ( $language !== false ) 04508 { 04509 $languageMask = $language; 04510 } 04511 else 04512 { 04513 // If the create is used and no language specified then 04514 // we need to match against all possible languages (which 04515 // is all bits set, ie. -1). 04516 $languageMask = -1; 04517 } 04518 } 04519 else 04520 { 04521 if ( $language !== false ) 04522 { 04523 if ( $languageList === false ) 04524 { 04525 $languageMask = $contentObject->attribute( 'language_mask' ); 04526 // We are restricting language check to just one language 04527 $languageMask &= $language; 04528 // If the resulting mask is 0 it means that the user is trying to 04529 // edit a language which does not exist, ie. translating. 04530 // The mask will then become the language trying to edit. 04531 if ( $languageMask == 0 ) 04532 { 04533 $languageMask = $language; 04534 } 04535 } 04536 } 04537 else 04538 { 04539 $languageMask = -1; 04540 } 04541 } 04542 // Fetch limit mask for limitation list 04543 $limitMask = eZContentLanguage::maskByLocale( $valueList ); 04544 if ( ( $languageMask & $limitMask ) != 0 ) 04545 { 04546 $access = 'allowed'; 04547 } 04548 else 04549 { 04550 $access = 'denied'; 04551 $limitationList = array( 'Limitation' => $key, 04552 'Required' => $valueList ); 04553 } 04554 } break; 04555 04556 case 'Owner': 04557 case 'ParentOwner': 04558 { 04559 // if limitation value == 2, anonymous limited to current session. 04560 if ( in_array( 2, $valueList ) && 04561 $user->isAnonymous() ) 04562 { 04563 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' ); 04564 if ( $createdObjectIDList && 04565 in_array( $contentObject->attribute( 'id' ), unserialize( $createdObjectIDList ) ) ) 04566 { 04567 $access = 'allowed'; 04568 } 04569 } 04570 else if ( $contentObject->attribute( 'owner_id' ) == $userID || $contentObject->attribute( 'id' ) == $userID ) 04571 { 04572 $access = 'allowed'; 04573 } 04574 if ( $access != 'allowed' ) 04575 { 04576 $access = 'denied'; 04577 $limitationList = array ( 'Limitation' => $key ); 04578 } 04579 } break; 04580 04581 case 'Group': 04582 case 'ParentGroup': 04583 { 04584 $access = $contentObject->checkGroupLimitationAccess( $valueList, $userID ); 04585 04586 if ( $access != 'allowed' ) 04587 { 04588 $access = 'denied'; 04589 $limitationList = array ( 'Limitation' => $key, 04590 'Required' => $valueList ); 04591 } 04592 } break; 04593 04594 case 'State': 04595 { 04596 if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 ) 04597 { 04598 $access = 'denied'; 04599 $limitationList = array ( 'Limitation' => $key, 04600 'Required' => $valueList ); 04601 } 04602 else 04603 { 04604 $access = 'allowed'; 04605 } 04606 } break; 04607 04608 case 'ParentDepth': 04609 { 04610 if ( in_array( $currentNode->attribute( 'depth' ), $valueList ) ) 04611 { 04612 $access = 'allowed'; 04613 } 04614 else 04615 { 04616 $access = 'denied'; 04617 $limitationList = array( 'Limitation' => $key, 04618 'Required' => $valueList ); 04619 } 04620 } break; 04621 04622 case 'Node': 04623 { 04624 $accessNode = false; 04625 $mainNodeID = $currentNode->attribute( 'main_node_id' ); 04626 foreach ( $valueList as $nodeID ) 04627 { 04628 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 04629 $limitationNodeID = $node['main_node_id']; 04630 if ( $mainNodeID == $limitationNodeID ) 04631 { 04632 $access = 'allowed'; 04633 $accessNode = true; 04634 break; 04635 } 04636 } 04637 if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree ) 04638 { 04639 $access = 'denied'; 04640 // ??? TODO: if there is a limitation on Subtree, return two limitations? 04641 $limitationList = array( 'Limitation' => $key, 04642 'Required' => $valueList ); 04643 } 04644 else 04645 { 04646 $access = 'allowed'; 04647 } 04648 $checkedNode = true; 04649 } break; 04650 04651 case 'Subtree': 04652 { 04653 $accessSubtree = false; 04654 $path = $currentNode->attribute( 'path_string' ); 04655 $subtreeArray = $valueList; 04656 foreach ( $subtreeArray as $subtreeString ) 04657 { 04658 if ( strstr( $path, $subtreeString ) ) 04659 { 04660 $access = 'allowed'; 04661 $accessSubtree = true; 04662 break; 04663 } 04664 } 04665 if ( $access != 'allowed' && $checkedNode && !$accessNode ) 04666 { 04667 $access = 'denied'; 04668 // ??? TODO: if there is a limitation on Node, return two limitations? 04669 $limitationList = array( 'Limitation' => $key, 04670 'Required' => $valueList ); 04671 } 04672 else 04673 { 04674 $access = 'allowed'; 04675 } 04676 $checkedSubtree = true; 04677 } break; 04678 04679 case 'User_Subtree': 04680 { 04681 $path = $currentNode->attribute( 'path_string' ); 04682 $subtreeArray = $valueList; 04683 foreach ( $subtreeArray as $subtreeString ) 04684 { 04685 if ( strstr( $path, $subtreeString ) ) 04686 { 04687 $access = 'allowed'; 04688 } 04689 } 04690 if ( $access != 'allowed' ) 04691 { 04692 $access = 'denied'; 04693 $limitationList = array( 'Limitation' => $key, 04694 'Required' => $valueList ); 04695 } 04696 } break; 04697 04698 default: 04699 { 04700 if ( strncmp( $key, 'StateGroup_', 11 ) === 0 ) 04701 { 04702 if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 ) 04703 { 04704 $access = 'denied'; 04705 $limitationList = array ( 'Limitation' => $key, 04706 'Required' => $valueList ); 04707 } 04708 else 04709 { 04710 $access = 'allowed'; 04711 } 04712 } 04713 } 04714 } 04715 04716 if ( $access == 'denied' ) 04717 { 04718 break; 04719 } 04720 } 04721 04722 $policyList[] = array( 'PolicyID' => $pkey, 04723 'LimitationList' => $limitationList ); 04724 } 04725 if ( $access == 'denied' ) 04726 { 04727 $accessList = array( 'FunctionRequired' => array ( 'Module' => 'content', 04728 'Function' => $origFunctionName, 04729 'ClassID' => $classID, 04730 'MainNodeID' => $currentNode->attribute( 'main_node_id' ) ), 04731 'PolicyList' => $policyList ); 04732 return 0; 04733 } 04734 else 04735 { 04736 return 1; 04737 } 04738 } 04739 } 04740 04741 // code-template::create-block: class-list-from-policy, is-node 04742 // code-template::auto-generated:START class-list-from-policy 04743 // This code is automatically generated from templates/classlistfrompolicy.ctpl 04744 // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD 04745 04746 function classListFromPolicy( $policy, $allowedLanguageCodes = false ) 04747 { 04748 $canCreateClassIDListPart = array(); 04749 $hasClassIDLimitation = false; 04750 $user = eZUser::currentUser(); 04751 $userID = $user->attribute( 'contentobject_id' ); 04752 04753 $object = false; 04754 if ( isset( $policy['ParentOwner'] ) ) 04755 { 04756 if ( $object === false ) 04757 $object = $this->attribute( 'object' ); 04758 04759 // if limitation value == 2, anonymous limited to current session. 04760 if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() ) 04761 { 04762 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' ); 04763 if ( !$createdObjectIDList || 04764 !in_array( $object->ID, unserialize( $createdObjectIDList ) ) ) 04765 { 04766 return array(); 04767 } 04768 } 04769 else if ( $object->attribute( 'owner_id' ) != $userID && 04770 $object->ID != $userID ) 04771 { 04772 return array(); 04773 } 04774 } 04775 04776 if ( isset( $policy['ParentGroup'] ) ) 04777 { 04778 if ( $object === false ) 04779 $object = $this->attribute( 'object' ); 04780 04781 $access = $object->checkGroupLimitationAccess( $policy['ParentGroup'], $userID ); 04782 if ( $access !== 'allowed' ) 04783 { 04784 return array(); 04785 } 04786 } 04787 04788 if ( isset( $policy['Class'] ) ) 04789 { 04790 $canCreateClassIDListPart = $policy['Class']; 04791 $hasClassIDLimitation = true; 04792 } 04793 04794 if ( isset( $policy['User_Section'] ) ) 04795 { 04796 if ( $object === false ) 04797 $object = $this->attribute( 'object' ); 04798 04799 if ( !in_array( $object->attribute( 'section_id' ), $policy['User_Section'] ) ) 04800 { 04801 return array(); 04802 } 04803 } 04804 04805 if ( isset( $policy['User_Subtree'] ) ) 04806 { 04807 $allowed = false; 04808 if ( $object === false ) 04809 $object = $this->attribute( 'object' ); 04810 04811 $assignedNodes = $object->attribute( 'assigned_nodes' ); 04812 foreach ( $assignedNodes as $assignedNode ) 04813 { 04814 $path = $assignedNode->attribute( 'path_string' ); 04815 foreach ( $policy['User_Subtree'] as $subtreeString ) 04816 { 04817 if ( strstr( $path, $subtreeString ) ) 04818 { 04819 $allowed = true; 04820 break; 04821 } 04822 } 04823 } 04824 if( !$allowed ) 04825 { 04826 return array(); 04827 } 04828 } 04829 04830 if ( isset( $policy['Section'] ) ) 04831 { 04832 if ( $object === false ) 04833 $object = $this->attribute( 'object' ); 04834 04835 if ( !in_array( $object->attribute( 'section_id' ), $policy['Section'] ) ) 04836 { 04837 return array(); 04838 } 04839 } 04840 04841 if ( isset( $policy['ParentClass'] ) ) 04842 { 04843 if ( $object === false ) 04844 $object = $this->attribute( 'object' ); 04845 04846 if ( !in_array( $object->attribute( 'contentclass_id' ), $policy['ParentClass'] ) ) 04847 { 04848 return array(); 04849 } 04850 } 04851 04852 if ( isset( $policy['ParentDepth'] ) && is_array( $policy['ParentDepth'] ) ) 04853 { 04854 $NodeDepth = $this->attribute( 'depth' ); 04855 if ( !in_array( '*', $policy['ParentDepth'] ) && !in_array( $NodeDepth, $policy['ParentDepth'] ) ) 04856 { 04857 return array(); 04858 } 04859 } 04860 04861 if ( isset( $policy['Assigned'] ) ) 04862 { 04863 if ( $object === false ) 04864 $object = $this->attribute( 'object' ); 04865 04866 if ( $object->attribute( 'owner_id' ) != $userID ) 04867 { 04868 return array(); 04869 } 04870 } 04871 04872 $allowedNode = false; 04873 if ( isset( $policy['Node'] ) ) 04874 { 04875 $allowed = false; 04876 foreach( $policy['Node'] as $nodeID ) 04877 { 04878 $mainNodeID = $this->attribute( 'main_node_id' ); 04879 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 04880 if ( $mainNodeID == $node['main_node_id'] ) 04881 { 04882 $allowed = true; 04883 $allowedNode = true; 04884 break; 04885 } 04886 } 04887 if ( !$allowed && !isset( $policy['Subtree'] ) ) 04888 { 04889 return array(); 04890 } 04891 } 04892 04893 if ( isset( $policy['Subtree'] ) ) 04894 { 04895 $allowed = false; 04896 if ( $object === false ) 04897 $object = $this->attribute( 'object' ); 04898 $assignedNodes = $object->attribute( 'assigned_nodes' ); 04899 foreach ( $assignedNodes as $assignedNode ) 04900 { 04901 $path = $assignedNode->attribute( 'path_string' ); 04902 foreach ( $policy['Subtree'] as $subtreeString ) 04903 { 04904 if ( strstr( $path, $subtreeString ) ) 04905 { 04906 $allowed = true; 04907 break; 04908 } 04909 } 04910 } 04911 if ( !$allowed && !$allowedNode ) 04912 { 04913 return array(); 04914 } 04915 } 04916 04917 if ( isset( $policy['Language'] ) ) 04918 { 04919 if ( $allowedLanguageCodes ) 04920 { 04921 $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] ); 04922 } 04923 else 04924 { 04925 $allowedLanguageCodes = $policy['Language']; 04926 } 04927 } 04928 04929 if ( $hasClassIDLimitation ) 04930 { 04931 return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes ); 04932 } 04933 return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes ); 04934 } 04935 04936 // This code is automatically generated from templates/classlistfrompolicy.ctpl 04937 // code-template::auto-generated:END class-list-from-policy 04938 04939 // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation, current-subtree-limitation 04940 // code-template::auto-generated:START can-instantiate-class-list 04941 // This code is automatically generated from templates/classcreatelist.ctpl 04942 // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD 04943 04944 /** 04945 * Checks if provided policy array has a limitation on current subtree 04946 * @param array $policy 04947 * @return bool 04948 */ 04949 protected function hasCurrentSubtreeLimitation( array $policy ) 04950 { 04951 // No Subtree nor Node limitation, so we consider that it is potentially OK to create content under current subtree 04952 if ( !isset( $policy['Subtree'] ) && !isset( $policy['Node'] ) ) 04953 { 04954 return true; 04955 } 04956 04957 // First check subtree limitations 04958 if ( isset( $policy['Subtree'] ) ) 04959 { 04960 foreach ( $policy['Subtree'] as $subtreeString ) 04961 { 04962 if ( strpos( $this->attribute( 'path_string' ), $subtreeString ) !== false ) 04963 { 04964 return true; 04965 } 04966 } 04967 } 04968 04969 // Then check node limitations 04970 if ( isset( $policy['Node'] ) ) 04971 { 04972 foreach ( $policy['Node'] as $subtreeString ) 04973 { 04974 if ( strpos( $this->attribute( 'path_string' ), $subtreeString ) !== false ) 04975 { 04976 return true; 04977 } 04978 } 04979 } 04980 04981 return false; 04982 } 04983 04984 /*! 04985 Finds all classes that the current user can create objects from and returns. 04986 It is also possible to filter the list event more with \a $includeFilter and \a $groupList. 04987 04988 \param $asObject If \c true then it return eZContentClass objects, if not it will 04989 be an associative array with \c name and \c id keys. 04990 \param $includeFilter If \c true then it will include only from class groups defined in 04991 \a $groupList, if not it will exclude those groups. 04992 \param $groupList An array with class group IDs that should be used in filtering, use 04993 \c false if you do not wish to filter at all. 04994 \param $fetchID A unique name for the current fetch, this must be supplied when filtering is 04995 used if you want caching to work. 04996 */ 04997 function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false ) 04998 { 04999 $ini = eZINI::instance(); 05000 $groupArray = array(); 05001 $languageCodeList = eZContentLanguage::fetchLocaleList(); 05002 $allowedLanguages = array( '*' => array() ); 05003 05004 $user = eZUser::currentUser(); 05005 $accessResult = $user->hasAccessTo( 'content' , 'create' ); 05006 $accessWord = $accessResult['accessWord']; 05007 05008 $classIDArray = array(); 05009 $classList = array(); 05010 $fetchAll = false; 05011 if ( $accessWord == 'yes' ) 05012 { 05013 $fetchAll = true; 05014 $allowedLanguages['*'] = $languageCodeList; 05015 } 05016 else if ( $accessWord == 'no' ) 05017 { 05018 // Cannot create any objects, return empty list. 05019 return $classList; 05020 } 05021 else 05022 { 05023 $policies = $accessResult['policies']; 05024 foreach ( $policies as $policyKey => $policy ) 05025 { 05026 $policyArray = $this->classListFromPolicy( $policy, $languageCodeList ); 05027 if ( empty( $policyArray ) ) 05028 { 05029 continue; 05030 } 05031 $classIDArrayPart = $policyArray['classes']; 05032 $languageCodeArrayPart = $policyArray['language_codes']; 05033 // No class limitation for this policy AND no previous limitation(s) 05034 if ( $classIDArrayPart == '*' && empty( $classIDArray ) ) 05035 { 05036 $fetchAll = true; 05037 $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) ); 05038 } 05039 else if ( is_array( $classIDArrayPart ) && $this->hasCurrentSubtreeLimitation( $policy ) ) 05040 { 05041 $fetchAll = false; 05042 foreach( $classIDArrayPart as $class ) 05043 { 05044 if ( isset( $allowedLanguages[$class] ) ) 05045 { 05046 $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) ); 05047 } 05048 else 05049 { 05050 $allowedLanguages[$class] = $languageCodeArrayPart; 05051 } 05052 } 05053 $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) ); 05054 } 05055 } 05056 } 05057 05058 $db = eZDB::instance(); 05059 05060 $filterTableSQL = ''; 05061 $filterSQL = ''; 05062 // Create extra SQL statements for the class group filters. 05063 if ( is_array( $groupList ) ) 05064 { 05065 if ( count( $groupList ) == 0 ) 05066 { 05067 return $classList; 05068 } 05069 05070 $filterTableSQL = ', ezcontentclass_classgroup ccg'; 05071 $filterSQL = ( " AND" . 05072 " cc.id = ccg.contentclass_id AND" . 05073 " " ); 05074 $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' ); 05075 } 05076 05077 $classNameFilter = eZContentClassName::sqlFilter( 'cc' ); 05078 05079 if ( $fetchAll ) 05080 { 05081 // If $asObject is true we fetch all fields in class 05082 $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]"; 05083 $rows = $db->arrayQuery( "SELECT DISTINCT $fields " . 05084 "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " . 05085 "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " . 05086 "ORDER BY $classNameFilter[nameField] ASC" ); 05087 $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject ); 05088 } 05089 else 05090 { 05091 // If the constrained class list is empty we are not allowed to create any class 05092 if ( count( $classIDArray ) == 0 ) 05093 { 05094 return $classList; 05095 } 05096 05097 $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' ); 05098 // If $asObject is true we fetch all fields in class 05099 $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]"; 05100 $rows = $db->arrayQuery( "SELECT DISTINCT $fields " . 05101 "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " . 05102 "WHERE $classIDCondition AND" . 05103 " cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " . 05104 "ORDER BY $classNameFilter[nameField] ASC" ); 05105 $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject ); 05106 } 05107 05108 if ( $asObject ) 05109 { 05110 foreach ( $classList as $key => $class ) 05111 { 05112 $id = $class->attribute( 'id' ); 05113 if ( isset( $allowedLanguages[$id] ) ) 05114 { 05115 $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) ); 05116 } 05117 else 05118 { 05119 $languageCodes = $allowedLanguages['*']; 05120 } 05121 $classList[$key]->setCanInstantiateLanguages( $languageCodes ); 05122 } 05123 } 05124 05125 eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" ); 05126 return $classList; 05127 } 05128 05129 // This code is automatically generated from templates/classcreatelist.ctpl 05130 // code-template::auto-generated:END can-instantiate-class-list 05131 05132 static function makeObjectsArray( $array , $with_contentobject = true ) 05133 { 05134 $retNodes = array(); 05135 if ( !is_array( $array ) ) 05136 return $retNodes; 05137 05138 foreach ( $array as $node ) 05139 { 05140 unset( $object ); 05141 05142 if( $node['node_id'] == 1 ) 05143 { 05144 if( !array_key_exists( 'name', $node ) || !$node['name'] ) 05145 $node['name'] = ezpI18n::tr( 'kernel/content', 'Top Level Nodes' ); 05146 } 05147 05148 $object = new eZContentObjectTreeNode( $node ); 05149 // If the name is not set it will be fetched later on when 05150 // getName()/attribute( 'name' ) is accessed. 05151 if ( isset( $node['name'] ) ) 05152 { 05153 $object->setName( $node['name'] ); 05154 } 05155 05156 if ( isset( $node['class_serialized_name_list'] ) ) 05157 { 05158 $node['class_name'] = eZContentClass::nameFromSerializedString( $node['class_serialized_name_list'] ); 05159 $object->ClassName = $node['class_name']; 05160 } 05161 if ( isset( $node['class_identifier'] ) ) 05162 $object->ClassIdentifier = $node['class_identifier']; 05163 05164 if ( isset( $node['is_container'] ) ) 05165 $object->ClassIsContainer = $node['is_container']; 05166 05167 if ( $with_contentobject ) 05168 { 05169 if ( isset( $node['class_name'] ) ) 05170 { 05171 unset( $node['remote_id'] ); 05172 $contentObject = new eZContentObject( $node ); 05173 05174 $permissions = array(); 05175 $contentObject->setPermissions( $permissions ); 05176 $contentObject->setClassName( $node['class_name'] ); 05177 if ( isset( $node['class_identifier'] ) ) 05178 $contentObject->ClassIdentifier = $node['class_identifier']; 05179 05180 } 05181 else 05182 { 05183 $contentObject = new eZContentObject( array()); 05184 if ( isset( $node['name'] ) ) 05185 $contentObject->setCachedName( $node['name'] ); 05186 } 05187 if ( isset( $node['real_translation'] ) && $node['real_translation'] != '' ) 05188 { 05189 $object->CurrentLanguage = $node['real_translation']; 05190 $contentObject->CurrentLanguage = $node['real_translation']; 05191 } 05192 if ( $node['node_id'] == 1 ) 05193 { 05194 $contentObject->ClassName = 'Folder'; 05195 $contentObject->ClassIdentifier = 'folder'; 05196 $contentObject->ClassID = 1; 05197 $contentObject->SectionID = 1; 05198 } 05199 05200 $object->setContentObject( $contentObject ); 05201 } 05202 $retNodes[] = $object; 05203 } 05204 return $retNodes; 05205 } 05206 05207 /*! 05208 Get parent node id by node id 05209 \param $nodeID the node id you want parent node id for. 05210 */ 05211 static function getParentNodeId( $nodeID ) 05212 { 05213 if ( !isset( $nodeID ) ) 05214 return null; 05215 $db = eZDB::instance(); 05216 $nodeID =(int) $nodeID; 05217 $parentArr = $db->arrayQuery( "SELECT 05218 parent_node_id 05219 FROM 05220 ezcontentobject_tree 05221 WHERE 05222 node_id = $nodeID"); 05223 return $parentArr[0]['parent_node_id']; 05224 } 05225 05226 /** 05227 * Get parent node id's by content object id's. 05228 * 05229 * @static 05230 * @since Version 4.1 05231 * @param int|array $objectIDs 05232 * @param bool $groupByObjectId groups parent node ids by object id they belong to. 05233 * @param bool $onlyMainNode limits result to parent node id of main node. 05234 * 05235 * @return array Returns array of parent node id's 05236 */ 05237 static function getParentNodeIdListByContentObjectID( $objectIDs, $groupByObjectId = false, $onlyMainNode = false ) 05238 { 05239 if ( !$objectIDs ) 05240 return null; 05241 if ( !is_array( $objectIDs ) ) 05242 $objectIDs = array( $objectIDs ); 05243 05244 $db = eZDB::instance(); 05245 $query = 'SELECT 05246 parent_node_id, contentobject_id 05247 FROM 05248 ezcontentobject_tree 05249 WHERE ' . $db->generateSQLINStatement( $objectIDs, 'contentobject_id', false, false, 'int' ); 05250 05251 if ( $onlyMainNode ) 05252 { 05253 $query .= ' AND node_id = main_node_id'; 05254 } 05255 05256 $list = $db->arrayQuery( $query ); 05257 $parentNodeIDs = array(); 05258 if ( $groupByObjectId ) 05259 { 05260 foreach( $list as $item ) 05261 { 05262 $objectID = $item['contentobject_id']; 05263 if ( !isset( $parentNodeIDs[$objectID] ) ) 05264 { 05265 $parentNodeIDs[$objectID] = array(); 05266 } 05267 $parentNodeIDs[$objectID][] = $item['parent_node_id']; 05268 } 05269 } 05270 else 05271 { 05272 foreach( $list as $item ) 05273 { 05274 $parentNodeIDs[] = $item['parent_node_id']; 05275 } 05276 } 05277 return $parentNodeIDs; 05278 } 05279 05280 /*! 05281 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05282 the calls within a db transaction; thus within db->begin and db->commit. 05283 */ 05284 static function deleteNodeWhereParent( $node, $id ) 05285 { 05286 eZContentObjectTreeNode::removeNode( eZContentObjectTreeNode::findNode( $node, $id ) ); 05287 05288 } 05289 05290 static function findNode( $parentNode, $id, $asObject = false, $remoteID = false ) 05291 { 05292 if ( !isset( $parentNode) || $parentNode == NULL ) 05293 { 05294 $parentNode = 2; 05295 } 05296 $parentNode =(int) $parentNode; 05297 $db = eZDB::instance(); 05298 if( $asObject ) 05299 { 05300 if ( $remoteID ) 05301 { 05302 $objectIDFilter = 'ezcontentobject.remote_id = ' . (string) $id; 05303 } 05304 else 05305 { 05306 $objectIDFilter = 'contentobject_id = ' . (int) $id; 05307 } 05308 05309 $query="SELECT ezcontentobject.*, 05310 ezcontentobject_tree.*, 05311 ezcontentclass.serialized_name_list as class_serialized_name_list, 05312 ezcontentclass.identifier as class_identifier, 05313 ezcontentclass.is_container as is_container 05314 FROM ezcontentobject_tree, 05315 ezcontentobject, 05316 ezcontentclass 05317 WHERE parent_node_id = $parentNode AND 05318 $objectIDFilter AND 05319 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 05320 ezcontentclass.version=0 AND 05321 ezcontentclass.id = ezcontentobject.contentclass_id "; 05322 05323 $nodeListArray = $db->arrayQuery( $query ); 05324 $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray ); 05325 05326 if ( count( $retNodeArray ) > 0 ) 05327 { 05328 return $retNodeArray[0]; 05329 } 05330 else 05331 { 05332 return null; 05333 } 05334 } 05335 else 05336 { 05337 $id = (int) $id; 05338 $getNodeQuery = "SELECT node_id 05339 FROM ezcontentobject_tree 05340 WHERE 05341 parent_node_id=$parentNode AND 05342 contentobject_id = $id "; 05343 $nodeArr = $db->arrayQuery( $getNodeQuery ); 05344 if ( isset( $nodeArr[0] ) ) 05345 { 05346 return $nodeArr[0]['node_id']; 05347 } 05348 else 05349 { 05350 return false; 05351 } 05352 } 05353 } 05354 05355 function getName( $language = false ) 05356 { 05357 // If the name is not set yet we fetch it from the object table 05358 if ( $this->Name === null || $language !== false ) 05359 { 05360 if ( $this->CurrentLanguage || $language !== false ) 05361 { 05362 $sql = "SELECT name FROM ezcontentobject_name WHERE contentobject_id=" . (int) $this->ContentObjectID . " AND content_version=" . (int)$this->attribute( 'contentobject_version' ) . " AND real_translation='" . ( $language !== false ? $language : $this->CurrentLanguage ) . "'"; 05363 } 05364 else 05365 { 05366 $sql = "SELECT name FROM ezcontentobject WHERE id=" . (int) $this->ContentObjectID; 05367 } 05368 $db = eZDB::instance(); 05369 $rows = $db->arrayQuery( $sql ); 05370 if ( count( $rows ) > 0 ) 05371 { 05372 if ( $language !== false ) 05373 { 05374 return $rows[0]['name']; 05375 } 05376 $this->Name = $rows[0]['name']; 05377 } 05378 else 05379 { 05380 if ( $language !== false ) 05381 { 05382 return false; 05383 } 05384 $this->Name = false; 05385 } 05386 } 05387 return $this->Name; 05388 } 05389 05390 function setName( $name ) 05391 { 05392 $this->Name = $name; 05393 } 05394 05395 /*! 05396 \static 05397 Creates propper nodeassigment from contentNodeDOMNode specification 05398 05399 \param contentobjecttreenode DOMNode 05400 \param contentobject. 05401 \param version 05402 \param isMain 05403 \param options 05404 05405 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05406 the calls within a db transaction; thus within db->begin and db->commit. 05407 */ 05408 static function unserialize( $contentNodeDOMNode, $contentObject, $version, $isMain, &$nodeList, &$options, $handlerType = 'ezcontentobject' ) 05409 { 05410 $parentNodeID = -1; 05411 05412 $remoteID = $contentNodeDOMNode->getAttribute( 'remote-id' ); 05413 $parentNodeRemoteID = $contentNodeDOMNode->getAttribute( 'parent-node-remote-id' ); 05414 $node = eZContentObjectTreeNode::fetchByRemoteID( $remoteID ); 05415 if ( is_object( $node ) ) 05416 { 05417 $description = "Node with remote ID $remoteID already exists."; 05418 05419 $choosenAction = eZPackageHandler::errorChoosenAction( eZContentObject::PACKAGE_ERROR_EXISTS, 05420 $options, $description, $handlerType, false ); 05421 05422 switch( $choosenAction ) 05423 { 05424 // In case user have choosen "Keep existing object and create new" 05425 case eZContentObject::PACKAGE_NEW: 05426 { 05427 $newRemoteID = eZRemoteIdUtility::generate( 'node' ); 05428 $node->setAttribute( 'remote_id', $newRemoteID ); 05429 $node->store(); 05430 $nodeInfo = array( 'contentobject_id' => $node->attribute( 'contentobject_id' ), 05431 'contentobject_version' => $node->attribute( 'contentobject_version' ), 05432 'parent_remote_id' => $remoteID ); 05433 $nodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(), 05434 null, 05435 $nodeInfo ); 05436 if ( is_object( $nodeAssignment ) ) 05437 { 05438 $nodeAssignment->setAttribute( 'parent_remote_id', $newRemoteID ); 05439 $nodeAssignment->store(); 05440 } 05441 } break; 05442 05443 // When running non-interactively with ezpm.php 05444 case eZPackage::NON_INTERACTIVE: 05445 case eZContentObject::PACKAGE_UPDATE: 05446 { 05447 // Update existing node settigns. 05448 if ( !$parentNodeRemoteID ) 05449 { 05450 // when top node of subtree export, only update node sort field and sort order 05451 $node->setAttribute( 'sort_field', eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ) ); 05452 $node->setAttribute( 'sort_order', $contentNodeDOMNode->getAttribute( 'sort-order' ) ); 05453 $node->store(); 05454 return true; 05455 } 05456 } break; 05457 05458 default: 05459 { 05460 // This error may occur only if data integrity is broken 05461 $options['error'] = array( 'error_code' => eZContentObject::PACKAGE_ERROR_NODE_EXISTS, 05462 'element_id' => $remoteID, 05463 'description' => $description ); 05464 return false; 05465 } break; 05466 } 05467 } 05468 05469 if ( $parentNodeRemoteID ) 05470 { 05471 $parentNode = eZContentObjectTreeNode::fetchByRemoteID( $parentNodeRemoteID ); 05472 if ( $parentNode !== null ) 05473 { 05474 $parentNodeID = $parentNode->attribute( 'node_id' ); 05475 } 05476 } 05477 else 05478 { 05479 if ( isset( $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id'] ) ) 05480 { 05481 $parentNodeID = $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id']; 05482 } 05483 else if ( isset( $options['top_nodes_map']['*'] ) ) 05484 { 05485 $parentNodeID = $options['top_nodes_map']['*']; 05486 } 05487 else 05488 { 05489 eZDebug::writeError( 'New parent node not set ' . $contentNodeDOMNode->getAttribute( 'name' ), __METHOD__ ); 05490 } 05491 } 05492 05493 $isMain = ( $isMain && $contentNodeDOMNode->getAttribute( 'is-main-node' ) ); 05494 05495 $nodeInfo = array( 'contentobject_id' => $contentObject->attribute( 'id' ), 05496 'contentobject_version' => $version, 05497 'is_main' => $isMain, 05498 'parent_node' => $parentNodeID, 05499 'parent_remote_id' => $remoteID, // meaning processed node remoteID (not parent) 05500 'sort_field' => eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ), 05501 'sort_order' => $contentNodeDOMNode->getAttribute( 'sort-order' ) ); 05502 05503 if ( $parentNodeID == -1 && $parentNodeRemoteID ) 05504 { 05505 if ( !isset( $options['suspended-nodes'] ) ) 05506 { 05507 $options['suspended-nodes'] = array(); 05508 } 05509 05510 $options['suspended-nodes'][$parentNodeRemoteID] = array( 'nodeinfo' => $nodeInfo, 05511 'priority' => $contentNodeDOMNode->getAttribute( 'priority' ) ); 05512 return true; 05513 } 05514 05515 $existNodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(), 05516 null, 05517 $nodeInfo ); 05518 $nodeInfo['priority'] = $contentNodeDOMNode->getAttribute( 'priority' ); 05519 if( !is_object( $existNodeAssignment ) ) 05520 { 05521 $nodeAssignment = eZNodeAssignment::create( $nodeInfo ); 05522 $nodeList[] = $nodeInfo; 05523 $nodeAssignment->store(); 05524 } 05525 05526 return true; 05527 } 05528 05529 /*! 05530 Serialize ContentObjectTreeNode 05531 05532 \params $options 05533 \params contentNodeIDArray 05534 \params topNodeIDArray 05535 */ 05536 function serialize( $options, $contentNodeIDArray, $topNodeIDArray ) 05537 { 05538 if ( $options['node_assignment'] == 'main' && 05539 $this->attribute( 'main_node_id' ) != $this->attribute( 'node_id' ) ) 05540 { 05541 return false; 05542 } 05543 if ( ! in_array( $this->attribute( 'node_id' ), array_keys( $contentNodeIDArray ) ) ) 05544 { 05545 return false; 05546 } 05547 05548 $dom = new DOMDocument( '1.0', 'utf-8' ); 05549 05550 $nodeAssignmentNode = $dom->createElement( 'node-assignment' ); 05551 if ( $this->attribute( 'main_node_id' ) == $this->attribute( 'node_id' ) ) 05552 { 05553 $nodeAssignmentNode->setAttribute( 'is-main-node', 1 ); 05554 } 05555 if( !in_array( $this->attribute( 'node_id'), $topNodeIDArray ) ) 05556 { 05557 $parentNode = $this->attribute( 'parent' ); 05558 $nodeAssignmentNode->setAttribute( 'parent-node-remote-id', $parentNode->attribute( 'remote_id' ) ); 05559 } 05560 $nodeAssignmentNode->setAttribute( 'name', $this->attribute( 'name' ) ); 05561 $nodeAssignmentNode->setAttribute( 'node-id', $this->attribute( 'node_id' ) ); 05562 $nodeAssignmentNode->setAttribute( 'remote-id', $this->attribute( 'remote_id' ) ); 05563 $nodeAssignmentNode->setAttribute( 'sort-field', eZContentObjectTreeNode::sortFieldName( $this->attribute( 'sort_field' ) ) ); 05564 $nodeAssignmentNode->setAttribute( 'sort-order', $this->attribute( 'sort_order' ) ); 05565 $nodeAssignmentNode->setAttribute( 'priority', $this->attribute( 'priority' ) ); 05566 return $nodeAssignmentNode; 05567 } 05568 05569 /*! 05570 Update and store modified_subnode value for this node and all super nodes. 05571 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05572 the calls within a db transaction; thus within db->begin and db->commit. 05573 */ 05574 function updateAndStoreModified() 05575 { 05576 $pathString = trim( $this->attribute( 'path_string' ), '/' ); 05577 05578 // during publishing, a temporary path is generated. The update query shouldn't be executed as it doesn't affect anything 05579 if ( $pathString == 'TEMPPATH' ) 05580 return; 05581 $pathArray = explode( '/', $pathString ); 05582 05583 if ( count( $pathArray ) > 0 ) 05584 { 05585 $db = eZDB::instance(); 05586 $db->query( 'UPDATE ezcontentobject_tree SET modified_subnode=' . time() . 05587 ' WHERE ' . $db->generateSQLINStatement( $pathArray, 'node_id', false, true, 'int' ) ); 05588 } 05589 } 05590 05591 /*! 05592 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05593 the calls within a db transaction; thus within db->begin and db->commit. 05594 */ 05595 function store( $fieldFilters = null ) 05596 { 05597 $db = eZDB::instance(); 05598 05599 $db->begin(); 05600 eZPersistentObject::store( $fieldFilters ); 05601 $this->updateAndStoreModified(); 05602 $db->commit(); 05603 } 05604 05605 function object() 05606 { 05607 if ( $this->hasContentObject() ) 05608 { 05609 return $this->ContentObject; 05610 } 05611 $contentobject_id = $this->attribute( 'contentobject_id' ); 05612 $obj = eZContentObject::fetch( $contentobject_id ); 05613 $obj->setCurrentLanguage( $this->CurrentLanguage ); 05614 $this->ContentObject = $obj; 05615 return $obj; 05616 } 05617 05618 function hasContentObject() 05619 { 05620 if ( isset( $this->ContentObject ) && $this->ContentObject instanceof eZContentObject ) 05621 return true; 05622 else 05623 return false; 05624 } 05625 05626 /*! 05627 Sets the current content object for this node. 05628 */ 05629 function setContentObject( $object ) 05630 { 05631 $this->ContentObject = $object; 05632 } 05633 05634 /*! 05635 \return the creator of the version published in the node. 05636 */ 05637 function creator() 05638 { 05639 $db = eZDB::instance(); 05640 $query = "SELECT creator_id 05641 FROM ezcontentobject_version 05642 WHERE 05643 contentobject_id = '$this->ContentObjectID' AND 05644 version = '$this->ContentObjectVersion' "; 05645 05646 $creatorArray = $db->arrayQuery( $query ); 05647 return eZContentObject::fetch( $creatorArray[0]['creator_id'] ); 05648 } 05649 05650 function contentObjectVersionObject( $asObject = true ) 05651 { 05652 $version = eZContentObjectVersion::fetchVersion( $this->ContentObjectVersion, $this->ContentObjectID, $asObject ); 05653 if ( $this->CurrentLanguage != false ) 05654 { 05655 $version->CurrentLanguage = $this->CurrentLanguage; 05656 } 05657 return $version; 05658 } 05659 05660 function urlAlias() 05661 { 05662 $useURLAlias =& $GLOBALS['eZContentObjectTreeNodeUseURLAlias']; 05663 $ini = eZINI::instance(); 05664 $cleanURL = ''; 05665 if ( !isset( $useURLAlias ) ) 05666 { 05667 $useURLAlias = $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled'; 05668 } 05669 if ( $useURLAlias ) 05670 { 05671 $path = $this->pathWithNames(); 05672 if ( $ini->hasVariable( 'SiteAccessSettings', 'PathPrefix' ) && 05673 $ini->variable( 'SiteAccessSettings', 'PathPrefix' ) != '' ) 05674 { 05675 $prepend = $ini->variable( 'SiteAccessSettings', 'PathPrefix' ); 05676 $pathIdenStr = substr( $prepend, strlen( $prepend ) -1 ) == '/' 05677 ? $path . '/' 05678 : $path; 05679 if ( strncasecmp( $pathIdenStr, $prepend, strlen( $prepend ) ) == 0 ) 05680 $cleanURL = eZURLAliasML::cleanURL( substr( $path, strlen( $prepend ) ) ); 05681 else 05682 $cleanURL = eZURLAliasML::cleanURL( $path ); 05683 } 05684 else 05685 { 05686 $cleanURL = eZURLAliasML::cleanURL( $path ); 05687 } 05688 } 05689 else 05690 { 05691 $cleanURL = eZURLAliasML::cleanURL( 'content/view/full/' . $this->NodeID ); 05692 } 05693 05694 return $cleanURL; 05695 } 05696 05697 function url() 05698 { 05699 $ini = eZINI::instance(); 05700 if ( $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled' ) 05701 { 05702 return $this->urlAlias(); 05703 } 05704 return 'content/view/full/' . $this->NodeID; 05705 } 05706 05707 05708 /*! 05709 \return the cached value of the class identifier if it exists, if not it's fetched dynamically 05710 */ 05711 public function classIdentifier() 05712 { 05713 if ( $this->ClassIdentifier === null ) 05714 { 05715 $object = $this->object(); 05716 $this->ClassIdentifier = $object->contentClassIdentifier(); 05717 } 05718 05719 return $this->ClassIdentifier; 05720 } 05721 05722 /*! 05723 \return the cached value of the class name if it exists, if not it's fetched dynamically 05724 */ 05725 public function className() 05726 { 05727 if ( $this->ClassName === null ) 05728 { 05729 $object = $this->object(); 05730 $class = $object->contentClass(); 05731 $this->ClassName = $class->attribute( 'name' ); 05732 } 05733 05734 return $this->ClassName; 05735 } 05736 05737 /*! 05738 \return the cached value of the class is_container flag if it exists, if not it's fetched dynamically 05739 */ 05740 public function classIsContainer() 05741 { 05742 if ( $this->ClassIsContainer === null ) 05743 { 05744 $object = $this->object(); 05745 $class = $object->contentClass(); 05746 $this->ClassIsContainer = $class->attribute( 'is_container' ); 05747 } 05748 return $this->ClassIsContainer; 05749 } 05750 05751 /*! 05752 \return combined string representation of both "is_hidden" and "is_invisible" attributes 05753 Used in the node view templates. 05754 FIXME: this method probably should be removed in the future. 05755 */ 05756 function hiddenInvisibleString() 05757 { 05758 return ( $this->IsHidden ? 'H' : '-' ) . '/' . ( $this->IsInvisible ? 'X' : '-' ); 05759 } 05760 05761 /*! 05762 \return combined string representation of both "is_hidden" and "is_invisible" attributes 05763 Used in the limitation handling templates. 05764 */ 05765 function hiddenStatusString() 05766 { 05767 if( $this->IsHidden ) 05768 { 05769 return ezpI18n::tr( 'kernel/content', 'Hidden' ); 05770 } 05771 else if( $this->IsInvisible ) 05772 { 05773 return ezpI18n::tr( 'kernel/content', 'Hidden by superior' ); 05774 } 05775 return ezpI18n::tr( 'kernel/content', 'Visible' ); 05776 } 05777 05778 /*! 05779 \a static 05780 05781 \param $node Root node of the subtree 05782 \param $modifyRootNode Whether to modify the root node (true/false) 05783 05784 Hide algorithm: 05785 if ( root node of the subtree is visible ) 05786 { 05787 1) Mark root node as hidden and invisible 05788 2) Recursively mark child nodes as invisible except for ones which have been previously marked as invisible 05789 } 05790 else 05791 { 05792 Mark root node as hidden 05793 } 05794 05795 In some cases we don't want to touch the root node when (un)hiding a subtree, for example 05796 after content/move or content/copy. 05797 That's why $modifyRootNode argument is used. 05798 05799 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05800 the calls within a db transaction; thus within db->begin and db->commit. 05801 */ 05802 static function hideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true ) 05803 { 05804 $nodeID = $node->attribute( 'node_id' ); 05805 $time = time(); 05806 $db = eZDB::instance(); 05807 05808 if ( eZAudit::isAuditEnabled() ) 05809 { 05810 // Set audit params. 05811 $objectID = $node->attribute( 'contentobject_id' ); 05812 $objectName = $node->attribute( 'name' ); 05813 eZAudit::writeAudit( 'content-hide', array( 'Node ID' => $nodeID, 05814 'Object ID' => $objectID, 05815 'Content Name' => $objectName, 05816 'Time' => $time, 05817 'Comment' => 'Node has been hidden: eZContentObjectTreeNode::hideSubTree()' ) ); 05818 } 05819 05820 $db->begin(); 05821 05822 if ( !$node->attribute( 'is_invisible' ) ) // if root node is visible 05823 { 05824 // 1) Mark root node as hidden and invisible. 05825 if ( $modifyRootNode ) 05826 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, is_invisible=1, modified_subnode=$time WHERE node_id=$nodeID" ); 05827 05828 // 2) Recursively mark child nodes as invisible, except for ones which have been previously marked as invisible. 05829 $nodePath = $node->attribute( 'path_string' ); 05830 $db->query( "UPDATE ezcontentobject_tree SET is_invisible=1, modified_subnode=$time WHERE is_invisible=0 AND path_string LIKE '$nodePath%'" ); 05831 } 05832 else 05833 { 05834 // Mark root node as hidden 05835 if ( $modifyRootNode ) 05836 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, modified_subnode=$time WHERE node_id=$nodeID" ); 05837 } 05838 05839 $node->updateAndStoreModified(); 05840 05841 $db->commit(); 05842 05843 eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode ); 05844 } 05845 05846 /*! 05847 \a static 05848 05849 \param $node Root node of the subtree 05850 \param $modifyRootNode Whether to modify the root node (true/false) 05851 05852 Unhide algorithm: 05853 if ( parent node is visible ) 05854 { 05855 1) Mark root node as not hidden and visible. 05856 2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children). 05857 } 05858 else 05859 { 05860 Mark root node as not hidden. 05861 } 05862 05863 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05864 the calls within a db transaction; thus within db->begin and db->commit. 05865 */ 05866 static function unhideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true ) 05867 { 05868 $nodeID = $node->attribute( 'node_id' ); 05869 $nodePath = $node->attribute( 'path_string' ); 05870 $nodeInvisible = $node->attribute( 'is_invisible' ); 05871 $parentNode = $node->attribute( 'parent' ); 05872 if ( !$parentNode instanceof eZContentObjectTreeNode ) 05873 { 05874 eZDebug::writeError( "Parent of Node #$nodeId doesn't exist or inaccesible.", __METHOD__ ); 05875 return; 05876 } 05877 05878 $time = time(); 05879 05880 if ( eZAudit::isAuditEnabled() ) 05881 { 05882 // Set audit params. 05883 $objectID = $node->attribute( 'contentobject_id' ); 05884 $objectName = $node->attribute( 'name' ); 05885 05886 eZAudit::writeAudit( 'content-hide', array( 'Node ID' => $nodeID, 05887 'Object ID' => $objectID, 05888 'Content Name' => $objectName, 05889 'Time' => $time, 05890 'Comment' => 'Node has been unhidden: eZContentObjectTreeNode::unhideSubTree()' ) ); 05891 } 05892 05893 $db = eZDB::instance(); 05894 05895 $db->begin(); 05896 05897 if ( ! $parentNode->attribute( 'is_invisible' ) ) // if parent node is visible 05898 { 05899 // 1) Mark root node as not hidden and visible. 05900 if ( $modifyRootNode ) 05901 $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" ); 05902 05903 // 2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children). 05904 05905 // 2.1) $hiddenChildren = Fetch all hidden children for the root node 05906 $hiddenChildren = $db->arrayQuery( "SELECT path_string FROM ezcontentobject_tree " . 05907 "WHERE node_id <> $nodeID AND is_hidden=1 AND path_string LIKE '$nodePath%'" ); 05908 $skipSubtreesString = ''; 05909 foreach ( $hiddenChildren as $i ) 05910 $skipSubtreesString .= " AND path_string NOT LIKE '" . $i['path_string'] . "%'"; 05911 05912 // 2.2) Mark those children as visible which are not under nodes in $hiddenChildren 05913 $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, modified_subnode=$time WHERE path_string LIKE '$nodePath%' $skipSubtreesString" ); 05914 } 05915 else 05916 { 05917 // Mark root node as not hidden. 05918 if ( $modifyRootNode ) 05919 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" ); 05920 } 05921 05922 $node->updateAndStoreModified(); 05923 05924 $db->commit(); 05925 05926 eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode ); 05927 } 05928 05929 /*! 05930 \a static 05931 Depending on the new parent node visibility, recompute "is_invisible" attribute for the given node and its children. 05932 (used after content/move or content/copy) 05933 */ 05934 static function updateNodeVisibility( $node, $parentNode, $recursive = true ) 05935 { 05936 if ( !$node ) 05937 { 05938 eZDebug::writeWarning( 'No such node to update visibility for.' ); 05939 return; 05940 } 05941 05942 if ( !$parentNode ) 05943 { 05944 eZDebug::writeWarning( 'No parent node found when updating node visibility' ); 05945 return; 05946 } 05947 05948 if ( $node->attribute( 'is_hidden' ) == 0 && 05949 $parentNode->attribute( 'is_invisible' ) != $node->attribute( 'is_invisible' ) ) 05950 { 05951 $parentNodeIsVisible = $parentNode->attribute( 'is_invisible' ); 05952 $nodeID = $node->attribute( 'node_id' ); 05953 $db = eZDB::instance(); 05954 $db->begin(); 05955 $db->query( "UPDATE ezcontentobject_tree SET is_invisible=$parentNodeIsVisible WHERE node_id=$nodeID" ); 05956 05957 if ( $recursive ) 05958 { 05959 // update visibility for children of the node 05960 if( $parentNodeIsVisible ) 05961 eZContentObjectTreeNode::hideSubTree( $node, $modifyRootNode = false ); 05962 else 05963 eZContentObjectTreeNode::unhideSubTree( $node, $modifyRootNode = false ); 05964 } 05965 $db->commit(); 05966 } 05967 } 05968 05969 /*! 05970 \a static 05971 \return true on success, false otherwise 05972 */ 05973 static function clearViewCacheForSubtree( eZContentObjectTreeNode $node, $clearForRootNode = true ) 05974 { 05975 // Max nodes to fetch at a time 05976 static $limit = 50; 05977 05978 if ( $clearForRootNode ) 05979 { 05980 $objectID = $node->attribute( 'contentobject_id' ); 05981 eZContentCacheManager::clearContentCacheIfNeeded( $objectID ); 05982 } 05983 05984 $offset = 0; 05985 $params = array( 'AsObject' => false, 05986 'Depth' => false, 05987 'Limitation' => array() ); // Empty array means no permission checking 05988 $subtreeCount = $node->subTreeCount( $params ); 05989 while ( $offset < $subtreeCount ) 05990 { 05991 $params['Offset'] = $offset; 05992 $params['Limit'] = $limit; 05993 05994 $subtreeChunk = $node->subTree( $params ); 05995 $nNodesInChunk = count( $subtreeChunk ); 05996 $offset += $nNodesInChunk; 05997 if ( $nNodesInChunk == 0 ) 05998 break; 05999 06000 $objectIDList = array(); 06001 foreach ( $subtreeChunk as $curNode ) 06002 $objectIDList[] = $curNode['contentobject_id']; 06003 $objectIDList = array_unique( $objectIDList ); 06004 unset( $subtreeChunk ); 06005 06006 foreach ( $objectIDList as $objectID ) 06007 eZContentCacheManager::clearContentCacheIfNeeded( $objectID ); 06008 } 06009 06010 return true; 06011 } 06012 06013 static function setVersionByObjectID( $objectID, $newVersion ) 06014 { 06015 $db = eZDB::instance(); 06016 $db->query( "UPDATE ezcontentobject_tree SET contentobject_version='$newVersion' WHERE contentobject_id='$objectID'" ); 06017 } 06018 06019 function currentLanguage() 06020 { 06021 return $this->CurrentLanguage; 06022 } 06023 06024 function setCurrentLanguage( $languageCode ) 06025 { 06026 $this->CurrentLanguage = $languageCode; 06027 if ( $this->hasContentObject() ) 06028 { 06029 $this->ContentObject->setCurrentLanguage( $languageCode ); 06030 } 06031 $this->Name = null; 06032 } 06033 06034 /*! 06035 \static 06036 */ 06037 static function parentDepthLimitationList() 06038 { 06039 $maxLevel = 0; 06040 $ini = eZINI::instance(); 06041 if ( $ini->hasVariable( 'RoleSettings', 'MaxParentDepthLimitation' ) ) 06042 $maxLevel = $ini->variable( 'RoleSettings', 'MaxParentDepthLimitation' ); 06043 06044 $depthArray = array(); 06045 for ( $i = 1; $i <= $maxLevel; $i++ ) 06046 { 06047 $depthArray[] = array( 'name' => $i, 'id' => $i ); 06048 } 06049 06050 return $depthArray; 06051 } 06052 06053 /* 06054 Returns available classes as Js array. 06055 Checks if the node is container, if yes emptyStr will be returned. 06056 */ 06057 function availableClassesJsArray() 06058 { 06059 return eZContentObjectTreeNode::availableClassListJsArray( array( 'node' => $this ) ); 06060 } 06061 06062 /* 06063 Returns available classes as Js array. 06064 Checks for ini settings. 06065 */ 06066 static function availableClassListJsArray( $parameters = false ) 06067 { 06068 $iniMenu = eZINI::instance( 'contentstructuremenu.ini' ); 06069 $falseValue = "''"; 06070 06071 if ( $iniMenu->hasVariable( 'TreeMenu', 'CreateHereMenu' ) ) 06072 { 06073 $createHereMenu = $iniMenu->variable( 'TreeMenu', 'CreateHereMenu' ); 06074 } 06075 else 06076 { 06077 $createHereMenu = 'simplified'; 06078 } 06079 06080 if ( $createHereMenu != 'simplified' and $createHereMenu != 'full' ) 06081 return $falseValue; 06082 06083 $ini = eZINI::instance( 'content.ini' ); 06084 list( $usersClassGroupID, $setupClassGroupID ) = $ini->variableMulti( 'ClassGroupIDs', array( 'Users', 'Setup' ) ); 06085 $userRootNode = $ini->variable( 'NodeSettings', 'UserRootNode' ); 06086 $groupIDs = false; 06087 $filterType = 'include'; 06088 06089 if ( !is_array( $parameters ) ) 06090 return $falseValue; 06091 06092 $node = isset( $parameters['node'] ) ? $parameters['node'] : false; 06093 if ( is_object( $node ) ) 06094 { 06095 if ( $createHereMenu == 'full' and !$node->canCreate() ) 06096 return $falseValue; 06097 06098 $obj = $node->object(); 06099 $contentClass = $obj->attribute( 'content_class' ); 06100 if ( !$contentClass->attribute( 'is_container' ) ) 06101 { 06102 return $falseValue; 06103 } 06104 06105 $pathArray = $node->pathArray(); 06106 } 06107 else 06108 { 06109 // If current object is not container we should not return class list, should not display "create here" menu. 06110 if ( isset( $parameters['is_container'] ) and !$parameters['is_container'] ) 06111 return $falseValue; 06112 06113 // Check if current user can create under this node 06114 if ( $createHereMenu == 'full' and isset( $parameters['node_id'] ) ) 06115 { 06116 $node = eZContentObjectTreeNode::fetch( $parameters['node_id'] ); 06117 if ( is_object( $node ) and !$node->canCreate() ) 06118 return $falseValue; 06119 } 06120 $pathString = isset( $parameters['path_string'] ) ? $parameters['path_string'] : false; 06121 if ( !$pathString ) 06122 return $falseValue; 06123 06124 $pathItems = explode( '/', $pathString ); 06125 $pathArray = array(); 06126 foreach ( $pathItems as $pathItem ) 06127 { 06128 if ( $pathItem != '' ) 06129 $pathArray[] = (int) $pathItem; 06130 } 06131 } 06132 06133 if ( in_array( $userRootNode, $pathArray ) ) 06134 { 06135 $groupIDs = array( $usersClassGroupID ); 06136 } 06137 else 06138 { 06139 $groupIDs = array( $usersClassGroupID, $setupClassGroupID ); 06140 $filterType = 'exclude'; 06141 } 06142 06143 if ( $createHereMenu == 'simplified' ) 06144 { 06145 $classes = eZContentClass::fetchAllClasses( false, $filterType == 'include', $groupIDs ); 06146 return eZContentObjectTreeNode::getClassesJsArray( false, $filterType == 'include', $groupIDs, false, $classes ); 06147 } 06148 06149 return eZContentObjectTreeNode::getClassesJsArray( $node, $filterType == 'include', $groupIDs ); 06150 } 06151 06152 /* 06153 Returns available classes as Js array. 06154 \note building js array. 06155 */ 06156 static function getClassesJsArray( $node = false, $includeFilter = true, $groupList = false, $fetchID = false, $classes = false ) 06157 { 06158 $falseValue = "''"; 06159 // If $classes is false we should check $node and fetch class list 06160 if ( $classes === false ) 06161 { 06162 // If $node is object we should fetch available classes from node, from ezcontentclass otherwise 06163 $classes = ( $node instanceOf eZContentObjectTreeNode ) 06164 ? $node->canCreateClassList( false, $includeFilter, $groupList, $fetchID ) 06165 : eZContentClass::canInstantiateClassList( false, $includeFilter, $groupList, $fetchID ); 06166 } 06167 if ( !is_array( $classes ) ) 06168 return $falseValue; 06169 06170 // Create javascript array 06171 $classList = array(); 06172 foreach ( $classes as $class ) 06173 { 06174 if ( $class instanceOf eZContentClass ) 06175 { 06176 $classID = $class->attribute( 'id' ); 06177 $className = $class->attribute( 'name' ); 06178 } 06179 elseif ( is_array( $class ) ) 06180 { 06181 $classID = $class['id']; 06182 $className = $class['name']; 06183 } 06184 $classList[] = array( 06185 'classID' => (int) $classID, 06186 'name' => $className 06187 ); 06188 } 06189 06190 if ( $classList ) 06191 return json_encode( $classList ); 06192 06193 return $falseValue; 06194 } 06195 06196 06197 /// The current language for the node 06198 public $CurrentLanguage = false; 06199 06200 /// Name of the node 06201 public $Name; 06202 06203 /// Contains the cached value of the class identifier 06204 public $ClassIdentifier = null; 06205 public $ClassName = null; 06206 protected $ClassIsContainer = null; 06207 } 06208 06209 ?>