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