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