PageRenderTime 79ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/kernel/classes/ezcontentobjecttreenode.php

https://github.com/granitegreg/ezpublish
PHP | 6134 lines | 4744 code | 572 blank | 818 comment | 610 complexity | 9d688ed6a4d53f7b14692975e5449f27 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. //
  3. // Definition of eZContentObjectTreeNode class
  4. //
  5. // Created on: <10-Jul-2002 19:28:22 sp>
  6. //
  7. // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  8. // SOFTWARE NAME: eZ Publish
  9. // SOFTWARE RELEASE: 4.1.x
  10. // COPYRIGHT NOTICE: Copyright (C) 1999-2011 eZ Systems AS
  11. // SOFTWARE LICENSE: GNU General Public License v2.0
  12. // NOTICE: >
  13. // This program is free software; you can redistribute it and/or
  14. // modify it under the terms of version 2.0 of the GNU General
  15. // Public License as published by the Free Software Foundation.
  16. //
  17. // This program is distributed in the hope that it will be useful,
  18. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. // GNU General Public License for more details.
  21. //
  22. // You should have received a copy of version 2.0 of the GNU General
  23. // Public License along with this program; if not, write to the Free
  24. // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25. // MA 02110-1301, USA.
  26. //
  27. //
  28. // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  29. //
  30. /*! \file
  31. */
  32. /*!
  33. \class eZContentObjectTreeNode ezcontentobjecttreenode.php
  34. \brief The class eZContentObjectTreeNode does
  35. \verbatim
  36. Some algorithms
  37. ----------
  38. 1. Adding new Node
  39. Enter 1 - parent_node
  40. 2 - contentobject_id, ( that is like a node value )
  41. (a) - get path_string, depth for parent node to built path_string and to count depth for new one
  42. (c) - calculating attributes for new node and inserting it
  43. Returns node_id for added node
  44. 2. Deleting node ( or subtree )
  45. Enter - node_id
  46. 3. Move subtree in tree
  47. Enter node_id,new_parent_id
  48. 4. fetching subtree
  49. \endverbatim
  50. */
  51. class eZContentObjectTreeNode extends eZPersistentObject
  52. {
  53. const SORT_FIELD_PATH = 1;
  54. const SORT_FIELD_PUBLISHED = 2;
  55. const SORT_FIELD_MODIFIED = 3;
  56. const SORT_FIELD_SECTION = 4;
  57. const SORT_FIELD_DEPTH = 5;
  58. const SORT_FIELD_CLASS_IDENTIFIER = 6;
  59. const SORT_FIELD_CLASS_NAME = 7;
  60. const SORT_FIELD_PRIORITY = 8;
  61. const SORT_FIELD_NAME = 9;
  62. const SORT_FIELD_MODIFIED_SUBNODE = 10;
  63. const SORT_FIELD_NODE_ID = 11;
  64. const SORT_FIELD_CONTENTOBJECT_ID = 12;
  65. const SORT_ORDER_DESC = 0;
  66. const SORT_ORDER_ASC = 1;
  67. /*!
  68. Constructor
  69. */
  70. function eZContentObjectTreeNode( $row = array() )
  71. {
  72. $this->eZPersistentObject( $row );
  73. }
  74. static function definition()
  75. {
  76. static $definition = array( "fields" => array( "node_id" => array( 'name' => "NodeID",
  77. 'datatype' => 'integer',
  78. 'default' => 0,
  79. 'required' => true ),
  80. "parent_node_id" => array( 'name' => "ParentNodeID",
  81. 'datatype' => 'integer',
  82. 'default' => 0,
  83. 'required' => true,
  84. 'foreign_class' => 'eZContentObjectTreeNode',
  85. 'foreign_attribute' => 'node_id',
  86. 'multiplicity' => '1..*' ),
  87. "main_node_id" => array( 'name' => "MainNodeID",
  88. 'datatype' => 'integer',
  89. 'default' => 0,
  90. 'required' => true,
  91. 'foreign_class' => 'eZContentObjectTreeNode',
  92. 'foreign_attribute' => 'node_id',
  93. 'multiplicity' => '1..*' ),
  94. "contentobject_id" => array( 'name' => "ContentObjectID",
  95. 'datatype' => 'integer',
  96. 'default' => 0,
  97. 'required' => true,
  98. 'foreign_class' => 'eZContentObject',
  99. 'foreign_attribute' => 'id',
  100. 'multiplicity' => '1..*' ),
  101. 'contentobject_version' => array( 'name' => 'ContentObjectVersion',
  102. 'datatype' => 'integer',
  103. 'default' => 0,
  104. 'required' => true ),
  105. 'contentobject_is_published' => array( 'name' => 'ContentObjectIsPublished',
  106. 'datatype' => 'integer',
  107. 'default' => 0,
  108. 'required' => true ),
  109. "depth" => array( 'name' => "Depth",
  110. 'datatype' => 'integer',
  111. 'default' => 0,
  112. 'required' => true ),
  113. 'sort_field' => array( 'name' => 'SortField',
  114. 'datatype' => 'integer',
  115. 'default' => self::SORT_FIELD_PATH,
  116. 'required' => true ),
  117. 'sort_order' => array( 'name' => 'SortOrder',
  118. 'datatype' => 'integer',
  119. 'default' => self::SORT_ORDER_ASC,
  120. 'required' => true ),
  121. 'priority' => array( 'name' => 'Priority',
  122. 'datatype' => 'integer',
  123. 'default' => 0,
  124. 'required' => true ),
  125. 'modified_subnode' => array( 'name' => 'ModifiedSubNode',
  126. 'datatype' => 'integer',
  127. 'default' => 0,
  128. 'required' => true ),
  129. "path_string" => array( 'name' => "PathString",
  130. 'datatype' => 'string',
  131. 'default' => '',
  132. 'required' => true ),
  133. "path_identification_string" => array( 'name' => "PathIdentificationString",
  134. 'datatype' => 'text',
  135. 'default' => '',
  136. 'required' => true ),
  137. 'remote_id' => array( 'name' => 'RemoteID',
  138. 'datatype' => 'string',
  139. 'default' => '',
  140. 'required' => true ),
  141. "is_hidden" => array( 'name' => "IsHidden",
  142. 'datatype' => 'integer',
  143. 'default' => 0,
  144. 'required' => true ),
  145. "is_invisible" => array( 'name' => "IsInvisible",
  146. 'datatype' => 'integer',
  147. 'default' => 0,
  148. 'required' => true ) ),
  149. "keys" => array( "node_id" ),
  150. "function_attributes" => array( "name" => "getName",
  151. 'data_map' => 'dataMap',
  152. 'remote_id' => 'remoteID', // Note: This overrides remote_id field
  153. "object" => "object",
  154. "subtree" => "subTree",
  155. "children" => "children",
  156. "children_count" => "childrenCount",
  157. 'view_count' => 'viewCount',
  158. 'contentobject_version_object' => 'contentObjectVersionObject',
  159. 'sort_array' => 'sortArray',
  160. 'can_read' => 'canRead',
  161. 'can_pdf' => 'canPdf',
  162. 'can_create' => 'canCreate',
  163. 'can_edit' => 'canEdit',
  164. 'can_hide' => 'canHide',
  165. 'can_remove' => 'canRemove',
  166. 'can_move' => 'canMoveFrom',
  167. 'can_move_from' => 'canMoveFrom',
  168. 'can_add_location' => 'canAddLocation',
  169. 'can_remove_location' => 'canRemoveLocation',
  170. 'can_view_embed' => 'canViewEmbed',
  171. 'is_main' => 'isMain',
  172. 'creator' => 'creator',
  173. "path_with_names" => "pathWithNames",
  174. "path" => "fetchPath",
  175. 'path_array' => 'pathArray',
  176. "parent" => "fetchParent",
  177. 'url' => 'url',
  178. 'url_alias' => 'urlAlias',
  179. 'class_identifier' => 'classIdentifier',
  180. 'class_name' => 'className',
  181. 'hidden_invisible_string' => 'hiddenInvisibleString',
  182. 'hidden_status_string' => 'hiddenStatusString',
  183. 'classes_js_array' => 'availableClassesJsArray',
  184. 'is_container' => 'classIsContainer' ),
  185. "increment_key" => "node_id",
  186. "class_name" => "eZContentObjectTreeNode",
  187. "name" => "ezcontentobject_tree" );
  188. return $definition;
  189. }
  190. /*!
  191. Creates a new tree node and returns it.
  192. \param $parentNodeID The ID of the parent or \c null if the node is not known yet.
  193. \param $contentObjectID The ID of the object it points to or \c null if it is not known yet.
  194. \param $contentObjectVersion The version of the object or \c 0 if not known yet.
  195. \param $sortField Number describing the field to sort by, or \c 0 if not known yet.
  196. \param $sortOrder Which way to sort, \c true means ascending while \c false is descending.
  197. \note The attribute \c remote_id will get an automatic and unique value.
  198. */
  199. static function create( $parentNodeID = null, $contentObjectID = null, $contentObjectVersion = 0,
  200. $sortField = 0, $sortOrder = true )
  201. {
  202. $row = array( 'node_id' => null,
  203. 'main_node_id' => null,
  204. 'parent_node_id' => $parentNodeID,
  205. 'contentobject_id' => $contentObjectID,
  206. 'contentobject_version' => $contentObjectVersion,
  207. 'contentobject_is_published' => false,
  208. 'depth' => 1,
  209. 'path_string' => null,
  210. 'path_identification_string' => null,
  211. 'is_hidden' => false,
  212. 'is_invisible' => false,
  213. 'sort_field' => $sortField,
  214. 'sort_order' => $sortOrder,
  215. 'modified_subnode' => 0,
  216. 'remote_id' => eZRemoteIdUtility::generate( 'node' ),
  217. 'priority' => 0 );
  218. $node = new eZContentObjectTreeNode( $row );
  219. return $node;
  220. }
  221. /*!
  222. \return a map with all the content object attributes where the keys are the
  223. attribute identifiers.
  224. \sa eZContentObject::fetchDataMap
  225. */
  226. function dataMap()
  227. {
  228. return $this->object()->fetchDataMap( $this->attribute( 'contentobject_version' ) );
  229. }
  230. /*!
  231. Get remote id of content node, the remote ID is often used to synchronise imports and exports.
  232. If there is no remote ID a new unique one will be generated.
  233. */
  234. function remoteID()
  235. {
  236. $remoteID = eZPersistentObject::attribute( 'remote_id', true );
  237. if ( !$remoteID )
  238. {
  239. $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'node' ) );
  240. $this->sync( array( 'remote_id' ) );
  241. $remoteID = eZPersistentObject::attribute( 'remote_id', true );
  242. }
  243. return $remoteID;
  244. }
  245. /*!
  246. \return true if this node is the main node.
  247. */
  248. function isMain()
  249. {
  250. return $this->NodeID == $this->MainNodeID;
  251. }
  252. /*!
  253. \return the ID of the class attribute with the given ID.
  254. False is returned if no class/attribute by that identifier is found.
  255. If multiple classes have the same identifier, the first found is returned.
  256. */
  257. static function classAttributeIDByIdentifier( $identifier )
  258. {
  259. return eZContentClassAttribute::classAttributeIDByIdentifier( $identifier );
  260. }
  261. /*!
  262. \return the ID of the class with the given ID.
  263. False is returned if no class by that identifier is found.
  264. If multiple classes have the same identifier, the first found is returned.
  265. */
  266. static function classIDByIdentifier( $identifier )
  267. {
  268. return eZContentClass::classIDByIdentifier( $identifier );
  269. }
  270. /*!
  271. \return \c true if the node can be read by the current user.
  272. \sa checkAccess().
  273. */
  274. function canRead( )
  275. {
  276. if ( !isset( $this->Permissions["can_read"] ) )
  277. {
  278. $this->Permissions["can_read"] = $this->checkAccess( 'read' );
  279. }
  280. return ( $this->Permissions["can_read"] == 1 );
  281. }
  282. /*!
  283. \return \c true if the current user can create a pdf of this content object.
  284. */
  285. function canPdf( )
  286. {
  287. if ( !isset( $this->Permissions["can_pdf"] ) )
  288. {
  289. $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' );
  290. }
  291. return ( $this->Permissions["can_pdf"] == 1 );
  292. }
  293. /*!
  294. \return \c true if the node can be viewed as embeded object by the current user.
  295. \sa checkAccess().
  296. */
  297. function canViewEmbed( )
  298. {
  299. if ( !isset( $this->Permissions["can_view_embed"] ) )
  300. {
  301. $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' );
  302. }
  303. return ( $this->Permissions["can_view_embed"] == 1 );
  304. }
  305. /*!
  306. \return \c true if the node can be edited by the current user.
  307. \sa checkAccess().
  308. */
  309. function canEdit( )
  310. {
  311. if ( !isset( $this->Permissions["can_edit"] ) )
  312. {
  313. $this->Permissions["can_edit"] = $this->checkAccess( 'edit' );
  314. if ( $this->Permissions["can_edit"] != 1 )
  315. {
  316. $user = eZUser::currentUser();
  317. if ( $user->id() == $this->ContentObject->attribute( 'id' ) )
  318. {
  319. $access = $user->hasAccessTo( 'user', 'selfedit' );
  320. if ( $access['accessWord'] == 'yes' )
  321. {
  322. $this->Permissions["can_edit"] = 1;
  323. }
  324. }
  325. }
  326. }
  327. return ( $this->Permissions["can_edit"] == 1 );
  328. }
  329. /*!
  330. \return \c true if the node can be hidden by the current user.
  331. \sa checkAccess().
  332. */
  333. function canHide( )
  334. {
  335. if ( !isset( $this->Permissions["can_hide"] ) )
  336. {
  337. $this->Permissions["can_hide"] = $this->checkAccess( 'hide' );
  338. }
  339. return ( $this->Permissions["can_hide"] == 1 );
  340. }
  341. /*!
  342. \return \c true if the current user can create a new node as child of this node.
  343. \sa checkAccess().
  344. */
  345. function canCreate( )
  346. {
  347. if ( !isset( $this->Permissions["can_create"] ) )
  348. {
  349. $this->Permissions["can_create"] = $this->checkAccess( 'create' );
  350. }
  351. return ( $this->Permissions["can_create"] == 1 );
  352. }
  353. /*!
  354. \return \c true if the node can be removed by the current user.
  355. \sa checkAccess().
  356. */
  357. function canRemove( )
  358. {
  359. if ( !isset( $this->Permissions["can_remove"] ) )
  360. {
  361. $this->Permissions["can_remove"] = $this->checkAccess( 'remove' );
  362. }
  363. return ( $this->Permissions["can_remove"] == 1 );
  364. }
  365. /*!
  366. Check if the node can be moved. (actually checks 'edit' and 'remove' permissions)
  367. \return \c true if the node can be moved by the current user.
  368. \sa checkAccess().
  369. \deprecated The function canMove() is preferred since its naming is clearer.
  370. */
  371. function canMove()
  372. {
  373. return $this->canMoveFrom();
  374. }
  375. /*!
  376. Check if the node can be moved. (actually checks 'edit' and 'remove' permissions)
  377. \return \c true if the node can be moved by the current user.
  378. \sa checkAccess().
  379. */
  380. function canMoveFrom( )
  381. {
  382. if ( !isset( $this->Permissions['can_move_from'] ) )
  383. {
  384. $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' );
  385. }
  386. return ( $this->Permissions['can_move_from'] == 1 );
  387. }
  388. /*!
  389. \return \c true if a node of class \a $classID can be moved to the current node by the current user.
  390. \sa checkAccess().
  391. */
  392. function canMoveTo( $classID = false )
  393. {
  394. if ( !isset( $this->Permissions['can_move_to'] ) )
  395. {
  396. $this->Permissions['can_move_to'] = $this->checkAccess( 'create', $classID );
  397. }
  398. return ( $this->Permissions['can_move_to'] == 1 );
  399. }
  400. /*!
  401. \return \c true if a node can be swaped by the current user.
  402. \sa checkAccess().
  403. */
  404. function canSwap()
  405. {
  406. if ( !isset( $this->Permissions['can_swap'] ) )
  407. {
  408. $this->Permissions['can_swap'] = $this->checkAccess( 'edit' );
  409. }
  410. return ( $this->Permissions['can_swap'] == 1 );
  411. }
  412. /*!
  413. \return \c true if current user can add object locations to current node.
  414. \sa checkAccess()
  415. */
  416. function canAddLocation()
  417. {
  418. if ( !isset( $this->Permissions['can_add_location'] ) )
  419. {
  420. $this->Permissions['can_add_location'] = $this->checkAccess( 'can_add_location' );
  421. }
  422. return ( $this->Permissions['can_add_location'] == 1 );
  423. }
  424. /*!
  425. \return \c true if current user can add object locations to current node.
  426. */
  427. function canRemoveLocation()
  428. {
  429. if ( !isset( $this->Permissions['can_remove_location'] ) )
  430. {
  431. $this->Permissions['can_remove_location'] = $this->checkAccess( 'can_remove_location' );
  432. }
  433. return ( $this->Permissions['can_remove_location'] == 1 );
  434. }
  435. /*!
  436. \static
  437. \returns the sort key for the given classAttributeID.
  438. int|string is returend. False is returned if unsuccessful.
  439. */
  440. static function sortKeyByClassAttributeID( $classAttributeID )
  441. {
  442. return eZContentClassAttribute::sortKeyTypeByID( $classAttributeID );
  443. }
  444. /*!
  445. \static
  446. */
  447. static function dataTypeByClassAttributeID( $classAttributeID )
  448. {
  449. return eZContentClassAttribute::dataTypeByID( $classAttributeID );
  450. }
  451. /*!
  452. Fetches the number of nodes which exists in the system.
  453. */
  454. static function fetchListCount()
  455. {
  456. $sql = "SELECT count( node_id ) as count FROM ezcontentobject_tree";
  457. $db = eZDB::instance();
  458. $rows = $db->arrayQuery( $sql );
  459. return $rows[0]['count'];
  460. }
  461. /*!
  462. Fetches a list of nodes and returns it. Offset and limitation can be set if needed.
  463. */
  464. static function fetchList( $asObject = true, $offset = false, $limit = false )
  465. {
  466. $sql = "SELECT * FROM ezcontentobject_tree";
  467. $parameters = array();
  468. if ( $offset !== false )
  469. $parameters['offset'] = $offset;
  470. if ( $limit !== false )
  471. $parameters['limit'] = $limit;
  472. $db = eZDB::instance();
  473. $rows = $db->arrayQuery( $sql, $parameters );
  474. $nodes = array();
  475. if ( $asObject )
  476. {
  477. foreach ( $rows as $row )
  478. {
  479. $nodes[] = new eZContentObjectTreeNode( $row );
  480. }
  481. return $nodes;
  482. }
  483. else
  484. return $rows;
  485. }
  486. /*!
  487. \a static
  488. */
  489. static function createSortingSQLStrings( $sortList, $treeTableName = 'ezcontentobject_tree', $allowCustomColumns = false )
  490. {
  491. $sortingInfo = array( 'sortCount' => 0,
  492. 'sortingFields' => " path_string ASC",
  493. 'attributeJoinCount' => 0,
  494. 'attributeFromSQL' => "",
  495. 'attributeTargetSQL' => "",
  496. 'attributeWhereSQL' => "" );
  497. if ( $sortList and is_array( $sortList ) and count( $sortList ) > 0 )
  498. {
  499. if ( count( $sortList ) > 1 and !is_array( $sortList[0] ) )
  500. {
  501. $sortList = array( $sortList );
  502. }
  503. $sortingFields = '';
  504. $sortCount = 0;
  505. $attributeJoinCount = 0;
  506. $stateJoinCount = 0;
  507. $attributeFromSQL = "";
  508. $attributeWhereSQL = "";
  509. $datatypeSortingTargetSQL = "";
  510. foreach ( $sortList as $sortBy )
  511. {
  512. if ( is_array( $sortBy ) and count( $sortBy ) > 0 )
  513. {
  514. if ( $sortCount > 0 )
  515. {
  516. $sortingFields .= ', ';
  517. }
  518. $sortField = $sortBy[0];
  519. switch ( $sortField )
  520. {
  521. case 'path':
  522. {
  523. $sortingFields .= 'path_string';
  524. } break;
  525. case 'path_string':
  526. {
  527. $sortingFields .= 'path_identification_string';
  528. } break;
  529. case 'published':
  530. {
  531. $sortingFields .= 'ezcontentobject.published';
  532. } break;
  533. case 'modified':
  534. {
  535. $sortingFields .= 'ezcontentobject.modified';
  536. } break;
  537. case 'modified_subnode':
  538. {
  539. $sortingFields .= 'modified_subnode';
  540. } break;
  541. case 'section':
  542. {
  543. $sortingFields .= 'ezcontentobject.section_id';
  544. } break;
  545. case 'node_id':
  546. {
  547. $sortingFields .= $treeTableName . '.node_id';
  548. } break;
  549. case 'contentobject_id':
  550. {
  551. $sortingFields .= $treeTableName . '.contentobject_id';
  552. } break;
  553. case 'depth':
  554. {
  555. $sortingFields .= 'depth';
  556. } break;
  557. case 'class_identifier':
  558. {
  559. $sortingFields .= 'ezcontentclass.identifier';
  560. } break;
  561. case 'class_name':
  562. {
  563. $classNameFilter = eZContentClassName::sqlFilter();
  564. $sortingFields .= 'contentclass_name';
  565. $datatypeSortingTargetSQL .= ", $classNameFilter[nameField] AS contentclass_name";
  566. $attributeFromSQL .= ", $classNameFilter[from]";
  567. $attributeWhereSQL .= "$classNameFilter[where] AND ";
  568. } break;
  569. case 'priority':
  570. {
  571. $sortingFields .= $treeTableName . '.priority';
  572. } break;
  573. case 'name':
  574. {
  575. $sortingFields .= 'ezcontentobject_name.name';
  576. } break;
  577. case 'attribute':
  578. {
  579. $classAttributeID = $sortBy[2];
  580. if ( !is_numeric( $classAttributeID ) )
  581. $classAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $classAttributeID );
  582. $contentAttributeTableAlias = "a$attributeJoinCount";
  583. $datatypeFromSQL = "ezcontentobject_attribute $contentAttributeTableAlias";
  584. $datatypeWhereSQL = "
  585. $contentAttributeTableAlias.contentobject_id = ezcontentobject.id AND
  586. $contentAttributeTableAlias.contentclassattribute_id = $classAttributeID AND
  587. $contentAttributeTableAlias.version = ezcontentobject_name.content_version AND";
  588. $datatypeWhereSQL .= eZContentLanguage::sqlFilter( $contentAttributeTableAlias, 'ezcontentobject' );
  589. $dataType = eZDataType::create( eZContentObjectTreeNode::dataTypeByClassAttributeID( $classAttributeID ) );
  590. if( is_object( $dataType ) && $dataType->customSorting() )
  591. {
  592. $params = array();
  593. $params['contentobject_attr_id'] = "$contentAttributeTableAlias.id";
  594. $params['contentobject_attr_version'] = "$contentAttributeTableAlias.version";
  595. $params['table_alias_suffix'] = "$attributeJoinCount";
  596. $sql = $dataType->customSortingSQL( $params );
  597. $datatypeFromSQL .= ", {$sql['from']}";
  598. $datatypeWhereSQL .= " AND {$sql['where']}";
  599. $datatypeSortingFieldSQL = $sql['sorting_field'];
  600. $datatypeSortingTargetSQL .= ', ' . $sql['sorting_field'];
  601. }
  602. else
  603. {
  604. // Look up datatype for standard sorting
  605. $sortKeyType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $classAttributeID );
  606. switch ( $sortKeyType )
  607. {
  608. case 'string':
  609. {
  610. $sortKey = 'sort_key_string';
  611. } break;
  612. case 'int':
  613. default:
  614. {
  615. $sortKey = 'sort_key_int';
  616. } break;
  617. }
  618. $datatypeSortingFieldSQL = "a$attributeJoinCount.$sortKey";
  619. $datatypeSortingTargetSQL .= ', ' . $datatypeSortingFieldSQL;
  620. }
  621. $sortingFields .= "$datatypeSortingFieldSQL";
  622. $attributeFromSQL .= ", $datatypeFromSQL";
  623. $attributeWhereSQL .= "$datatypeWhereSQL AND ";
  624. $attributeJoinCount++;
  625. }break;
  626. case 'state':
  627. {
  628. $stateGroupID = $sortBy[2];
  629. if ( !is_numeric( $stateGroupID ) )
  630. {
  631. $stateGroup = eZContentObjectStateGroup::fetchByIdentifier( $stateGroupID );
  632. if ( $stateGroup )
  633. {
  634. $stateGroupID = $stateGroup->attribute( 'id' );
  635. }
  636. else
  637. {
  638. eZDebug::writeError( "Unknown content object state group '$stateGroupID'" );
  639. continue 2;
  640. }
  641. }
  642. $stateAlias = "s$stateJoinCount";
  643. $stateLinkAlias = "sl$stateJoinCount";
  644. $sortingFields .= "$stateAlias.priority";
  645. $datatypeSortingTargetSQL .= ", $stateAlias.priority";
  646. $attributeFromSQL .= ", ezcobj_state $stateAlias, ezcobj_state_link $stateLinkAlias";
  647. $attributeWhereSQL .= "$stateLinkAlias.contentobject_id=$treeTableName.contentobject_id AND
  648. $stateLinkAlias.contentobject_state_id=$stateAlias.id AND
  649. $stateAlias.group_id=$stateGroupID AND ";
  650. } break;
  651. default:
  652. {
  653. if ( $allowCustomColumns )
  654. {
  655. $sortingFields .= $sortField;
  656. }
  657. else
  658. {
  659. eZDebug::writeWarning( 'Unknown sort field: ' . $sortField, __METHOD__ );
  660. continue;
  661. }
  662. }
  663. }
  664. $sortOrder = true; // true is ascending
  665. if ( isset( $sortBy[1] ) )
  666. $sortOrder = $sortBy[1];
  667. $sortingFields .= $sortOrder ? " ASC" : " DESC";
  668. ++$sortCount;
  669. }
  670. }
  671. $sortingInfo['sortCount'] = $sortCount;
  672. $sortingInfo['sortingFields'] = $sortingFields;
  673. $sortingInfo['attributeTargetSQL'] = $datatypeSortingTargetSQL;
  674. $sortingInfo['attributeJoinCount'] = $attributeJoinCount;
  675. $sortingInfo['attributeFromSQL'] = $attributeFromSQL;
  676. $sortingInfo['attributeWhereSQL'] = $attributeWhereSQL;
  677. }
  678. return $sortingInfo;
  679. }
  680. /*!
  681. \a static
  682. */
  683. static function createClassFilteringSQLString( $classFilterType, &$classFilterArray )
  684. {
  685. // Check for class filtering
  686. $classCondition = '';
  687. if ( isset( $classFilterType ) &&
  688. ( $classFilterType == 'include' || $classFilterType == 'exclude' ) &&
  689. count( $classFilterArray ) > 0 )
  690. {
  691. $classCondition = ' ';
  692. $i = 0;
  693. $classCount = count( $classFilterArray );
  694. $classIDArray = array();
  695. foreach ( $classFilterArray as $classID )
  696. {
  697. $originalClassID = $classID;
  698. // Check if classes are recerenced by identifier
  699. if ( is_string( $classID ) && !is_numeric( $classID ) )
  700. {
  701. $classID = eZContentClass::classIDByIdentifier( $classID );
  702. }
  703. if ( is_numeric( $classID ) )
  704. {
  705. $classIDArray[] = $classID;
  706. }
  707. else
  708. {
  709. eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
  710. }
  711. }
  712. if ( count( $classIDArray ) > 0 )
  713. {
  714. $classCondition .= " ezcontentobject.contentclass_id ";
  715. if ( $classFilterType == 'include' )
  716. $classCondition .= " IN ";
  717. else
  718. $classCondition .= " NOT IN ";
  719. $classIDString = implode( ', ', $classIDArray );
  720. $classCondition .= ' ( ' . $classIDString . ' ) AND';
  721. }
  722. else
  723. {
  724. if ( count( $classIDArray ) == 0 and count( $classFilterArray ) > 0 and $classFilterType == 'include' )
  725. {
  726. $classCondition = false;
  727. }
  728. }
  729. }
  730. return $classCondition;
  731. }
  732. /*!
  733. \a static
  734. */
  735. static function createExtendedAttributeFilterSQLStrings( &$extendedAttributeFilter )
  736. {
  737. $filter = array( 'tables' => '',
  738. 'joins' => '',
  739. 'columns' => '',
  740. 'group_by' => '' );
  741. if ( $extendedAttributeFilter and count( $extendedAttributeFilter ) > 1 )
  742. {
  743. $extendedAttributeFilterID = $extendedAttributeFilter['id'];
  744. $extendedAttributeFilterParams = $extendedAttributeFilter['params'];
  745. $filterINI = eZINI::instance( 'extendedattributefilter.ini' );
  746. if ( !$filterINI->hasGroup( $extendedAttributeFilterID ) )
  747. {
  748. eZDebug::writeError( "Unable to find configuration for the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
  749. return $filter;
  750. }
  751. $filterClassName = $filterINI->variable( $extendedAttributeFilterID, 'ClassName' );
  752. $filterMethodName = $filterINI->variable( $extendedAttributeFilterID, 'MethodName' );
  753. if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'FileName' ) )
  754. {
  755. $filterFile = $filterINI->variable( $extendedAttributeFilterID, 'FileName' );
  756. if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'ExtensionName' ) )
  757. {
  758. $extensionName = $filterINI->variable( $extendedAttributeFilterID, 'ExtensionName' );
  759. include_once( eZExtension::baseDirectory() . "/$extensionName/$filterFile" );
  760. }
  761. else
  762. {
  763. include_once( $filterFile );
  764. }
  765. }
  766. if ( !class_exists( $filterClassName, true ) )
  767. {
  768. eZDebug::writeError( "Unable to find the PHP class '$filterClassName' associated with the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
  769. return $filter;
  770. }
  771. $classObject = new $filterClassName();
  772. $parameterArray = array( $extendedAttributeFilterParams );
  773. $sqlResult = call_user_func_array( array( $classObject, $filterMethodName ), $parameterArray );
  774. $filter['tables'] = $sqlResult['tables'];
  775. $filter['joins'] = $sqlResult['joins'];
  776. $filter['columns'] = isset( $sqlResult['columns'] ) ? $sqlResult['columns'] : '';
  777. if( isset( $sqlResult['group_by'] ) )
  778. $filter['group_by'] = $sqlResult['group_by'];
  779. }
  780. return $filter;
  781. }
  782. /*!
  783. \a static
  784. */
  785. static function createMainNodeConditionSQLString( $mainNodeOnly )
  786. {
  787. // Main node check
  788. $mainNodeCondition = '';
  789. if ( isset( $mainNodeOnly ) && $mainNodeOnly === true )
  790. {
  791. $mainNodeCondition = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
  792. }
  793. return $mainNodeCondition;
  794. }
  795. /*!
  796. \a static
  797. */
  798. static function createObjectNameFilterConditionSQLString( $filter )
  799. {
  800. if ( !$filter )
  801. return '';
  802. $db = eZDB::instance();
  803. if ( $filter == 'others' )
  804. {
  805. $alphabet = eZAlphabetOperator::fetchAlphabet();
  806. $sql = '';
  807. foreach ( $alphabet as $letter )
  808. {
  809. $sql .= " AND ezcontentobject.name NOT LIKE '". $db->escapeString( $letter ) . "%' ";
  810. }
  811. return $sql;
  812. }
  813. $objectNameFilterSQL = " AND ezcontentobject.name LIKE '" . $db->escapeString( $filter ) ."%'";
  814. return $objectNameFilterSQL;
  815. }
  816. /*!
  817. \a static
  818. */
  819. static function createAttributeFilterSQLStrings( &$attributeFilter, &$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 ) )
  820. {
  821. // Check for attribute filtering
  822. $filterSQL = array( 'from' => '',
  823. 'where' => '' );
  824. $totalAttributesFiltersCount = 0;
  825. $invalidAttributesFiltersCount = 0;
  826. if ( isset( $attributeFilter ) && $attributeFilter !== false )
  827. {
  828. if ( !is_array( $attributeFilter ) )
  829. {
  830. eZDebug::writeError( "\$attributeFilter needs to be an array", __METHOD__ );
  831. return $filterSQL;
  832. }
  833. $filterArray = $attributeFilter;
  834. // Check if first value of array is a string.
  835. // To check for and/or filtering
  836. $filterJoinType = 'AND';
  837. if ( is_string( $filterArray[0] ) )
  838. {
  839. if ( strtolower( $filterArray[0] ) == 'or' )
  840. {
  841. $filterJoinType = 'OR';
  842. }
  843. else if ( strtolower( $filterArray[0] ) == 'and' )
  844. {
  845. $filterJoinType = 'AND';
  846. }
  847. unset( $filterArray[0] );
  848. }
  849. $attibuteFilterJoinSQL = "";
  850. $filterCount = $sortingInfo['sortCount'];
  851. $justFilterCount = 0;
  852. $db = eZDB::instance();
  853. if ( is_array( $filterArray ) )
  854. {
  855. // Handle attribute filters and generate SQL
  856. $totalAttributesFiltersCount = count( $filterArray );
  857. foreach ( $filterArray as $filter )
  858. {
  859. $isFilterValid = true; // by default assumes that filter is valid
  860. $filterAttributeID = $filter[0];
  861. $filterType = $filter[1];
  862. $filterValue = is_array( $filter[2] ) ? '' : $db->escapeString( $filter[2] );
  863. $useAttributeFilter = false;
  864. switch ( $filterAttributeID )
  865. {
  866. case 'path':
  867. {
  868. $filterField = 'path_string';
  869. } break;
  870. case 'published':
  871. {
  872. $filterField = 'ezcontentobject.published';
  873. } break;
  874. case 'modified':
  875. {
  876. $filterField = 'ezcontentobject.modified';
  877. } break;
  878. case 'modified_subnode':
  879. {
  880. $filterField = 'modified_subnode';
  881. } break;
  882. case 'node_id':
  883. {
  884. $filterField = 'ezcontentobject_tree.node_id';
  885. } break;
  886. case 'contentobject_id':
  887. {
  888. $filterField = 'ezcontentobject_tree.contentobject_id';
  889. } break;
  890. case 'section':
  891. {
  892. $filterField = 'ezcontentobject.section_id';
  893. } break;
  894. case 'state':
  895. {
  896. // state only supports =, !=, in, and not_in
  897. // other operators do not make any sense in this context
  898. $hasFilterOperator = true;
  899. switch ( $filterType )
  900. {
  901. case '=' :
  902. case '!=':
  903. {
  904. $subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2];
  905. $filterOperator = ( $filterType == '=' ? 'IN' : 'NOT IN' );
  906. } break;
  907. case 'in':
  908. case 'not_in' :
  909. {
  910. if ( is_array( $filter[2] ) )
  911. {
  912. $subQueryCondition = $db->generateSQLINStatement( $filter[2], 'contentobject_state_id', false, false, 'int' );
  913. $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
  914. }
  915. else
  916. {
  917. $hasFilterOperator = false;
  918. }
  919. } break;
  920. default :
  921. {
  922. $hasFilterOperator = false;
  923. eZDebug::writeError( "Unknown attribute filter type for state: $filterType", __METHOD__ );
  924. } break;
  925. }
  926. if ( $hasFilterOperator )
  927. {
  928. if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
  929. $attibuteFilterJoinSQL .= " $filterJoinType ";
  930. $attibuteFilterJoinSQL .= "ezcontentobject.id $filterOperator (SELECT contentobject_id FROM ezcobj_state_link WHERE $subQueryCondition)";
  931. $filterCount++;
  932. $justFilterCount++;
  933. }
  934. continue 2;
  935. } break;
  936. case 'depth':
  937. {
  938. $filterField = 'depth';
  939. } break;
  940. case 'class_identifier':
  941. {
  942. $filterField = 'ezcontentclass.identifier';
  943. } break;
  944. case 'class_name':
  945. {
  946. $classNameFilter = eZContentClassName::sqlFilter();
  947. $filterField = $classNameFilter['nameField'];
  948. $filterSQL['from'] .= ", $classNameFilter[from]";
  949. $filterSQL['where'] .= "$classNameFilter[where] AND ";
  950. } break;
  951. case 'priority':
  952. {
  953. $filterField = 'ezcontentobject_tree.priority';
  954. } break;
  955. case 'name':
  956. {
  957. $filterField = 'ezcontentobject_name.name';
  958. } break;
  959. case 'owner':
  960. {
  961. $filterField = 'ezcontentobject.owner_id';
  962. } break;
  963. case 'visibility':
  964. {
  965. $filterValue = ( $filterValue == '1' ) ? 0 : 1;
  966. $filterField = 'ezcontentobject_tree.is_invisible';
  967. } break;
  968. default:
  969. {
  970. $useAttributeFilter = true;
  971. } break;
  972. }
  973. if ( $useAttributeFilter )
  974. {
  975. if ( !is_numeric( $filterAttributeID ) )
  976. $filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $filterAttributeID );
  977. if ( $filterAttributeID === false )
  978. {
  979. $isFilterValid = false;
  980. if( $filterJoinType === 'AND' )
  981. {
  982. // go out
  983. $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
  984. break;
  985. }
  986. ++$invalidAttributesFiltersCount;
  987. }
  988. else
  989. {
  990. // Check datatype for filtering
  991. $filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $filterAttributeID );
  992. if ( $filterDataType === false )
  993. {
  994. $isFilterValid = false;
  995. if( $filterJoinType === 'AND' )
  996. {
  997. // go out
  998. $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
  999. break;
  1000. }
  1001. // check next filter
  1002. ++$invalidAttributesFiltersCount;
  1003. }
  1004. else
  1005. {
  1006. $sortKey = false;
  1007. if ( $filterDataType == 'string' )
  1008. {
  1009. $sortKey = 'sort_key_string';
  1010. }
  1011. else
  1012. {
  1013. $sortKey = 'sort_key_int';
  1014. }
  1015. $filterField = "a$filterCount.$sortKey";
  1016. // Use the same joins as we do when sorting,
  1017. // if more attributes are filtered by we will append them
  1018. if ( $filterCount >= $sortingInfo['attributeJoinCount'] )
  1019. {
  1020. $filterSQL['from'] .= ", ezcontentobject_attribute a$filterCount ";
  1021. }
  1022. $filterSQL['where'] .= "
  1023. a$filterCount.contentobject_id = ezcontentobject.id AND
  1024. a$filterCount.contentclassattribute_id = $filterAttributeID AND
  1025. a$filterCount.version = ezcontentobject_name.content_version AND ";
  1026. $filterSQL['where'] .= eZContentLanguage::sqlFilter( "a$filterCount", 'ezcontentobject' ). ' AND ';
  1027. }
  1028. }
  1029. }
  1030. if ( $isFilterValid )
  1031. {
  1032. $hasFilterOperator = true;
  1033. // Controls quotes around filter value, some filters do this manually
  1034. $noQuotes = false;
  1035. // Controls if $filterValue or $filter[2] is used, $filterValue is already escaped
  1036. $unEscape = false;
  1037. switch ( $filterType )
  1038. {
  1039. case '=' :
  1040. {
  1041. $filterOperator = '=';
  1042. }break;
  1043. case '!=' :
  1044. {
  1045. $filterOperator = '<>';
  1046. }break;
  1047. case '>' :
  1048. {
  1049. $filterOperator = '>';
  1050. }break;
  1051. case '<' :
  1052. {
  1053. $filterOperator = '<';
  1054. }break;
  1055. case '<=' :
  1056. {
  1057. $filterOperator = '<=';
  1058. }break;
  1059. case '>=' :
  1060. {
  1061. $filterOperator = '>=';
  1062. }break;
  1063. case 'like':
  1064. case 'not_like':
  1065. {
  1066. $filterOperator = ( $filterType == 'like' ? 'LIKE' : 'NOT LIKE' );
  1067. // We escape the string ourselves, this MUST be done before wildcard replace
  1068. $filter[2] = $db->escapeString( $filter[2] );
  1069. $unEscape = true;
  1070. // Since * is used as wildcard we need to transform the string to
  1071. // use % as wildcard. The following rules apply:
  1072. // - % -> \%
  1073. // - * -> %
  1074. // - \* -> *
  1075. // - \\ -> \
  1076. $filter[2] = preg_replace( array( '#%#m',
  1077. '#(?<!\\\\)\\*#m',
  1078. '#(?<!\\\\)\\\\\\*#m',
  1079. '#\\\\\\\\#m' ),
  1080. array( '\\%',
  1081. '%',
  1082. '*',
  1083. '\\\\' ),
  1084. $filter[2] );
  1085. } break;
  1086. case 'in':
  1087. case 'not_in' :
  1088. {
  1089. $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
  1090. // Turn off quotes for value, we do this ourselves
  1091. $noQuotes = true;
  1092. if ( is_array( $filter[2] ) )
  1093. {
  1094. reset( $filter[2] );
  1095. while ( list( $key, $value ) = each( $filter[2] ) )
  1096. {
  1097. // Non-numerics must be escaped to avoid SQL injection
  1098. $filter[2][$key] = is_numeric( $value ) ? $value : "'" . $db->escapeString( $value ) . "'";
  1099. }
  1100. $filterValue = '(' . implode( ",", $filter[2] ) . ')';
  1101. }
  1102. else
  1103. {
  1104. $hasFilterOperator = false;
  1105. }
  1106. } break;
  1107. case 'between':
  1108. case 'not_between' :
  1109. {
  1110. $filterOperator = ( $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN' );
  1111. // Turn off quotes for value, we do this ourselves
  1112. $noQuotes = true;
  1113. if ( is_array( $filter[2] ) )
  1114. {
  1115. // Check for non-numerics to avoid SQL injection
  1116. if ( !is_numeric( $filter[2][0] ) )
  1117. $filter[2][0] = "'" . $db->escapeString( $filter[2][0] ) . "'";
  1118. if ( !is_numeric( $filter[2][1] ) )
  1119. $filter[2][1] = "'" . $db->escapeString( $filter[2][1] ) . "'";
  1120. $filterValue = $filter[2][0] . ' AND ' . $filter[2][1];
  1121. }
  1122. } break;
  1123. default :
  1124. {
  1125. $hasFilterOperator = false;
  1126. eZDebug::writeError( "Unknown attribute filter type: $filterType", __METHOD__ );
  1127. }break;
  1128. }
  1129. if ( $hasFilterOperator )
  1130. {
  1131. if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
  1132. $attibuteFilterJoinSQL .= " $filterJoinType ";
  1133. // If $unEscape is true we get the filter value from the 2nd element instead
  1134. // which must have been escaped by filter type
  1135. $filterValue = $unEscape ? $filter[2] : $filterValue;
  1136. $attibuteFilterJoinSQL .= "$filterField $filterOperator ";
  1137. $attibuteFilterJoinSQL .= $noQuotes ? "$filterValue " : "'$filterValue' ";
  1138. $filterCount++;
  1139. $justFilterCount++;
  1140. }
  1141. }
  1142. } // end of 'foreach ( $filterArray as $filter )'
  1143. if ( $totalAttributesFiltersCount == $invalidAttributesFiltersCount )
  1144. {
  1145. eZDebug::writeNotice( "Attribute filter returned false" );
  1146. $filterSQL = false;
  1147. }
  1148. else
  1149. {
  1150. if ( $justFilterCount > 0 )
  1151. $filterSQL['where'] .= " ( " . $attibuteFilterJoinSQL . " ) AND ";
  1152. }
  1153. } // end of 'if ( is_array( $filterArray ) )'
  1154. }
  1155. return $filterSQL;
  1156. }
  1157. /*!
  1158. \a static
  1159. */
  1160. static function createNotEqParentSQLString( $nodeID, $depth = false, $depthOperator = 'le' )
  1161. {
  1162. $notEqParentString = '';
  1163. if( !$depth || $depthOperator == 'le' || $depthOperator == 'lt' )
  1164. {
  1165. $notEqParentString = "ezcontentobject_tree.node_id != $nodeID AND";
  1166. }
  1167. return $notEqParentString;
  1168. }
  1169. /*!
  1170. \a static
  1171. */
  1172. static function createPathConditionSQLString( $nodePath, $nodeDepth, $depth = false, $depthOperator = 'le' )
  1173. {
  1174. $pathCondition = '';
  1175. $depthCondition = '';
  1176. if ( $depth )
  1177. {
  1178. $sqlDepthOperator = '<=';
  1179. if ( $depthOperator )
  1180. {
  1181. if ( $depthOperator == 'lt' )
  1182. {
  1183. $sqlDepthOperator = '<';
  1184. }
  1185. else if ( $depthOperator == 'gt' )
  1186. {
  1187. $sqlDepthOperator = '>';
  1188. }
  1189. else if ( $depthOperator == 'le' )
  1190. {
  1191. $sqlDepthOperator = '<=';
  1192. }
  1193. else if ( $depthOperator == 'ge' )
  1194. {
  1195. $sqlDepthOperator = '>=';
  1196. }
  1197. else if ( $depthOperator == 'eq' )
  1198. {
  1199. $sqlDepthOperator = '=';
  1200. }
  1201. }
  1202. $nodeDepth += $depth;
  1203. $depthCondition = ' ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' and ';
  1204. }
  1205. $pathCondition = " ezcontentobject_tree.path_string like '$nodePath%' and $depthCondition ";
  1206. return $pathCondition;
  1207. }
  1208. /*!
  1209. \a static
  1210. */
  1211. static function createPathConditionAndNotEqParentSQLStrings( &$outPathConditionStr, &$outNotEqParentStr, $nodeID, $depth = false, $depthOperator = 'le' )
  1212. {
  1213. if ( !$depthOperator )
  1214. {
  1215. $depthOperator = 'le';
  1216. }
  1217. // check if we are only fetching children
  1218. // - depth (lower than or) eqaul to 1
  1219. // - depth lower than 2 = depth equal to 1
  1220. $onlyChildren = ( $depth === 1 && ( $depthOperator === 'le' || $depthOperator === 'eq' ) ) ||
  1221. ( $depth === 2 && $depthOperator === 'lt' );
  1222. if ( is_array( $nodeID ) && count( $nodeID ) == 1 )
  1223. {
  1224. $nodeID = $nodeID[0];
  1225. }
  1226. if ( is_array( $nodeID ) )
  1227. {
  1228. $outNotEqParentStr = '';
  1229. // a parent_node_id condition suffits when only fetching children
  1230. if ( $onlyChildren )
  1231. {
  1232. $db = eZDB::instance();
  1233. $outPathConditionStr = $db->generateSQLINStatement( $nodeID, 'ezcontentobject_tree.parent_node_id', false, true, 'int' ) . ' and';
  1234. }
  1235. else
  1236. {
  1237. $nodeIDList = $nodeID;
  1238. $sqlPartForOneNodeList = array();
  1239. foreach ( $nodeIDList as $nodeID )
  1240. {
  1241. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  1242. if ( !is_array( $node ) )
  1243. return false;
  1244. $nodePath = $node['path_string'];
  1245. $nodeDepth = $node['depth'];
  1246. $depthCond = '';
  1247. if ( $depth )
  1248. {
  1249. $sqlDepthOperator = '<=';
  1250. if ( $depthOperator )
  1251. {
  1252. if ( $depthOperator == 'lt' )
  1253. {
  1254. $sqlDepthOperator = '<';
  1255. }
  1256. else if ( $depthOperator == 'gt' )
  1257. {
  1258. $sqlDepthOperator = '>';
  1259. }
  1260. else if ( $depthOperator == 'le' )
  1261. {
  1262. $sqlDepthOperator = '<=';
  1263. }
  1264. else if ( $depthOperator == 'ge' )
  1265. {
  1266. $sqlDepthOperator = '>=';
  1267. }
  1268. else if ( $depthOperator == 'eq' )
  1269. {
  1270. $sqlDepthOperator = '=';
  1271. }
  1272. }
  1273. $nodeDepth += $depth;
  1274. $depthCond = ' and ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' ';
  1275. }
  1276. $requireNotEqParentStr = !$depth || $depthOperator == 'le' || $depthOperator == 'lt';
  1277. $notEqParentStr = $requireNotEqParentStr ? " and ezcontentobject_tree.node_id != $nodeID " : '';
  1278. $sqlPartForOneNodeList[] = " ( ezcontentobject_tree.path_string like '$nodePath%' $depthCond $notEqParentStr ) ";
  1279. }
  1280. $outPathConditionStr = implode( ' or ', $sqlPartForOneNodeList );
  1281. $outPathConditionStr = ' (' . $outPathConditionStr . ') and';
  1282. }
  1283. }
  1284. else
  1285. {
  1286. if ( $nodeID == 0 )
  1287. {
  1288. return false;
  1289. }
  1290. // a parent_node_id condition suffits when only fetching children
  1291. if ( $onlyChildren )
  1292. {
  1293. $outNotEqParentStr = '';
  1294. $outPathConditionStr = 'ezcontentobject_tree.parent_node_id = ' . (int) $nodeID . ' and';
  1295. }
  1296. else
  1297. {
  1298. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  1299. if ( !is_array( $node ) )
  1300. return false;
  1301. $nodePath = $node['path_string'];
  1302. $nodeDepth = $node['depth'];
  1303. $outNotEqParentStr = eZContentObjectTreeNode::createNotEqParentSQLString( $nodeID, $depth, $depthOperator );
  1304. $outPathConditionStr = eZContentObjectTreeNode::createPathConditionSQLString( $nodePath, $nodeDepth, $depth, $depthOperator );
  1305. }
  1306. }
  1307. return true;
  1308. }
  1309. /*!
  1310. \a static
  1311. */
  1312. static function createGroupBySQLStrings( &$outGroupBySelectText, &$outGroupByText, $groupBy )
  1313. {
  1314. if ( $groupBy )
  1315. {
  1316. if ( isset( $groupBy['field'] ) and isset( $groupBy['type'] ) )
  1317. {
  1318. $groupByField = $groupBy['field'];
  1319. $groupByFieldType = $groupBy['type'];
  1320. switch ( $groupByField )
  1321. {
  1322. case 'published':
  1323. {
  1324. $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
  1325. $groupBySelect['field'] = "ezcontentobject." . $groupByField;
  1326. } break;
  1327. case 'modified':
  1328. {
  1329. $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
  1330. $groupBySelect['field'] = "ezcontentobject." . $groupByField;
  1331. } break;
  1332. }
  1333. $outGroupBySelectText = ", " . $groupBySelect['select'];
  1334. $outGroupByText = "GROUP BY " . $groupBySelect['group_field'];
  1335. }
  1336. }
  1337. }
  1338. /*!
  1339. \a static
  1340. */
  1341. static function createVersionNameTablesSQLString( $useVersionName )
  1342. {
  1343. $versionNameTables = '';
  1344. if ( $useVersionName )
  1345. {
  1346. $versionNameTables = ', ezcontentobject_name ';
  1347. }
  1348. return $versionNameTables;
  1349. }
  1350. /*!
  1351. \a static
  1352. */
  1353. static function createVersionNameTargetsSQLString( $useVersionName )
  1354. {
  1355. $versionNameTargets = '';
  1356. if ( $useVersionName )
  1357. {
  1358. $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation ';
  1359. }
  1360. return $versionNameTargets;
  1361. }
  1362. /*!
  1363. \a static
  1364. */
  1365. static function createVersionNameJoinsSQLString( $useVersionName, $includeAnd = true, $onlyTranslated = false, $lang = false, $treeTableName = 'ezcontentobject_tree' )
  1366. {
  1367. $versionNameJoins = '';
  1368. if ( $useVersionName )
  1369. {
  1370. if ( $includeAnd )
  1371. {
  1372. $versionNameJoins .= ' AND ';
  1373. }
  1374. $versionNameJoins .= " $treeTableName.contentobject_id = ezcontentobject_name.contentobject_id and
  1375. $treeTableName.contentobject_version = ezcontentobject_name.content_version and ";
  1376. $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
  1377. }
  1378. return $versionNameJoins;
  1379. }
  1380. /*!
  1381. \a static
  1382. Deprecated. Use 'createPermissionCheckingSQL' instead.
  1383. */
  1384. static function createPermissionCheckingSQLString( $limitationList )
  1385. {
  1386. $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
  1387. return $sqlPermissionChecking['where'];
  1388. }
  1389. /*!
  1390. \a static
  1391. */
  1392. static function createPermissionCheckingSQL( $limitationList, $treeTableName = 'ezcontentobject_tree', $tableAliasName = 'ezcontentobject_tree' )
  1393. {
  1394. $db = eZDB::instance();
  1395. $sqlPermissionCheckingFrom = '';
  1396. $sqlPermissionCheckingWhere = '';
  1397. $sqlPermissionTempTables = array();
  1398. $groupPermTempTable = false;
  1399. $createdStateAliases = array();
  1400. if ( is_array( $limitationList ) && count( $limitationList ) > 0 )
  1401. {
  1402. $sqlParts = array();
  1403. foreach( $limitationList as $limitationArray )
  1404. {
  1405. $sqlPartPart = array();
  1406. $sqlPartPartPart = array();
  1407. $sqlPlacementPart = array();
  1408. foreach ( array_keys( $limitationArray ) as $ident )
  1409. {
  1410. switch( $ident )
  1411. {
  1412. case 'Class':
  1413. {
  1414. $sqlPartPart[] = 'ezcontentobject.contentclass_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
  1415. } break;
  1416. case 'Section':
  1417. case 'User_Section':
  1418. {
  1419. $sqlPartPart[] = 'ezcontentobject.section_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
  1420. } break;
  1421. case 'Owner':
  1422. {
  1423. $user = eZUser::currentUser();
  1424. $userID = $user->attribute( 'contentobject_id' );
  1425. $sqlPartPart[] = "ezcontentobject.owner_id = '" . $db->escapeString( $userID ) . "'";
  1426. } break;
  1427. case 'Group':
  1428. {
  1429. if ( !$groupPermTempTable )
  1430. {
  1431. $user = eZUser::currentUser();
  1432. $userContentObject = $user->attribute( 'contentobject' );
  1433. $parentList = $userContentObject->attribute( 'parent_nodes' );
  1434. $groupPermTempTable = $db->generateUniqueTempTableName( 'ezgroup_perm_tmp_%' );
  1435. $sqlPermissionTempTables[] = $groupPermTempTable;
  1436. $db->createTempTable( "CREATE TEMPORARY TABLE $groupPermTempTable ( user_id int NOT NULL PRIMARY KEY )" );
  1437. $db->query( "INSERT INTO $groupPermTempTable
  1438. SELECT DISTINCT contentobject_id AS user_id
  1439. FROM ezcontentobject_tree
  1440. WHERE parent_node_id IN (" . implode( ', ', $parentList ) . ')',
  1441. eZDBInterface::SERVER_SLAVE );
  1442. $sqlPermissionCheckingFrom .= ', ' . $groupPermTempTable;
  1443. }
  1444. $sqlPartPart[] = "ezcontentobject.owner_id = $groupPermTempTable.user_id";
  1445. } break;
  1446. case 'Node':
  1447. {
  1448. $sqlPlacementPart[] = $tableAliasName . '.node_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
  1449. } break;
  1450. case 'Subtree':
  1451. {
  1452. $sqlSubtreePart = array();
  1453. foreach ( $limitationArray[$ident] as $limitationPathString )
  1454. {
  1455. $sqlSubtreePart[] = "$tableAliasName.path_string like '$limitationPathString%'";
  1456. }
  1457. $sqlPlacementPart[] = implode( ' OR ', $sqlSubtreePart );
  1458. } break;
  1459. case 'User_Subtree':
  1460. {
  1461. $sqlPartUserSubtree = array();
  1462. foreach ( $limitationArray[$ident] as $limitationPathString )
  1463. {
  1464. $sqlPartUserSubtree[] = "$tableAliasName.path_string like '$limitationPathString%'";
  1465. }
  1466. $sqlPartPart[] = implode( ' OR ', $sqlPartUserSubtree );
  1467. } break;
  1468. default:
  1469. {
  1470. if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 )
  1471. {
  1472. $stateIdentifier = substr( $ident, 11 );
  1473. if ( !isset( $createdStateAliases[$stateIdentifier] ) )
  1474. {
  1475. $stateIndex = count( $createdStateAliases );
  1476. }
  1477. else
  1478. {
  1479. $stateIndex = $createdStateAliases[$stateIdentifier];
  1480. }
  1481. $stateTable = "ezcobj_state_{$stateIndex}_perm";
  1482. if ( !isset( $createdStateAliases[$stateIdentifier] ) )
  1483. {
  1484. $createdStateAliases[$stateIdentifier] = $stateIndex;
  1485. $stateLinkTable = "ezcobj_state_lnk_{$stateIndex}_perm";
  1486. $stateGroupTable = "ezcobj_state_grp_{$stateIndex}_perm";
  1487. $stateAliasTables[$stateIdentifier] = $stateTable;
  1488. $sqlPermissionCheckingFrom .= ", ezcobj_state_link $stateLinkTable ";
  1489. $sqlPermissionCheckingFrom .= ", ezcobj_state_group $stateGroupTable ";
  1490. $sqlPermissionCheckingFrom .= ", ezcobj_state $stateTable ";
  1491. $sqlPermissionCheckingWhere .= "AND $stateLinkTable.contentobject_id = ezcontentobject.id " .
  1492. "AND $stateTable.id = $stateLinkTable.contentobject_state_id " .
  1493. "AND $stateTable.group_id = $stateGroupTable.id " .
  1494. "AND $stateGroupTable.identifier='" . $db->escapeString( $stateIdentifier ) . "' ";
  1495. }
  1496. if ( count( $limitationArray[$ident] ) > 1 )
  1497. {
  1498. $sqlPartPart[] = $db->generateSQLINStatement( $limitationArray[$ident], "$stateTable.id" );
  1499. }
  1500. else
  1501. {
  1502. $sqlPartPart[] = "$stateTable.id = " . $limitationArray[$ident][0];
  1503. }
  1504. }
  1505. }
  1506. }
  1507. }
  1508. if ( $sqlPlacementPart )
  1509. {
  1510. $sqlPartPart[] = '( ( ' . implode( ' ) OR ( ', $sqlPlacementPart ) . ' ) )';
  1511. }
  1512. if ( $sqlPartPartPart )
  1513. {
  1514. $sqlPartPart[] = '( ' . implode( ' ) OR ( ', $sqlPartPartPart ) . ' )';
  1515. }
  1516. $sqlParts[] = implode( ' AND ', $sqlPartPart );
  1517. }
  1518. $sqlPermissionCheckingWhere .= ' AND ((' . implode( ") OR (", $sqlParts ) . ')) ';
  1519. }
  1520. $sqlPermissionChecking = array( 'from' => $sqlPermissionCheckingFrom,
  1521. 'where' => $sqlPermissionCheckingWhere,
  1522. 'temp_tables' => $sqlPermissionTempTables );
  1523. return $sqlPermissionChecking;
  1524. }
  1525. /*!
  1526. \a static
  1527. \param $limit maximum number of nodes in the path to use, starting from last node
  1528. */
  1529. static function createNodesConditionSQLStringFromPath( $nodePath, $includingLastNodeInThePath, $limit = false )
  1530. {
  1531. $pathString = false;
  1532. $pathArray = explode( '/', trim( $nodePath, '/' ) );
  1533. $pathArrayCount = count( $pathArray );
  1534. if ( $limit && $includingLastNodeInThePath == false )
  1535. {
  1536. $limit++;
  1537. }
  1538. $sliceOffset = $limit && $pathArrayCount > $limit ? $pathArrayCount - $limit : 0;
  1539. $sliceLength = $includingLastNodeInThePath ? $pathArrayCount - $sliceOffset : $pathArrayCount - ( $sliceOffset + 1 );
  1540. // only take a slice when necessary
  1541. if ( ( $sliceOffset + $sliceLength ) < $pathArrayCount )
  1542. {
  1543. $pathArray = array_slice( $pathArray, $sliceOffset, $sliceLength );
  1544. }
  1545. if ( $sliceLength == 1 )
  1546. {
  1547. $pathString = ' node_id = ' . implode( '', $pathArray ) . ' and ';
  1548. }
  1549. else if ( $sliceLength > 0 )
  1550. {
  1551. $db = eZDB::instance();
  1552. $pathString = ' ' . $db->generateSQLINStatement( $pathArray, 'node_id' ) . ' and ';
  1553. }
  1554. return $pathString;
  1555. }
  1556. /*!
  1557. \a static
  1558. If \a $useSettings is true \a $fetchHidden will be ignored.
  1559. If \a $useSettings is false \a $fetchHidden will be used.
  1560. */
  1561. static function createShowInvisibleSQLString( $useSettings, $fetchHidden = true )
  1562. {
  1563. $showInvisibleNodesCond = '';
  1564. $showInvisible = $fetchHidden;
  1565. if ( $useSettings )
  1566. $showInvisible = eZContentObjectTreeNode::showInvisibleNodes();
  1567. if ( !$showInvisible )
  1568. $showInvisibleNodesCond = 'AND ezcontentobject_tree.is_invisible = 0';
  1569. return $showInvisibleNodesCond;
  1570. }
  1571. /*!
  1572. \a static
  1573. \returns true if we should show invisible nodes (determined by ini setting), false otherwise.
  1574. */
  1575. static function showInvisibleNodes()
  1576. {
  1577. static $cachedResult;
  1578. if ( !isset( $cachedResult ) )
  1579. {
  1580. $ini = eZINI::instance( 'site.ini' );
  1581. $cachedResult = $ini->hasVariable( 'SiteAccessSettings', 'ShowHiddenNodes' ) ?
  1582. $ini->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) == 'true' :
  1583. true;
  1584. }
  1585. return $cachedResult;
  1586. }
  1587. /*!
  1588. \a static
  1589. */
  1590. static function getLimitationList( &$limitation )
  1591. {
  1592. $currentUser = eZUser::currentUser();
  1593. $currentUserID = $currentUser->attribute( 'contentobject_id' );
  1594. $limitationList = array();
  1595. if ( $limitation !== false )
  1596. {
  1597. $limitationList = $limitation;
  1598. }
  1599. else if ( isset( $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] ) )
  1600. {
  1601. $limitationList =& $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'];
  1602. eZDebugSetting::writeDebug( 'kernel-content-treenode', $limitationList, "limitation list" );
  1603. }
  1604. else
  1605. {
  1606. $accessResult = $currentUser->hasAccessTo( 'content', 'read' );
  1607. if ( $accessResult['accessWord'] == 'no' )
  1608. {
  1609. $limitationList = false;
  1610. $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = false;
  1611. }
  1612. else if ( $accessResult['accessWord'] == 'limited' )
  1613. {
  1614. $limitationList = $accessResult['policies'];
  1615. $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = $accessResult['policies'];
  1616. }
  1617. }
  1618. return $limitationList;
  1619. }
  1620. /*!
  1621. \sa subTree
  1622. */
  1623. static function subTreeByNodeID( $params = false, $nodeID = 0 )
  1624. {
  1625. if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
  1626. {
  1627. return null;
  1628. }
  1629. if ( $params === false )
  1630. {
  1631. $params = array( 'Depth' => false,
  1632. 'Offset' => false,
  1633. //'OnlyTranslated' => false,
  1634. 'Language' => false,
  1635. 'Limit' => false,
  1636. 'SortBy' => false,
  1637. 'AttributeFilter' => false,
  1638. 'ExtendedAttributeFilter' => false,
  1639. 'ClassFilterType' => false,
  1640. 'ClassFilterArray' => false,
  1641. 'GroupBy' => false );
  1642. }
  1643. $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false;
  1644. //$onlyTranslated = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated'] : false;
  1645. $language = ( isset( $params['Language'] ) ) ? $params['Language'] : false;
  1646. $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false;
  1647. $depth = ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) ? $params['Depth'] : false;
  1648. $depthOperator = ( isset( $params['DepthOperator'] ) ) ? $params['DepthOperator'] : false;
  1649. $asObject = ( isset( $params['AsObject'] ) ) ? $params['AsObject'] : true;
  1650. $loadDataMap = ( isset( $params['LoadDataMap'] ) ) ? $params['LoadDataMap'] : false;
  1651. $groupBy = ( isset( $params['GroupBy'] ) ) ? $params['GroupBy'] : false;
  1652. $mainNodeOnly = ( isset( $params['MainNodeOnly'] ) ) ? $params['MainNodeOnly'] : false;
  1653. $ignoreVisibility = ( isset( $params['IgnoreVisibility'] ) ) ? $params['IgnoreVisibility'] : false;
  1654. $objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false;
  1655. if ( $offset < 0 )
  1656. {
  1657. $offset = abs( $offset );
  1658. }
  1659. if ( !isset( $params['SortBy'] ) )
  1660. $params['SortBy'] = false;
  1661. if ( !isset( $params['ClassFilterType'] ) )
  1662. $params['ClassFilterType'] = false;
  1663. $allowCustomSorting = false;
  1664. if ( isset( $params['ExtendedAttributeFilter'] ) && is_array ( $params['ExtendedAttributeFilter'] ) )
  1665. {
  1666. $allowCustomSorting = true;
  1667. }
  1668. $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $params['SortBy'], 'ezcontentobject_tree', $allowCustomSorting );
  1669. $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] );
  1670. if ( $classCondition === false )
  1671. {
  1672. eZDebug::writeNotice( "Class filter returned false" );
  1673. return null;
  1674. }
  1675. $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo );
  1676. if ( $attributeFilter === false )
  1677. {
  1678. return null;
  1679. }
  1680. $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
  1681. $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
  1682. $pathStringCond = '';
  1683. $notEqParentString = '';
  1684. // If the node(s) doesn't exist we return null.
  1685. if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
  1686. {
  1687. return null;
  1688. }
  1689. if ( $language )
  1690. {
  1691. if ( !is_array( $language ) )
  1692. {
  1693. $language = array( $language );
  1694. }
  1695. // This call must occur after eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings,
  1696. // because the parent node may not exist in Language
  1697. eZContentLanguage::setPrioritizedLanguages( $language );
  1698. }
  1699. $groupBySelectText = '';
  1700. $groupBySQL = $extendedAttributeFilter['group_by'];
  1701. if ( !$groupBySQL )
  1702. {
  1703. eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
  1704. }
  1705. else if ( $groupBy )
  1706. {
  1707. eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
  1708. }
  1709. $useVersionName = true;
  1710. $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
  1711. $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
  1712. $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false );
  1713. $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
  1714. if ( $language )
  1715. {
  1716. eZContentLanguage::clearPrioritizedLanguages();
  1717. }
  1718. $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
  1719. $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false;
  1720. $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
  1721. $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
  1722. // Determine whether we should show invisible nodes.
  1723. $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
  1724. $query = "SELECT DISTINCT
  1725. ezcontentobject.*,
  1726. ezcontentobject_tree.*,
  1727. ezcontentclass.serialized_name_list as class_serialized_name_list,
  1728. ezcontentclass.identifier as class_identifier,
  1729. ezcontentclass.is_container as is_container
  1730. $groupBySelectText
  1731. $versionNameTargets
  1732. $sortingInfo[attributeTargetSQL]
  1733. $extendedAttributeFilter[columns]
  1734. FROM
  1735. ezcontentobject_tree,
  1736. ezcontentobject,ezcontentclass
  1737. $versionNameTables
  1738. $sortingInfo[attributeFromSQL]
  1739. $attributeFilter[from]
  1740. $extendedAttributeFilter[tables]
  1741. $sqlPermissionChecking[from]
  1742. WHERE
  1743. $pathStringCond
  1744. $extendedAttributeFilter[joins]
  1745. $sortingInfo[attributeWhereSQL]
  1746. $attributeFilter[where]
  1747. ezcontentclass.version=0 AND
  1748. $notEqParentString
  1749. ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
  1750. ezcontentclass.id = ezcontentobject.contentclass_id AND
  1751. $mainNodeOnlyCond
  1752. $classCondition
  1753. $versionNameJoins
  1754. $showInvisibleNodesCond
  1755. $sqlPermissionChecking[where]
  1756. $objectNameFilterSQL
  1757. $languageFilter
  1758. $groupBySQL";
  1759. if ( $sortingInfo['sortingFields'] )
  1760. $query .= " ORDER BY $sortingInfo[sortingFields]";
  1761. $db = eZDB::instance();
  1762. $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
  1763. $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
  1764. 'limit' => $limit ),
  1765. $server );
  1766. if ( $asObject )
  1767. {
  1768. $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  1769. if ( $loadDataMap === true )
  1770. eZContentObject::fillNodeListAttributes( $retNodeList );
  1771. else if ( $loadDataMap && is_numeric( $loadDataMap ) && $loadDataMap >= count( $retNodeList ) )
  1772. eZContentObject::fillNodeListAttributes( $retNodeList );
  1773. }
  1774. else
  1775. {
  1776. $retNodeList = $nodeListArray;
  1777. }
  1778. // cleanup temp tables
  1779. $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
  1780. return $retNodeList;
  1781. }
  1782. function subTree( $params = false )
  1783. {
  1784. return eZContentObjectTreeNode::subTreeByNodeID( $params, $this->attribute( 'node_id' ) );
  1785. }
  1786. /*!
  1787. Retrieve subtrees from multiple paths.
  1788. This method composes a list of objects retrieved from various node paths,
  1789. sorted by criteria that are globally applied to the whole list.
  1790. It is for example useful for an RSS feed that serves content from
  1791. several node paths. The respective subtrees need to be amalgated and
  1792. the resulting object listed sorted by publishing date to show the latest
  1793. entries in chronological order.
  1794. The first parameter is a multi-dimensional array containing the
  1795. node IDs and filter criteria assigned to each of the nodes:
  1796. array(
  1797. [node_1] => array(
  1798. 'ClassFilterType' => [filter_type],
  1799. 'ClassFilterArray' => [filter_array]
  1800. ),
  1801. [node_2] => array(
  1802. 'ClassFilterType' => [filter_type],
  1803. 'ClassFilterArray' => [filter_array]
  1804. )
  1805. )
  1806. The second parameter is a single-dimensional array with criteria
  1807. applied to the list of objects retrieved from the various subtrees:
  1808. array(
  1809. 'SortBy' => [sorting-criteria]
  1810. )
  1811. */
  1812. static function subTreeMultiPaths( $nodesParams, $listParams = NULL )
  1813. {
  1814. if( !is_array( $nodesParams ) || !count( $nodesParams ) )
  1815. {
  1816. eZDebug::writeWarning( __METHOD__.': Nodes parameter must be an array with at least one key.' );
  1817. return null;
  1818. }
  1819. if( $listParams === null )
  1820. {
  1821. $listParams = array(
  1822. 'SortBy' => false,
  1823. 'Offset' => false,
  1824. 'Limit' => false,
  1825. 'SortBy' => false,
  1826. 'GroupBy' => false );
  1827. }
  1828. $offset = ( isset( $listParams['Offset'] ) && is_numeric( $listParams['Offset'] ) ) ? $listParams['Offset'] : false;
  1829. $limit = ( isset( $listParams['Limit'] ) && is_numeric( $listParams['Limit'] ) ) ? $listParams['Limit'] : false;
  1830. $groupBy = ( isset( $listParams['GroupBy'] ) ) ? $listParams['GroupBy'] : false;
  1831. if ( !isset( $listParams['SortBy'] ) )
  1832. {
  1833. $listParams['SortBy'] = false;
  1834. }
  1835. $sortBy = $listParams['SortBy'];
  1836. $queryNodes = '';
  1837. foreach( $nodesParams as $nodeParams )
  1838. {
  1839. $nodeID = $nodeParams['ParentNodeID'];
  1840. if ( !is_numeric( $nodeID ) && !is_array( $nodeID ) )
  1841. {
  1842. eZDebug::writeWarning( __METHOD__.': Nodes parameter must be numeric or an array with numeric values.' );
  1843. $retValue = null;
  1844. return $retValue;
  1845. }
  1846. if ( $nodeParams === null )
  1847. {
  1848. $nodeParams = array(
  1849. 'Depth' => false,
  1850. //'OnlyTranslated' => false,
  1851. 'Language' => false,
  1852. 'AttributeFilter' => false,
  1853. 'ExtendedAttributeFilter' => false,
  1854. 'ClassFilterType' => false,
  1855. 'ClassFilterArray' => false );
  1856. }
  1857. //$onlyTranslated = ( isset( $nodeParams['OnlyTranslated'] ) ) ? $nodeParams['OnlyTranslated'] : false;
  1858. $language = ( isset( $nodeParams['Language'] ) ) ? $nodeParams['Language'] : false;
  1859. $depth = ( isset( $nodeParams['Depth'] ) && is_numeric( $nodeParams['Depth'] ) ) ? $nodeParams['Depth'] : false;
  1860. $depthOperator = ( isset( $nodeParams['DepthOperator'] ) ) ? $nodeParams['DepthOperator'] : false;
  1861. $asObject = ( isset( $nodeParams['AsObject'] ) ) ? $nodeParams['AsObject'] : true;
  1862. $mainNodeOnly = ( isset( $nodeParams['MainNodeOnly'] ) ) ? $nodeParams['MainNodeOnly'] : false;
  1863. $ignoreVisibility = ( isset( $nodeParams['IgnoreVisibility'] ) ) ? $nodeParams['IgnoreVisibility'] : false;
  1864. if ( !isset( $nodeParams['ClassFilterType'] ) )
  1865. {
  1866. $nodeParams['ClassFilterType'] = false;
  1867. }
  1868. if ( $language )
  1869. {
  1870. if ( !is_array( $language ) )
  1871. {
  1872. $language = array( $language );
  1873. }
  1874. eZContentLanguage::setPrioritizedLanguages( $language );
  1875. }
  1876. $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortBy );
  1877. $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $nodeParams['ClassFilterType'], $nodeParams['ClassFilterArray'] );
  1878. $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $nodeParams['AttributeFilter'], $sortingInfo );
  1879. $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $nodeParams['ExtendedAttributeFilter'] );
  1880. $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
  1881. $pathStringCond = '';
  1882. $notEqParentString = '';
  1883. // If the node(s) doesn't exist we return null.
  1884. if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
  1885. {
  1886. $retValue = null;
  1887. return $retValue;
  1888. }
  1889. $useVersionName = true;
  1890. $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
  1891. $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
  1892. $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false );
  1893. $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
  1894. if ( $language )
  1895. {
  1896. eZContentLanguage::clearPrioritizedLanguages();
  1897. }
  1898. $limitation = ( isset( $nodeParams['Limitation'] ) && is_array( $nodeParams['Limitation'] ) ) ? $nodeParams['Limitation']: false;
  1899. $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
  1900. $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
  1901. // Determine whether we should show invisible nodes.
  1902. $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
  1903. $queryNodes .= " (
  1904. $pathStringCond
  1905. $extendedAttributeFilter[joins]
  1906. $sortingInfo[attributeWhereSQL]
  1907. $attributeFilter[where]
  1908. ezcontentclass.version=0 AND
  1909. $notEqParentString
  1910. ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
  1911. ezcontentclass.id = ezcontentobject.contentclass_id AND
  1912. $mainNodeOnlyCond
  1913. $classCondition
  1914. $versionNameJoins
  1915. $showInvisibleNodesCond
  1916. $sqlPermissionChecking[where]
  1917. $languageFilter
  1918. )
  1919. OR";
  1920. }
  1921. $groupBySelectText = '';
  1922. $groupBySQL = $extendedAttributeFilter['group_by'];
  1923. if ( !$groupBySQL )
  1924. {
  1925. eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
  1926. }
  1927. else if ( $groupBy )
  1928. {
  1929. eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
  1930. }
  1931. $query = "SELECT DISTINCT
  1932. ezcontentobject.*,
  1933. ezcontentobject_tree.*,
  1934. ezcontentclass.serialized_name_list as class_serialized_name_list,
  1935. ezcontentclass.identifier as class_identifier,
  1936. ezcontentclass.is_container as is_container
  1937. $groupBySelectText
  1938. $versionNameTargets
  1939. $sortingInfo[attributeTargetSQL]
  1940. , ".$nodeParams['ResultID']." AS resultid
  1941. FROM
  1942. ezcontentobject_tree,
  1943. ezcontentobject,ezcontentclass
  1944. $versionNameTables
  1945. $sortingInfo[attributeFromSQL]
  1946. $attributeFilter[from]
  1947. $extendedAttributeFilter[tables]
  1948. $sqlPermissionChecking[from]
  1949. WHERE
  1950. ".substr($queryNodes, 0, -2)."
  1951. $groupBySQL";
  1952. if ( $sortingInfo['sortingFields'] )
  1953. {
  1954. $query .= " ORDER BY $sortingInfo[sortingFields]";
  1955. }
  1956. $db = eZDB::instance();
  1957. $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
  1958. if ( !$offset && !$limit )
  1959. {
  1960. $nodeListArray = $db->arrayQuery( $query, array(), $server );
  1961. }
  1962. else
  1963. {
  1964. $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
  1965. 'limit' => $limit ),
  1966. $server );
  1967. }
  1968. if ( $asObject )
  1969. {
  1970. $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  1971. }
  1972. else
  1973. {
  1974. $retNodeList = $nodeListArray;
  1975. }
  1976. // cleanup temp tables
  1977. $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
  1978. return $retNodeList;
  1979. }
  1980. static function subTreeGroupByDateField( $field, $type )
  1981. {
  1982. $divisor = 0;
  1983. switch ( $type )
  1984. {
  1985. case 'year':
  1986. {
  1987. $divisor = 60*60*24*365;
  1988. } break;
  1989. case 'week':
  1990. {
  1991. $divisor = 60*60*24*7;
  1992. } break;
  1993. case 'day':
  1994. {
  1995. $divisor = 60*60*24;
  1996. } break;
  1997. case 'hour':
  1998. {
  1999. $divisor = 60*60;
  2000. } break;
  2001. case 'minute':
  2002. {
  2003. $divisor = 60;
  2004. } break;
  2005. case 'second':
  2006. {
  2007. $divisor = 0;
  2008. } break;
  2009. default:
  2010. {
  2011. eZDebug::writeError( "Unknown field type $type", __METHOD__ );
  2012. }
  2013. }
  2014. if ( $divisor > 0 )
  2015. $text = "( $field / $divisor ) AS groupbyfield";
  2016. else
  2017. $text = "$field AS groupbyfield";
  2018. return array( 'select' => $text,
  2019. 'group_field' => "( $field / $divisor )" );
  2020. }
  2021. /*!
  2022. \sa subTreeCount
  2023. */
  2024. static function subTreeCountByNodeID( $params = array(), $nodeID )
  2025. {
  2026. if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
  2027. {
  2028. return null;
  2029. }
  2030. $language = ( isset( $params['Language'] ) ) ? $params['Language'] : false;
  2031. if ( $language )
  2032. {
  2033. if ( !is_array( $language ) )
  2034. {
  2035. $language = array( $language );
  2036. }
  2037. eZContentLanguage::setPrioritizedLanguages( $language );
  2038. }
  2039. $depth = isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ? $params['Depth'] : false;
  2040. $depthOperator = isset( $params['DepthOperator'] ) ? $params['DepthOperator'] : false;
  2041. $pathStringCond = '';
  2042. $notEqParentString = '';
  2043. // If the node(s) doesn't exist we return null.
  2044. if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
  2045. {
  2046. return null;
  2047. }
  2048. $db = eZDB::instance();
  2049. $ini = eZINI::instance();
  2050. // Check for class filtering
  2051. $classCondition = '';
  2052. if ( isset( $params['ClassFilterType'] ) and isset( $params['ClassFilterArray'] ) and
  2053. ( $params['ClassFilterType'] == 'include' or $params['ClassFilterType'] == 'exclude' )
  2054. and count( $params['ClassFilterArray'] ) > 0 )
  2055. {
  2056. $classCondition = ' ';
  2057. $i = 0;
  2058. $classCount = count( $params['ClassFilterArray'] );
  2059. $classIDArray = array();
  2060. foreach ( $params['ClassFilterArray'] as $classID )
  2061. {
  2062. $originalClassID = $classID;
  2063. // Check if classes are recerenced by identifier
  2064. if ( is_string( $classID ) && !is_numeric( $classID ) )
  2065. {
  2066. $classID = eZContentClass::classIDByIdentifier( $classID );
  2067. }
  2068. if ( is_numeric( $classID ) )
  2069. {
  2070. $classIDArray[] = $classID;
  2071. }
  2072. else
  2073. {
  2074. eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
  2075. }
  2076. }
  2077. if ( count( $classIDArray ) > 0 )
  2078. {
  2079. $classCondition .= " ezcontentobject.contentclass_id ";
  2080. if ( $params['ClassFilterType'] == 'include' )
  2081. $classCondition .= " IN ";
  2082. else
  2083. $classCondition .= " NOT IN ";
  2084. $classIDString = implode( ', ', $classIDArray );
  2085. $classCondition .= ' ( ' . $classIDString . ' ) AND';
  2086. }
  2087. }
  2088. // Main node check
  2089. $mainNodeOnlyCond = '';
  2090. if ( isset( $params['MainNodeOnly'] ) && $params['MainNodeOnly'] === true )
  2091. {
  2092. $mainNodeOnlyCond = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
  2093. }
  2094. $attributeFilterParam = isset( $params['AttributeFilter'] ) ? $params['AttributeFilter'] : false;
  2095. $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $attributeFilterParam );
  2096. if ( $attributeFilter === false )
  2097. {
  2098. return null;
  2099. }
  2100. //$onlyTranslated = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated'] : false;
  2101. $useVersionName = true;
  2102. $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
  2103. $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
  2104. $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName, false );
  2105. $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
  2106. if ( $language )
  2107. {
  2108. eZContentLanguage::clearPrioritizedLanguages();
  2109. }
  2110. $objectNameFilter = ( isset( $params['ObjectNameFilter'] ) ) ? $params['ObjectNameFilter'] : false;
  2111. $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
  2112. $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
  2113. // Determine whether we should show invisible nodes.
  2114. $ignoreVisibility = isset( $params['IgnoreVisibility'] ) ? $params['IgnoreVisibility'] : false;
  2115. $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
  2116. $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false;
  2117. $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
  2118. $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
  2119. $query = "SELECT
  2120. count( DISTINCT ezcontentobject_tree.node_id ) as count
  2121. FROM
  2122. ezcontentobject_tree,
  2123. ezcontentobject,ezcontentclass
  2124. $versionNameTables
  2125. $attributeFilter[from]
  2126. $extendedAttributeFilter[tables]
  2127. $sqlPermissionChecking[from]
  2128. WHERE $pathStringCond
  2129. $extendedAttributeFilter[joins]
  2130. $mainNodeOnlyCond
  2131. $classCondition
  2132. $attributeFilter[where]
  2133. ezcontentclass.version=0 AND
  2134. $notEqParentString
  2135. ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
  2136. ezcontentclass.id = ezcontentobject.contentclass_id AND
  2137. $versionNameJoins
  2138. $showInvisibleNodesCond
  2139. $sqlPermissionChecking[where]
  2140. $objectNameFilterSQL
  2141. $languageFilter ";
  2142. $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
  2143. $nodeListArray = $db->arrayQuery( $query, array(), $server );
  2144. // cleanup temp tables
  2145. $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
  2146. return $nodeListArray[0]['count'];
  2147. }
  2148. /*!
  2149. Count number of subnodes
  2150. \param params array
  2151. */
  2152. function subTreeCount( $params = array() )
  2153. {
  2154. return eZContentObjectTreeNode::subTreeCountByNodeID( $params, $this->attribute( 'node_id' ) );
  2155. }
  2156. /*!
  2157. \return The date/time list when object were published
  2158. */
  2159. static function calendar( $params = false, $nodeID = 0 )
  2160. {
  2161. if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
  2162. {
  2163. return array();
  2164. }
  2165. if ( $params === false )
  2166. {
  2167. $params = array( 'Depth' => false,
  2168. 'Offset' => false,
  2169. 'Limit' => false,
  2170. 'AttributeFilter' => false,
  2171. 'ExtendedAttributeFilter' => false,
  2172. 'ClassFilterType' => false,
  2173. 'ClassFilterArray' => false,
  2174. 'GroupBy' => false );
  2175. }
  2176. $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false;
  2177. $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false;
  2178. $depth = ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ) ? $params['Depth'] : false;
  2179. $depthOperator = ( isset( $params['DepthOperator'] ) ) ? $params['DepthOperator'] : false;
  2180. $groupBy = ( isset( $params['GroupBy'] ) ) ? $params['GroupBy'] : false;
  2181. $mainNodeOnly = ( isset( $params['MainNodeOnly'] ) ) ? $params['MainNodeOnly'] : false;
  2182. $ignoreVisibility = ( isset( $params['IgnoreVisibility'] ) ) ? $params['IgnoreVisibility'] : false;
  2183. if ( !isset( $params['ClassFilterType'] ) )
  2184. $params['ClassFilterType'] = false;
  2185. $classCondition = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] );
  2186. $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo );
  2187. $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
  2188. $mainNodeOnlyCond = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
  2189. $pathStringCond = '';
  2190. $notEqParentString = '';
  2191. eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator );
  2192. $groupBySelectText = '';
  2193. $groupBySQL = $extendedAttributeFilter['group_by'];
  2194. if ( !$groupBySQL )
  2195. {
  2196. eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
  2197. }
  2198. else if ( $groupBy )
  2199. {
  2200. eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
  2201. }
  2202. $useVersionName = true;
  2203. $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString( $useVersionName );
  2204. $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString( $useVersionName, false );
  2205. $limitation = ( isset( $params['Limitation'] ) && is_array( $params['Limitation'] ) ) ? $params['Limitation']: false;
  2206. $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
  2207. $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
  2208. // Determine whether we should show invisible nodes.
  2209. $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
  2210. $query = "SELECT DISTINCT
  2211. ezcontentobject.published as published
  2212. $groupBySelectText
  2213. FROM
  2214. ezcontentobject_tree,
  2215. ezcontentobject,ezcontentclass
  2216. $versionNameTables
  2217. $attributeFilter[from]
  2218. $extendedAttributeFilter[tables]
  2219. $sqlPermissionChecking[from]
  2220. WHERE
  2221. $pathStringCond
  2222. $extendedAttributeFilter[joins]
  2223. $attributeFilter[where]
  2224. ezcontentclass.version=0
  2225. AND
  2226. $notEqParentString
  2227. $mainNodeOnlyCond
  2228. ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
  2229. ezcontentclass.id = ezcontentobject.contentclass_id AND
  2230. $classCondition
  2231. $versionNameJoins
  2232. $showInvisibleNodesCond
  2233. $sqlPermissionChecking[where]
  2234. $groupBySQL";
  2235. $db = eZDB::instance();
  2236. $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
  2237. if ( !$offset && !$limit )
  2238. {
  2239. $nodeListArray = $db->arrayQuery( $query, array(), $server );
  2240. }
  2241. else
  2242. {
  2243. $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
  2244. 'limit' => $limit ),
  2245. $server );
  2246. }
  2247. // cleanup temp tables
  2248. $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
  2249. return $nodeListArray;
  2250. }
  2251. /*!
  2252. \return the children(s) of the current node as an array of eZContentObjectTreeNode objects
  2253. */
  2254. function childrenByName( $name )
  2255. {
  2256. $nodeID = $this->attribute( 'node_id' );
  2257. $fromNode = $nodeID ;
  2258. $nodePath = $this->attribute( 'path_string' );
  2259. $nodeDepth = $this->attribute( 'depth' );
  2260. $childrensPath = $nodePath ;
  2261. $db = eZDB::instance();
  2262. $pathString = " path_string like '$childrensPath%' and ";
  2263. $depthCond = '';
  2264. $nodeDepth = $this->Depth + 1;
  2265. $depthCond = ' depth <= ' . $nodeDepth . ' and ';
  2266. $ini = eZINI::instance();
  2267. $db = eZDB::instance();
  2268. $name = $db->escapeString( $name );
  2269. $query = "SELECT ezcontentobject.*,
  2270. ezcontentobject_tree.*,
  2271. ezcontentclass.serialized_name_list as class_serialized_name_list,
  2272. ezcontentclass.identifier as class_identifier,
  2273. ezcontentclass.is_container as is_container
  2274. FROM
  2275. ezcontentobject_tree,
  2276. ezcontentobject,ezcontentclass
  2277. WHERE $pathString
  2278. $depthCond
  2279. ezcontentobject.name = '$name' AND
  2280. ezcontentclass.version=0 AND
  2281. node_id != $fromNode AND
  2282. ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
  2283. ezcontentclass.id = ezcontentobject.contentclass_id";
  2284. $nodeListArray = $db->arrayQuery( $query );
  2285. return eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  2286. }
  2287. /*!
  2288. Returns the first level children in sorted order.
  2289. */
  2290. function children()
  2291. {
  2292. return $this->subTree( array( 'Depth' => 1,
  2293. 'DepthOperator' => 'eq',
  2294. 'SortBy' => $this->sortArray() ) );
  2295. }
  2296. /*!
  2297. Returns the number of children for the current node.
  2298. \params $checkPolicies If \c true it will only include nodes which can be read using the current policies,
  2299. if \c false all nodes are included in count.
  2300. */
  2301. function childrenCount( $checkPolicies = true )
  2302. {
  2303. $params = array( 'Depth' => 1,
  2304. 'DepthOperator' => 'eq' );
  2305. if ( !$checkPolicies )
  2306. $params['Limitation'] = array();
  2307. return $this->subTreeCount( $params );
  2308. }
  2309. /*!
  2310. Get amount views of content node.
  2311. */
  2312. function viewCount()
  2313. {
  2314. $count = eZViewCounter::fetch( $this->attribute( 'node_id' ), false );
  2315. return (int) $count['count'];
  2316. }
  2317. /*!
  2318. \return the sort field name for the numeric sort field ID \a $sortFieldID.
  2319. Gives a warning if the ID is unknown and returns \c 'path'.
  2320. */
  2321. static function sortFieldName( $sortFieldID )
  2322. {
  2323. switch ( $sortFieldID )
  2324. {
  2325. default:
  2326. eZDebug::writeWarning( 'Unknown sort field ID: ' . $sortFieldID, __METHOD__ );
  2327. case self::SORT_FIELD_PATH:
  2328. return 'path';
  2329. case self::SORT_FIELD_PUBLISHED:
  2330. return 'published';
  2331. case self::SORT_FIELD_MODIFIED:
  2332. return 'modified';
  2333. case self::SORT_FIELD_SECTION:
  2334. return 'section';
  2335. case self::SORT_FIELD_DEPTH:
  2336. return 'depth';
  2337. case self::SORT_FIELD_CLASS_IDENTIFIER:
  2338. return 'class_identifier';
  2339. case self::SORT_FIELD_CLASS_NAME:
  2340. return 'class_name';
  2341. case self::SORT_FIELD_PRIORITY:
  2342. return 'priority';
  2343. case self::SORT_FIELD_NAME:
  2344. return 'name';
  2345. case self::SORT_FIELD_MODIFIED_SUBNODE:
  2346. return 'modified_subnode';
  2347. case self::SORT_FIELD_NODE_ID:
  2348. return 'node_id';
  2349. case self::SORT_FIELD_CONTENTOBJECT_ID:
  2350. return 'contentobject_id';
  2351. }
  2352. }
  2353. /*!
  2354. \return the numeric sort field ID for the sort field name \a $sortFieldName.
  2355. Gives a warning if the name is unknown and returns self::SORT_FIELD_PATH.
  2356. */
  2357. static function sortFieldID( $sortFieldName )
  2358. {
  2359. switch ( $sortFieldName )
  2360. {
  2361. default:
  2362. eZDebug::writeWarning( 'Unknown sort field name: ' . $sortFieldName, __METHOD__ );
  2363. case 'path':
  2364. return self::SORT_FIELD_PATH;
  2365. case 'published':
  2366. return self::SORT_FIELD_PUBLISHED;
  2367. case 'modified':
  2368. return self::SORT_FIELD_MODIFIED;
  2369. case 'section':
  2370. return self::SORT_FIELD_SECTION;
  2371. case 'depth':
  2372. return self::SORT_FIELD_DEPTH;
  2373. case 'class_identifier':
  2374. return self::SORT_FIELD_CLASS_IDENTIFIER;
  2375. case 'class_name':
  2376. return self::SORT_FIELD_CLASS_NAME;
  2377. case 'priority':
  2378. return self::SORT_FIELD_PRIORITY;
  2379. case 'name':
  2380. return self::SORT_FIELD_NAME;
  2381. case 'modified_subnode':
  2382. return self::SORT_FIELD_MODIFIED_SUBNODE;
  2383. case 'node_id':
  2384. return self::SORT_FIELD_NODE_ID;
  2385. case 'contentobject_id':
  2386. return self::SORT_FIELD_CONTENTOBJECT_ID;
  2387. }
  2388. }
  2389. /*!
  2390. \return an array which defines the sorting method for this node.
  2391. The array will contain one element which is an array with sort field
  2392. and sort order.
  2393. */
  2394. function sortArray()
  2395. {
  2396. return eZContentObjectTreeNode::sortArrayBySortFieldAndSortOrder( $this->attribute( 'sort_field' ),
  2397. $this->attribute( 'sort_order' ) );
  2398. }
  2399. /*!
  2400. \static
  2401. \return an array which defines the sorting method for this node.
  2402. The array will contain one element which is an array with sort field
  2403. and sort order.
  2404. */
  2405. static function sortArrayBySortFieldAndSortOrder( $sortField, $sortOrder )
  2406. {
  2407. return array( array( eZContentObjectTreeNode::sortFieldName( $sortField ),
  2408. $sortOrder ) );
  2409. }
  2410. /*!
  2411. Will assign a section to the current node and all child objects.
  2412. Only main node assignments will be updated.
  2413. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2414. the calls within a db transaction; thus within db->begin and db->commit.
  2415. */
  2416. static function assignSectionToSubTree( $nodeID, $sectionID, $oldSectionID = false )
  2417. {
  2418. $db = eZDB::instance();
  2419. $node = eZContentObjectTreeNode::fetch( $nodeID );
  2420. $nodePath = $node->attribute( 'path_string' );
  2421. $sectionID =(int) $sectionID;
  2422. $pathString = " path_string like '$nodePath%' AND ";
  2423. // fetch the object id's which needs to be updated
  2424. $objectIDArray = $db->arrayQuery( "SELECT
  2425. ezcontentobject.id
  2426. FROM
  2427. ezcontentobject_tree, ezcontentobject
  2428. WHERE
  2429. $pathString
  2430. ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
  2431. ezcontentobject_tree.main_node_id=ezcontentobject_tree.node_id" );
  2432. if ( count( $objectIDArray ) == 0 )
  2433. return;
  2434. // Who assigns which section at which node should be logged.
  2435. $section = eZSection::fetch( $sectionID );
  2436. $object = $node->object();
  2437. eZAudit::writeAudit( 'section-assign', array( 'Section ID' => $sectionID, 'Section name' => $section->attribute( 'name' ),
  2438. 'Node ID' => $nodeID,
  2439. 'Content object ID' => $object->attribute( 'id' ),
  2440. 'Content object name' => $object->attribute( 'name' ),
  2441. 'Comment' => 'Assigned a section to the current node and all child objects: eZContentObjectTreeNode::assignSectionToSubTree()' ) );
  2442. $objectSimpleIDArray = array();
  2443. foreach ( $objectIDArray as $objectID )
  2444. {
  2445. $objectSimpleIDArray[] = $objectID['id'];
  2446. }
  2447. $filterPart = '';
  2448. if ( $oldSectionID !== false )
  2449. {
  2450. $oldSectionID =(int) $oldSectionID;
  2451. $filterPart = " section_id = '$oldSectionID' and ";
  2452. }
  2453. $db->begin();
  2454. foreach ( array_chunk( $objectSimpleIDArray, 100 ) as $pagedObjectIDs )
  2455. {
  2456. $db->query( "UPDATE ezcontentobject SET section_id='$sectionID' WHERE $filterPart " . $db->generateSQLINStatement( $pagedObjectIDs, 'id', false, true, 'int' ) );
  2457. $db->query( "UPDATE ezsearch_object_word_link SET section_id='$sectionID' WHERE $filterPart " . $db->generateSQLINStatement( $pagedObjectIDs, 'contentobject_id', false, true, 'int' ) );
  2458. }
  2459. $db->commit();
  2460. // clear caches for updated objects
  2461. eZContentObject::clearCache( $objectSimpleIDArray );
  2462. }
  2463. /*!
  2464. \static
  2465. Updates the main node selection for the content object \a $objectID.
  2466. \param $mainNodeID The ID of the node that should be that main node
  2467. \param $objectID The ID of the object that all nodes belong to
  2468. \param $version The version of the object to update node assignments, use \c false for currently published version.
  2469. \param $parentMainNodeID The ID of the parent node of the new main placement
  2470. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2471. the calls within a db transaction; thus within db->begin and db->commit.
  2472. */
  2473. static function updateMainNodeID( $mainNodeID, $objectID, $version = false, $parentMainNodeID, $updateSection = true )
  2474. {
  2475. $mainNodeID = (int)$mainNodeID;
  2476. $parentMainNodeID = (int)$parentMainNodeID;
  2477. $objectID = (int)$objectID;
  2478. $version = (int)$version;
  2479. $db = eZDB::instance();
  2480. $db->begin();
  2481. $db->query( "UPDATE ezcontentobject_tree SET main_node_id=$mainNodeID WHERE contentobject_id=$objectID" );
  2482. if ( !$version )
  2483. {
  2484. $rows = $db->arrayQuery( "SELECT current_version FROM ezcontentobject WHERE id=$objectID" );
  2485. $version = $rows[0]['current_version'];
  2486. }
  2487. $db->query( "UPDATE eznode_assignment SET is_main=1 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node=$parentMainNodeID" );
  2488. $db->query( "UPDATE eznode_assignment SET is_main=0 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node!=$parentMainNodeID" );
  2489. $contentObject = eZContentObject::fetch( $objectID );
  2490. $parentContentObject = eZContentObject::fetchByNodeID( $parentMainNodeID );
  2491. if ( $updateSection && $contentObject->attribute( 'section_id' ) != $parentContentObject->attribute( 'section_id' ) )
  2492. {
  2493. $newSectionID = $parentContentObject->attribute( 'section_id' );
  2494. eZContentObjectTreeNode::assignSectionToSubTree( $mainNodeID, $newSectionID );
  2495. }
  2496. $db->commit();
  2497. }
  2498. function fetchByCRC( $pathStr )
  2499. {
  2500. eZDebug::writeWarning( "Obsolete: use eZURLAlias instead", __METHOD__ );
  2501. return null;
  2502. }
  2503. static function fetchByContentObjectID( $contentObjectID, $asObject = true, $contentObjectVersion = false )
  2504. {
  2505. $conds = array( 'contentobject_id' => $contentObjectID );
  2506. if ( $contentObjectVersion !== false )
  2507. {
  2508. $conds['contentobject_version'] = $contentObjectVersion;
  2509. }
  2510. return eZPersistentObject::fetchObjectList( eZContentObjectTreeNode::definition(),
  2511. null,
  2512. $conds,
  2513. null,
  2514. null,
  2515. $asObject );
  2516. }
  2517. static function fetchByRemoteID( $remoteID, $asObject = true )
  2518. {
  2519. return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "remote_id" => $remoteID ) );
  2520. }
  2521. static function fetchByPath( $pathString, $asObject = true )
  2522. {
  2523. return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_string" => $pathString ) );
  2524. }
  2525. static function fetchByURLPath( $pathString, $asObject = true )
  2526. {
  2527. if ( $pathString == "" )
  2528. {
  2529. eZDebug::writeWarning( 'Can not fetch, given URLPath is empty', __METHOD__ );
  2530. return null;
  2531. }
  2532. return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_identification_string" => $pathString ) );
  2533. }
  2534. /**
  2535. * Fetches path_identification_string for a list of nodes
  2536. *
  2537. * @param array(int) $nodeList
  2538. *
  2539. * @return array Associative array
  2540. */
  2541. static function fetchAliasesFromNodeList( $nodeList )
  2542. {
  2543. if ( !is_array( $nodeList ) || count( $nodeList ) < 1 )
  2544. return array();
  2545. $db = eZDB::instance();
  2546. $where = $db->generateSQLINStatement( $nodeList, 'node_id', false, false, 'int' );
  2547. $query = "SELECT node_id, path_identification_string FROM ezcontentobject_tree WHERE $where";
  2548. $pathListArray = $db->arrayQuery( $query );
  2549. return $pathListArray;
  2550. }
  2551. static function findMainNode( $objectID, $asObject = false )
  2552. {
  2553. $objectID = (int)$objectID;
  2554. $query = "SELECT node_id
  2555. FROM ezcontentobject_tree
  2556. WHERE contentobject_id=$objectID AND
  2557. main_node_id = node_id";
  2558. $db = eZDB::instance();
  2559. $nodeListArray = $db->arrayQuery( $query );
  2560. if ( count( $nodeListArray ) == 1 )
  2561. {
  2562. if ( $asObject )
  2563. {
  2564. return eZContentObjectTreeNode::fetch( $nodeListArray[0]['node_id'] );
  2565. }
  2566. else
  2567. {
  2568. return $nodeListArray[0]['node_id'];
  2569. }
  2570. }
  2571. else if ( count( $nodeListArray ) > 1 )
  2572. {
  2573. eZDebug::writeError( $nodeListArray , "There are more then one main_node for objectID: $objectID" );
  2574. }
  2575. return null;
  2576. }
  2577. /**
  2578. * Fetches the main nodes for an array of object id's
  2579. * @param array(int) $objectIDArray an array of object IDs
  2580. * @param bool $asObject
  2581. * Wether to return the result as an array of eZContentObjectTreeNode
  2582. * (true) or as an array of associative arrays (false)
  2583. * @return array(array|eZContentObjectTreeNode)
  2584. */
  2585. static function findMainNodeArray( $objectIDArray, $asObject = true )
  2586. {
  2587. if ( count( $objectIDArray ) )
  2588. {
  2589. $db = eZDB::instance();
  2590. $objectIDINSQL = $db->generateSQLINStatement( $objectIDArray, 'ezcontentobject_tree.contentobject_id', false, false, 'int' );
  2591. $query="SELECT ezcontentobject.*,
  2592. ezcontentobject_tree.*,
  2593. ezcontentclass.serialized_name_list as class_serialized_name_list,
  2594. ezcontentclass.identifier as class_identifier,
  2595. ezcontentclass.is_container as is_container
  2596. FROM ezcontentobject_tree,
  2597. ezcontentobject,
  2598. ezcontentclass
  2599. WHERE $objectIDINSQL AND
  2600. ezcontentobject_tree.main_node_id = ezcontentobject_tree.node_id AND
  2601. ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
  2602. ezcontentclass.version=0 AND
  2603. ezcontentclass.id = ezcontentobject.contentclass_id";
  2604. $nodeListArray = $db->arrayQuery( $query );
  2605. if ( $asObject )
  2606. {
  2607. $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  2608. return $retNodeArray;
  2609. }
  2610. else
  2611. {
  2612. return $nodeListArray;
  2613. }
  2614. }
  2615. return null;
  2616. }
  2617. /**
  2618. * Fetches a node by ID
  2619. *
  2620. * @param int|array $nodeID Either a node ID or array of node IDs
  2621. * @param string $lang language code to fetch the node in. If not provided, the prioritized language list is used
  2622. * @param bool $asObject True to fetch the node as an eZContentObjectTreeNode, false to fetch its attributes as an array
  2623. * @param array $conditions An associative array (field => value) of fetch conditions. Will be applied as is to the SQL query
  2624. *
  2625. * @return eZContentObjectTreeNode
  2626. */
  2627. static function fetch( $nodeID = false, $lang = false, $asObject = true, $conditions = false )
  2628. {
  2629. $returnValue = null;
  2630. $db = eZDB::instance();
  2631. if ( ( is_numeric( $nodeID ) && $nodeID == 1 ) ||
  2632. ( is_array( $nodeID ) && count( $nodeID ) === 1 && $nodeID[0] == 1 ) )
  2633. {
  2634. $query = "SELECT *
  2635. FROM ezcontentobject_tree
  2636. WHERE node_id = 1";
  2637. }
  2638. else
  2639. {
  2640. $versionNameTables = ', ezcontentobject_name ';
  2641. $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation ';
  2642. $versionNameJoins = " and ezcontentobject_tree.contentobject_id = ezcontentobject_name.contentobject_id and
  2643. ezcontentobject_tree.contentobject_version = ezcontentobject_name.content_version and ";
  2644. if ( $lang )
  2645. {
  2646. $lang = $db->escapeString( $lang );
  2647. $versionNameJoins .= " ezcontentobject_name.content_translation = '$lang' ";
  2648. }
  2649. else
  2650. {
  2651. $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
  2652. }
  2653. $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
  2654. $sqlCondition = '';
  2655. if ( $nodeID !== false )
  2656. {
  2657. if ( is_array( $nodeID ) )
  2658. {
  2659. if( count( $nodeID ) === 1 )
  2660. {
  2661. $sqlCondition = 'node_id = ' . (int) $nodeID[0] . ' AND ';
  2662. }
  2663. else
  2664. {
  2665. $sqlCondition = $db->generateSQLINStatement( $nodeID, 'node_id', false, true, 'int' ) . ' AND ';
  2666. }
  2667. }
  2668. else
  2669. {
  2670. $sqlCondition = 'node_id = ' . (int) $nodeID . ' AND ';
  2671. }
  2672. }
  2673. if ( is_array( $conditions ) )
  2674. {
  2675. foreach( $conditions as $key => $condition )
  2676. {
  2677. if ( is_string( $condition ) )
  2678. {
  2679. $condition = $db->escapeString( $condition );
  2680. $condition = "'$condition'";
  2681. }
  2682. $sqlCondition .= "ezcontentobject_tree." . $db->escapeString( $key ) . "=$condition AND ";
  2683. }
  2684. }
  2685. if ( $sqlCondition == '' )
  2686. {
  2687. eZDebug::writeWarning( 'Cannot fetch node, emtpy ID or no conditions given', __METHOD__ );
  2688. return $returnValue;
  2689. }
  2690. $query="SELECT ezcontentobject.*,
  2691. ezcontentobject_tree.*,
  2692. ezcontentclass.serialized_name_list as class_serialized_name_list,
  2693. ezcontentclass.identifier as class_identifier,
  2694. ezcontentclass.is_container as is_container
  2695. $versionNameTargets
  2696. FROM ezcontentobject_tree,
  2697. ezcontentobject,
  2698. ezcontentclass
  2699. $versionNameTables
  2700. WHERE $sqlCondition
  2701. ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
  2702. ezcontentclass.version=0 AND
  2703. ezcontentclass.id = ezcontentobject.contentclass_id
  2704. $languageFilter
  2705. $versionNameJoins";
  2706. }
  2707. $nodeListArray = $db->arrayQuery( $query );
  2708. if ( count( $nodeListArray ) > 0 )
  2709. {
  2710. if ( $asObject )
  2711. {
  2712. $returnValue = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  2713. if ( count( $returnValue ) === 1 )
  2714. $returnValue = $returnValue[0];
  2715. }
  2716. else
  2717. {
  2718. if ( count( $nodeListArray ) === 1 )
  2719. $returnValue = $nodeListArray[0];
  2720. else
  2721. $returnValue = $nodeListArray;
  2722. }
  2723. }
  2724. return $returnValue;
  2725. }
  2726. /*!
  2727. \static
  2728. Finds the node for the object \a $contentObjectID which placed as child of node \a $parentNodeID.
  2729. \return An eZContentObjectTreeNode object or \c null if no node was found.
  2730. */
  2731. static function fetchNode( $contentObjectID, $parentNodeID )
  2732. {
  2733. $returnValue = null;
  2734. $ini = eZINI::instance();
  2735. $db = eZDB::instance();
  2736. $contentObjectID =(int) $contentObjectID;
  2737. $parentNodeID =(int) $parentNodeID;
  2738. $query = "SELECT ezcontentobject_tree.*,
  2739. ezcontentclass.serialized_name_list as class_serialized_name_list,
  2740. ezcontentclass.identifier as class_identifier,
  2741. ezcontentclass.is_container as is_container
  2742. FROM ezcontentobject_tree,
  2743. ezcontentobject,
  2744. ezcontentclass
  2745. WHERE ezcontentobject_tree.contentobject_id = '$contentObjectID' AND
  2746. ezcontentobject.id = '$contentObjectID' AND
  2747. ezcontentobject_tree.parent_node_id = '$parentNodeID' AND
  2748. ezcontentclass.version=0 AND
  2749. ezcontentclass.id = ezcontentobject.contentclass_id AND ".
  2750. eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
  2751. $nodeListArray = $db->arrayQuery( $query );
  2752. if ( count( $nodeListArray ) == 1 )
  2753. {
  2754. $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray, false );
  2755. $returnValue = $retNodeArray[0];
  2756. }
  2757. return $returnValue;
  2758. }
  2759. function fetchParent()
  2760. {
  2761. return $this->fetch( $this->attribute( 'parent_node_id' ) );
  2762. }
  2763. function pathArray()
  2764. {
  2765. $pathString = $this->attribute( 'path_string' );
  2766. $pathItems = explode( '/', $pathString );
  2767. $pathArray = array();
  2768. foreach ( $pathItems as $pathItem )
  2769. {
  2770. if ( $pathItem != '' )
  2771. $pathArray[] = (int) $pathItem;
  2772. }
  2773. return $pathArray;
  2774. }
  2775. function fetchPath()
  2776. {
  2777. $nodePath = $this->attribute( 'path_string' );
  2778. return eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, false, true );
  2779. }
  2780. /*!
  2781. \static
  2782. \return An array with content node objects that is present in the node path \a $nodePath.
  2783. \param $withLastNode If \c true the last node in the path is included in the list.
  2784. The last node is the node which the path was fetched from.
  2785. \param $asObjects If \c true then return PHP objects, if not return raw row data.
  2786. \param $limit maximum number of nodes in the path to use, starting from last node
  2787. */
  2788. static function fetchNodesByPathString( $nodePath, $withLastNode = false, $asObjects = true, $limit = false )
  2789. {
  2790. $nodesListArray = array();
  2791. $pathString = eZContentObjectTreeNode::createNodesConditionSQLStringFromPath( $nodePath, $withLastNode, $limit );
  2792. if ( $pathString )
  2793. {
  2794. $useVersionName = true;
  2795. $versionNameTables = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
  2796. $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
  2797. $versionNameJoins = eZContentObjectTreeNode::createVersionNameJoinsSQLString ( $useVersionName );
  2798. $query = "SELECT ezcontentobject.*,
  2799. ezcontentobject_tree.*,
  2800. ezcontentclass.serialized_name_list as class_serialized_name_list,
  2801. ezcontentclass.identifier as class_identifier,
  2802. ezcontentclass.is_container as is_container
  2803. $versionNameTargets
  2804. FROM ezcontentobject_tree,
  2805. ezcontentobject,
  2806. ezcontentclass
  2807. $versionNameTables
  2808. WHERE $pathString
  2809. ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
  2810. ezcontentclass.version=0 AND
  2811. ezcontentclass.id = ezcontentobject.contentclass_id
  2812. $versionNameJoins
  2813. ORDER BY path_string";
  2814. $db = eZDB::instance();
  2815. $nodesListArray = $db->arrayQuery( $query );
  2816. }
  2817. if ( $asObjects )
  2818. {
  2819. return eZContentObjectTreeNode::makeObjectsArray( $nodesListArray );
  2820. }
  2821. return $nodesListArray;
  2822. }
  2823. /*!
  2824. \static
  2825. Extracts each node that in the path from db and returns an array of class identifiers
  2826. \param $nodePath A string containing the path of the node, it consists of
  2827. node IDs starting from the root and delimited by / (slash).
  2828. \param $withLastNode If \c true the last node in the path is included in the list.
  2829. The last node is the node which the path was fetched from.
  2830. \param $limit maximum number of nodes in the path to use, starting from last node
  2831. \return An array with class identifier and node ID.
  2832. Example
  2833. \code
  2834. $list = fetchClassIdentifierListByPathString( '/2/10/', false );
  2835. \endcode
  2836. */
  2837. static function fetchClassIdentifierListByPathString( $nodePath, $withLastNode, $limit = false )
  2838. {
  2839. $itemList = array();
  2840. $nodes = eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, $withLastNode, false, $limit );
  2841. foreach ( $nodes as $node )
  2842. {
  2843. $itemList[] = array( 'node_id' => $node['node_id'],
  2844. 'class_identifier' => $node['class_identifier'] );
  2845. }
  2846. return $itemList;
  2847. }
  2848. /*!
  2849. \deprecated This function should no longer be used, use the eZContentClass::instantiate and eZNodeAssignment::create instead.
  2850. */
  2851. function createObject( $contentClassID, $parentNodeID = 2 )
  2852. {
  2853. $class = eZContentClass::fetch( $contentClassID );
  2854. $parentNode = eZContentObjectTreeNode::fetch( $parentNodeID );
  2855. $parentContentObject = $parentNode->attribute( 'object' );
  2856. $sectionID = $parentContentObject->attribute( 'section_id' );
  2857. $object = $class->instantiate( false, $sectionID );
  2858. // $parentContentObject = $parentNode->attribute( 'contentobject' );
  2859. $node = eZContentObjectTreeNode::addChildTo( $object->attribute( "id" ), $parentNodeID, true );
  2860. // $object->setAttribute( "main_node_id", $node->attribute( 'node_id' ) );
  2861. $node->setAttribute( 'main_node_id', $node->attribute( 'node_id' ) );
  2862. $object->store();
  2863. $node->store();
  2864. return $object;
  2865. }
  2866. /*!
  2867. Add a child for this node to the object tree.
  2868. \param $contentobjectID The ID of the contentobject the child-node should point to.
  2869. \param $asObject If true it will return the new child-node as an object, if not it returns the ID.
  2870. \param $contentObjectVersion The version to use on the newly created child-node, if
  2871. false it uses the current_version of the specified object.
  2872. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2873. the calls within a db transaction; thus within db->begin and db->commit.
  2874. */
  2875. function addChild( $contentobjectID, $asObject = false, $contentObjectVersion = false )
  2876. {
  2877. return self::addChildTo( $contentobjectID, $this->attribute( 'node_id' ), $asObject, $contentObjectVersion );
  2878. }
  2879. /*!
  2880. Add a child to the object tree.
  2881. \param $contentobjectID The ID of the contentobject the child-node should point to.
  2882. \param $nodeID The ID of the parent-node to add child-node to.
  2883. \param $asObject If true it will return the new child-node as an object, if not it returns the ID.
  2884. \param $contentObjectVersion The version to use on the newly created child-node, if
  2885. false it uses the current_version of the specified object.
  2886. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2887. the calls within a db transaction; thus within db->begin and db->commit.
  2888. */
  2889. static function addChildTo( $contentobjectID, $nodeID, $asObject = false, $contentObjectVersion = false )
  2890. {
  2891. $node = eZContentObjectTreeNode::fetch( $nodeID );
  2892. $contentObject = eZContentObject::fetch( $contentobjectID );
  2893. if ( !$contentObject )
  2894. {
  2895. return false;
  2896. }
  2897. if ( !$contentObjectVersion )
  2898. {
  2899. $contentObjectVersion = $contentObject->attribute( 'current_version' );
  2900. }
  2901. $db = eZDB::instance();
  2902. $parentMainNodeID = $node->attribute( 'node_id' ); //$parent->attribute( 'main_node_id' );
  2903. $parentPath = $node->attribute( 'path_string' );
  2904. $parentDepth = $node->attribute( 'depth' );
  2905. $isInvinsible = $node->attribute( 'is_invisible' );
  2906. $nodeDepth = $parentDepth + 1 ;
  2907. $insertedNode = eZContentObjectTreeNode::create( $parentMainNodeID, $contentobjectID );
  2908. // set default sorting from content class
  2909. $contentClass = $contentObject->attribute( 'content_class' );
  2910. $insertedNode->setAttribute( 'sort_field', $contentClass->attribute( 'sort_field' ) );
  2911. $insertedNode->setAttribute( 'sort_order', $contentClass->attribute( 'sort_order' ) );
  2912. $insertedNode->setAttribute( 'depth', $nodeDepth );
  2913. $insertedNode->setAttribute( 'path_string', '/TEMPPATH' );
  2914. $insertedNode->setAttribute( 'contentobject_version', $contentObjectVersion );
  2915. // If the parent node is invisible, the new created node should be invisible as well.
  2916. $insertedNode->setAttribute( 'is_invisible', $isInvinsible );
  2917. $db->begin();
  2918. $insertedNode->store();
  2919. $insertedID = $insertedNode->attribute( 'node_id' );
  2920. $newNodePath = $parentPath . $insertedID . '/';
  2921. $insertedNode->setAttribute( 'path_string', $newNodePath );
  2922. $insertedNode->store();
  2923. $db->commit();
  2924. if ( $asObject )
  2925. {
  2926. return $insertedNode;
  2927. }
  2928. else
  2929. {
  2930. return $insertedID;
  2931. }
  2932. }
  2933. /*!
  2934. \return an url alias for the current node. It will generate a unique alias.
  2935. */
  2936. function pathWithNames( $regenerateCurrent = false )
  2937. {
  2938. // Only set name if current node is not the content root
  2939. $ini = eZINI::instance( 'content.ini' );
  2940. $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' );
  2941. if ( $this->attribute( 'node_id' ) != $contentRootID )
  2942. {
  2943. $pathArray = $this->pathArray();
  2944. // Get rid of node with ID 1 (a special node)
  2945. array_shift( $pathArray );
  2946. if ( $regenerateCurrent )
  2947. {
  2948. // Get rid of current node, path element for this will be calculated
  2949. array_pop( $pathArray );
  2950. }
  2951. if ( count( $pathArray ) == 0 )
  2952. {
  2953. $path = '';
  2954. }
  2955. else
  2956. {
  2957. $path = eZURLAliasML::fetchPathByActionList( "eznode", $pathArray, $this->CurrentLanguage );
  2958. }
  2959. // Fallback in case fetchPathByActionList() fails,
  2960. // then we ask for the path from the parent and generate the current
  2961. // entry ourselves.
  2962. if ( $path === null )
  2963. {
  2964. if ( $this->attribute( 'depth' ) == 0 ) // Top node should just return empty string
  2965. {
  2966. return '';
  2967. }
  2968. eZDebug::writeError( __METHOD__ . "() failed to fetch path of node " . $this->attribute( 'node_id' ) . ", falling back to generated url entries. Run updateniceurls.php to fix the problem." );
  2969. // Return a perma-link when the path lookup failed, this link will always work
  2970. $path = 'content/view/full/' . $this->attribute( 'node_id' );
  2971. return $path;
  2972. }
  2973. if ( $regenerateCurrent )
  2974. {
  2975. $nodeName = $this->attribute( 'name' );
  2976. $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $this->attribute( 'node_id' ) );
  2977. if ( $path != '' )
  2978. {
  2979. $path .= '/' . $nodeName ;
  2980. }
  2981. else
  2982. {
  2983. $path = $nodeName ;
  2984. }
  2985. }
  2986. }
  2987. else
  2988. {
  2989. $path = '';
  2990. }
  2991. if ( $regenerateCurrent )
  2992. {
  2993. $path = $this->checkPath( $path );
  2994. }
  2995. return $path;
  2996. }
  2997. /*!
  2998. Check if a node with the same name already exists. If so create a $name + __x value.
  2999. */
  3000. function checkPath( $path )
  3001. {
  3002. $path = eZURLAliasML::cleanURL( $path );
  3003. $elements = explode( "/", $path );
  3004. $element = array_pop( $elements );
  3005. return $this->adjustPathElement( $element );
  3006. }
  3007. /*!
  3008. Checks the path element $element against reserved words and existing elements.
  3009. If the path element is already used, it will append a number and try again.
  3010. The adjusted path element is returned.
  3011. \param $element The desired url element name
  3012. \param $useParentFromNodeObject Use the parent from node object as a base
  3013. for checking name collisions. This is needed
  3014. when moving nodes, and the url entries are
  3015. not updated yet.
  3016. \code
  3017. echo $node->adjustPathElement( 'Content' ); // outputs Content1
  3018. \endcode
  3019. */
  3020. function adjustPathElement( $element, $useParentFromNodeObject = false )
  3021. {
  3022. $nodeID = (int)$this->attribute( 'node_id' );
  3023. $parentNodeID = (int)$this->attribute( 'parent_node_id' );
  3024. $action = "eznode:" . $nodeID;
  3025. $elements = eZURLAliasML::fetchByAction( 'eznode', $nodeID );
  3026. if ( count( $elements ) > 0 and !$useParentFromNodeObject )
  3027. {
  3028. $parentElementID = (int)$elements[0]->attribute( 'parent' );
  3029. return eZURLAliasML::findUniqueText( $parentElementID, $element, $action );
  3030. }
  3031. else
  3032. {
  3033. $parentElements = eZURLAliasML::fetchByAction( 'eznode', $parentNodeID );
  3034. if ( count( $parentElements ) > 0 && $parentElements[0]->attribute( 'text' ) != '' )
  3035. {
  3036. // Pick one of the parents and get the ID
  3037. $parentElementID = (int)$parentElements[0]->attribute( 'id' );
  3038. return eZURLAliasML::findUniqueText( $parentElementID, $element, $action );
  3039. }
  3040. }
  3041. return eZURLAliasML::findUniqueText( 0, $element, $action );
  3042. }
  3043. /*!
  3044. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3045. the calls within a db transaction; thus within db->begin and db->commit.
  3046. \deprecated Use updateSubTreePath() instead.
  3047. */
  3048. function updateURLAlias()
  3049. {
  3050. eZDebug::writeWarning( __METHOD__ . " is deprecated, use updateSubTreePath() instead" );
  3051. return $this->updateSubTreePath();
  3052. }
  3053. /*!
  3054. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3055. the calls within a db transaction; thus within db->begin and db->commit.
  3056. */
  3057. function updateSubTreePath( $updateParent = true, $nodeMove = false )
  3058. {
  3059. $changeCount = 0;
  3060. $nodeID = $this->attribute( 'node_id' );
  3061. $parentNodeID = $this->attribute( 'parent_node_id' );
  3062. // Avoid recursion due to database inconsistencies
  3063. if ( $nodeID === $parentNodeID )
  3064. {
  3065. eZDebug::writeError( "Parent node ID equals node ID for node: $nodeID. The node cannot be a parent of itself!", __METHOD__ );
  3066. return false;
  3067. }
  3068. // Only set name if current node is not the content root
  3069. $ini = eZINI::instance( 'content.ini' );
  3070. $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' );
  3071. $obj = $this->object();
  3072. $alwaysMask = ( $obj->attribute( 'language_mask' ) & 1 );
  3073. $languages = $obj->allLanguages();
  3074. $nameList = array();
  3075. $initialLanguageID = $obj->attribute( 'initial_language_id' );
  3076. $pathIdentificationName = false;
  3077. foreach ( $languages as $language )
  3078. {
  3079. $nodeName = '';
  3080. if ( $nodeID != $contentRootID )
  3081. {
  3082. $objClass = $obj->attribute( 'content_class' );
  3083. $nodeName = $objClass->urlAliasName( $obj, false, $language->attribute( 'locale' ) );
  3084. $nodeName = eZURLAliasFilter::processFilters( $nodeName, $language, $this );
  3085. $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $nodeID );
  3086. $nodeName = $this->adjustPathElement( $nodeName, $nodeMove );
  3087. // Compatibility mode:
  3088. // Store name for the 'path_identification_string' column.
  3089. if ( $initialLanguageID == $language->attribute( 'id' ) )
  3090. {
  3091. $pathIdentificationName = eZURLAliasML::convertToAliasCompat( $nodeName, 'node_' . $nodeID );
  3092. }
  3093. }
  3094. $nameList[] = array( 'text' => $nodeName,
  3095. 'language' => $language );
  3096. }
  3097. $parentActionName = "eznode";
  3098. $parentActionValue = $parentNodeID;
  3099. $parentElementID = false;
  3100. $existingElements = eZURLAliasML::fetchByAction( "eznode", $nodeID );
  3101. $existingElementID = null;
  3102. if ( count( $existingElements ) > 0 )
  3103. {
  3104. $existingElementID = $existingElements[0]->attribute( 'id' );
  3105. $parentElementID = $existingElements[0]->attribute( 'parent' );
  3106. }
  3107. // If we have parent element it means the node is already published
  3108. // and we have to see if it has been moved
  3109. if ( $parentNodeID != 1 and $updateParent )
  3110. {
  3111. $parents = eZURLAliasML::fetchByAction( "eznode", $parentNodeID );
  3112. if ( count( $parents ) == 0 )
  3113. {
  3114. $parentNode = $this->fetchParent();
  3115. if ( !$parentNode )
  3116. {
  3117. return false;
  3118. }
  3119. $result = $parentNode->updateSubTreePath();
  3120. if ( !$result )
  3121. {
  3122. return false;
  3123. }
  3124. $parents = eZURLAliasML::fetchByAction( $parentActionName, $parentActionValue );
  3125. if ( count( $parents ) == 0 )
  3126. {
  3127. return false;
  3128. }
  3129. $oldParentElementID = $parentElementID;
  3130. foreach ( $parents as $paren )
  3131. {
  3132. $parentElementID = 0;
  3133. if ( $paren->attribute( 'text' ) != '' )
  3134. {
  3135. $parentElementID = (int)$paren->attribute( 'link' );
  3136. break;
  3137. }
  3138. }
  3139. }
  3140. else
  3141. {
  3142. $oldParentElementID = $parentElementID;
  3143. $parentElementID = 0;
  3144. foreach ( $parents as $paren )
  3145. {
  3146. if ( $paren->attribute( 'text' ) != '' )
  3147. {
  3148. $parentElementID = (int)$paren->attribute( 'link' );
  3149. break;
  3150. }
  3151. }
  3152. }
  3153. }
  3154. else // Parent is ID 1, ie. this node is top-level
  3155. {
  3156. }
  3157. $this->updatePathIdentificationString( $pathIdentificationName );
  3158. $languageID = $obj->attribute( 'initial_language_id' );
  3159. $cleanup = false;
  3160. foreach ( $nameList as $nameEntry )
  3161. {
  3162. $text = $nameEntry['text'];
  3163. $language = $nameEntry['language'];
  3164. $result = eZURLAliasML::storePath( $text, 'eznode:' . $nodeID, $language, false, $alwaysMask, $parentElementID, $cleanup );
  3165. if ( $result['status'] === true )
  3166. $changeCount++;
  3167. }
  3168. return $changeCount;
  3169. }
  3170. /*!
  3171. \private
  3172. Updates the path_identification_string field in ezcontentobject_tree by
  3173. fetching the value from the parent and appending $pathIdentificationName.
  3174. \note This stores the current object to the database
  3175. */
  3176. function updatePathIdentificationString( $pathIdentificationName )
  3177. {
  3178. // Update the path_identification_string column for the node
  3179. $pathIdentificationString = '';
  3180. if ( $this->attribute( 'parent_node_id' ) != 1 )
  3181. {
  3182. if ( !isset( $parentNode ) )
  3183. $parentNode = $this->fetchParent();
  3184. // Avoid crashes due to database inconsistencies
  3185. if ( $parentNode instanceof eZContentObjectTreeNode )
  3186. {
  3187. $pathIdentificationString = $parentNode->attribute( 'path_identification_string' );
  3188. }
  3189. else
  3190. {
  3191. eZDebug::writeError( 'Failed to fetch parent node for pathIdentificationName "' . $pathIdentificationName .
  3192. '" and parent_node_id ' . $this->attribute( 'parent_node_id' ), __METHOD__ );
  3193. }
  3194. }
  3195. if ( strlen( $pathIdentificationString ) > 0 )
  3196. $pathIdentificationString .= '/' . $pathIdentificationName;
  3197. else
  3198. $pathIdentificationString = $pathIdentificationName;
  3199. if ( $this->attribute( 'path_identification_string' ) != $pathIdentificationString )
  3200. {
  3201. $this->setAttribute( 'path_identification_string', $pathIdentificationString );
  3202. $this->sync();
  3203. }
  3204. }
  3205. /*!
  3206. \static
  3207. \sa removeThis
  3208. */
  3209. static function removeNode( $nodeID = 0 )
  3210. {
  3211. $node = eZContentObjectTreeNode::fetch( $nodeID );
  3212. if ( !is_object( $node ) )
  3213. {
  3214. return;
  3215. }
  3216. return $node->removeThis();
  3217. }
  3218. /*!
  3219. Removes the current node.
  3220. Use ->removeNodeFromTree() if you need to handle main node change + remove object if needed
  3221. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3222. the calls within a db transaction; thus within db->begin and db->commit.
  3223. */
  3224. function removeThis()
  3225. {
  3226. $ini = eZINI::instance();
  3227. $object = $this->object();
  3228. $nodeID = $this->attribute( 'node_id' );
  3229. if ( eZAudit::isAuditEnabled() )
  3230. {
  3231. // Set audit params.
  3232. $objectID = $object->attribute( 'id' );
  3233. $objectName = $object->attribute( 'name' );
  3234. eZAudit::writeAudit( 'content-delete', array( 'Node ID' => $nodeID, 'Object ID' => $objectID, 'Content Name' => $objectName,
  3235. 'Comment' => 'Removed the current node: eZContentObjectTreeNode::removeNode()' ) );
  3236. }
  3237. $db = eZDB::instance();
  3238. $db->begin();
  3239. $nodePath = $this->attribute( 'path_string' );
  3240. $childrensPath = $nodePath ;
  3241. $pathString = " path_string like '$childrensPath%' ";
  3242. $urlAlias = $this->attribute( 'url_alias' );
  3243. // Remove static cache
  3244. if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
  3245. {
  3246. $staticCache = new eZStaticCache();
  3247. $staticCache->removeURL( "/" . $urlAlias );
  3248. $staticCache->generateAlwaysUpdatedCache();
  3249. $parent = $this->fetchParent();
  3250. }
  3251. $db->query( "DELETE FROM ezcontentobject_tree
  3252. WHERE $pathString OR
  3253. path_string = '$nodePath'" );
  3254. // Re-cache parent node
  3255. if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
  3256. {
  3257. if ( $parent )
  3258. {
  3259. $staticCache->cacheURL( "/" . $parent->urlAlias() );
  3260. }
  3261. }
  3262. // Clean up URL alias entries
  3263. eZURLAliasML::removeByAction( 'eznode', $nodeID );
  3264. // Clean up content cache
  3265. eZContentCacheManager::clearContentCacheIfNeeded( $this->attribute( 'contentobject_id' ) );
  3266. // clean up user cache
  3267. if ( in_array( $object->attribute( 'contentclass_id' ), eZUser::contentClassIDs() ) )
  3268. {
  3269. eZUser::removeSessionData( $object->attribute( 'id' ) );
  3270. eZUser::purgeUserCacheByUserId( $object->attribute( 'id' ) );
  3271. }
  3272. $parentNode = $this->attribute( 'parent' );
  3273. if ( is_object( $parentNode ) )
  3274. {
  3275. eZContentCacheManager::clearContentCacheIfNeeded( $parentNode->attribute( 'contentobject_id' ) );
  3276. $parentNode->updateAndStoreModified();
  3277. }
  3278. // Clean up policies and limitations
  3279. eZRole::cleanupByNode( $this );
  3280. // Clean up recent items
  3281. eZContentBrowseRecent::removeRecentByNodeID( $nodeID );
  3282. // Clean up bookmarks
  3283. eZContentBrowseBookmark::removeByNodeID( $nodeID );
  3284. // Clean up tip-a-friend counter
  3285. eZTipafriendCounter::removeForNode( $nodeID );
  3286. // Clean up view counter
  3287. eZViewCounter::removeCounter( $nodeID );
  3288. $db->commit();
  3289. }
  3290. /*!
  3291. \static
  3292. Returns information on what will happen if all subtrees in \a $deleteIDArray
  3293. is removed. The returned structure is:
  3294. - move_to_trash - \c true if removed objects can be moved to trash,
  3295. some objects are not allowed to be in trash (e.g user).
  3296. - total_child_count - The total number of children for all delete items
  3297. - can_remove_all - Will be set to \c true if all selected items can be removed, \c false otherwise
  3298. - delete_list - A list of all subtrees that should be removed, structure:
  3299. -- node - The content node
  3300. -- object - The content object
  3301. -- class - The content class
  3302. -- node_name - The name of the node
  3303. -- child_count - Total number of child items below the node
  3304. -- can_remove - Boolean which tells if the user has permission to remove the node
  3305. -- can_remove_subtree - Boolean which tells if the user has permission to remove items in the subtree
  3306. -- new_main_node_id - The new main node ID for the node if it needs to be moved, or \c false if not
  3307. -- object_node_count - The number of nodes the object has (before removal)
  3308. -- sole_node_count - The number of nodes in the subtree (excluding current) that does
  3309. not have multiple locations.
  3310. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3311. the calls within a db transaction; thus within db->begin and db->commit.
  3312. */
  3313. static function subtreeRemovalInformation( $deleteIDArray )
  3314. {
  3315. return eZContentObjectTreeNode::removeSubtrees( $deleteIDArray, true, true );
  3316. }
  3317. /*!
  3318. \static
  3319. Will remove the nodes in the subtrees defined in \a $deleteIDArray,
  3320. it will only remove the nodes unless there are no more nodes for
  3321. an object in which case the object is removed too.
  3322. \param $moveToTrash If \c true it will move the object to trash, if \c false
  3323. the object will be purged from the system.
  3324. \param $infoOnly If set to \c true then it will not remove the subtree
  3325. but instead return information on what will happen
  3326. if it is removed. See subtreeRemovalInformation() for the
  3327. returned structure.
  3328. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3329. the calls within a db transaction; thus within db->begin and db->commit.
  3330. */
  3331. static function removeSubtrees( $deleteIDArray, $moveToTrash = true, $infoOnly = false )
  3332. {
  3333. $moveToTrashAllowed = true;
  3334. $deleteResult = array();
  3335. $totalChildCount = 0;
  3336. $totalLoneNodeCount = 0;
  3337. $canRemoveAll = true;
  3338. $hasPendingObject = false;
  3339. $db = eZDB::instance();
  3340. $db->begin();
  3341. $userClassIDArray = eZUser::contentClassIDs();
  3342. foreach ( $deleteIDArray as $deleteID )
  3343. {
  3344. $node = eZContentObjectTreeNode::fetch( $deleteID );
  3345. if ( $node === null )
  3346. continue;
  3347. $object = $node->attribute( 'object' );
  3348. if ( $object === null )
  3349. continue;
  3350. $class = $object->attribute( 'content_class' );
  3351. $canRemove = $object->attribute( 'can_remove' );
  3352. $canRemoveSubtree = true;
  3353. $nodeID = $node->attribute( 'node_id' );
  3354. $nodeName = $object->attribute( 'name' );
  3355. $childCount = 0;
  3356. $newMainNodeID = false;
  3357. $objectNodeCount = 0;
  3358. $readableChildCount = 0;
  3359. if ( $canRemove )
  3360. {
  3361. $isUserClass = in_array( $class->attribute( 'id' ), $userClassIDArray );
  3362. if ( $moveToTrashAllowed and $isUserClass )
  3363. {
  3364. $moveToTrashAllowed = false;
  3365. }
  3366. $readableChildCount = $node->subTreeCount( array( 'Limitation' => array() ) );
  3367. $childCount = $node->subTreeCount( array( 'IgnoreVisibility' => true ) );
  3368. $totalChildCount += $childCount;
  3369. $allAssignedNodes = $object->attribute( 'assigned_nodes' );
  3370. $objectNodeCount = count( $allAssignedNodes );
  3371. // We need to find a new main node ID if we are trying
  3372. // to remove the current main node.
  3373. if ( $node->attribute( 'main_node_id' ) == $nodeID )
  3374. {
  3375. if ( count( $allAssignedNodes ) > 1 )
  3376. {
  3377. foreach( $allAssignedNodes as $assignedNode )
  3378. {
  3379. $assignedNodeID = $assignedNode->attribute( 'node_id' );
  3380. if ( $assignedNodeID == $nodeID )
  3381. continue;
  3382. $newMainNodeID = $assignedNodeID;
  3383. break;
  3384. }
  3385. }
  3386. }
  3387. if ( $infoOnly )
  3388. {
  3389. // Find the number of items in the subtree we are allowed to remove
  3390. // if this differs from the total count it means we have items we cannot remove
  3391. // We do this by fetching the limitation list for content/remove
  3392. // and passing it to the subtree count function.
  3393. $currentUser = eZUser::currentUser();
  3394. $accessResult = $currentUser->hasAccessTo( 'content', 'remove' );
  3395. if ( $accessResult['accessWord'] == 'limited' )
  3396. {
  3397. $limitationList = $accessResult['policies'];
  3398. $removeableChildCount = $node->subTreeCount( array( 'Limitation' => $limitationList, 'IgnoreVisibility' => true ) );
  3399. $canRemoveSubtree = ( $removeableChildCount == $childCount );
  3400. $canRemove = $canRemoveSubtree;
  3401. }
  3402. //check if there is sub object in pending status
  3403. $limitCount = 100;
  3404. $offset = 0;
  3405. while( 1 )
  3406. {
  3407. $children = $node->subTree( array( 'Limitation' => array(),
  3408. 'SortBy' => array( 'path' , false ),
  3409. 'Offset' => $offset,
  3410. 'Limit' => $limitCount,
  3411. 'IgnoreVisibility' => true,
  3412. 'AsObject' => false ) );
  3413. // fetch pending node assignment(pending object)
  3414. $idList = array();
  3415. //add node itself into idList
  3416. if( $offset === 0 )
  3417. {
  3418. $idList[] = $nodeID;
  3419. }
  3420. foreach( $children as $child )
  3421. {
  3422. $idList[] = $child['node_id'];
  3423. }
  3424. if( count( $idList ) === 0 )
  3425. {
  3426. break;
  3427. }
  3428. $pendingChildCount = eZNodeAssignment::fetchChildCountByVersionStatus( $idList,
  3429. eZContentObjectVersion::STATUS_PENDING );
  3430. if( $pendingChildCount !== 0 )
  3431. {
  3432. // there is pending object
  3433. $hasPendingObject = true;
  3434. break;
  3435. }
  3436. $offset += $limitCount;
  3437. }
  3438. }
  3439. // We will only remove the subtree if are allowed
  3440. // and are told to do so.
  3441. if ( $canRemove and !$infoOnly )
  3442. {
  3443. $moveToTrashTemp = $moveToTrash;
  3444. if ( !$moveToTrashAllowed )
  3445. $moveToTrashTemp = false;
  3446. // Remove children, fetching them by 100 to avoid memory overflow.
  3447. // removeNodeFromTree -> removeThis handles cache clearing
  3448. while ( 1 )
  3449. {
  3450. // We should remove the latest subitems first,
  3451. // so we should fetch subitems sorted by 'path_string' DESC
  3452. $children = $node->subTree( array( 'Limitation' => array(),
  3453. 'SortBy' => array( 'path' , false ),
  3454. 'Limit' => 100,
  3455. 'IgnoreVisibility' => true ) );
  3456. if ( !$children )
  3457. break;
  3458. foreach ( $children as $child )
  3459. {
  3460. $childObject = $child->attribute( 'object' );
  3461. $child->removeNodeFromTree( $moveToTrashTemp );
  3462. eZContentObject::clearCache();
  3463. }
  3464. }
  3465. $node->removeNodeFromTree( $moveToTrashTemp );
  3466. }
  3467. }
  3468. if ( !$canRemove )
  3469. $canRemoveAll = false;
  3470. // Do not create info list if we are removing subtrees
  3471. if ( !$infoOnly )
  3472. continue;
  3473. $soleNodeCount = $node->subtreeSoleNodeCount();
  3474. $totalLoneNodeCount += $soleNodeCount;
  3475. if ( $objectNodeCount <= 1 )
  3476. ++$totalLoneNodeCount;
  3477. $item = array( "nodeName" => $nodeName, // Backwards compatibility
  3478. "childCount" => $childCount, // Backwards compatibility
  3479. "additionalWarning" => '', // Backwards compatibility, this will always be empty
  3480. 'node' => $node,
  3481. 'object' => $object,
  3482. 'class' => $class,
  3483. 'node_name' => $nodeName,
  3484. 'child_count' => $childCount,
  3485. 'object_node_count' => $objectNodeCount,
  3486. 'sole_node_count' => $soleNodeCount,
  3487. 'can_remove' => $canRemove,
  3488. 'can_remove_subtree' => $canRemoveSubtree,
  3489. 'real_child_count' => $readableChildCount,
  3490. 'new_main_node_id' => $newMainNodeID );
  3491. $deleteResult[] = $item;
  3492. }
  3493. $db->commit();
  3494. if ( !$infoOnly )
  3495. return true;
  3496. if ( $moveToTrashAllowed and $totalLoneNodeCount == 0 )
  3497. $moveToTrashAllowed = false;
  3498. return array( 'move_to_trash' => $moveToTrashAllowed,
  3499. 'total_child_count' => $totalChildCount,
  3500. 'can_remove_all' => $canRemoveAll,
  3501. 'delete_list' => $deleteResult,
  3502. 'has_pending_object' => $hasPendingObject,
  3503. 'reverse_related_count' => eZContentObjectTreeNode::reverseRelatedCount( $deleteIDArray ) );
  3504. }
  3505. /*!
  3506. \private
  3507. \static
  3508. Return reverse related count for specified node
  3509. \param $nodeIDArray, array of node id's
  3510. \return reverse related count.
  3511. */
  3512. static function reverseRelatedCount( $nodeIDArray )
  3513. {
  3514. // Select count of all elements having reverse relations. And ignore those items that don't relate to objects other than being removed.
  3515. if ( $nodeIDArray === array() )
  3516. {
  3517. return 0;
  3518. }
  3519. foreach( $nodeIDArray as $nodeID )
  3520. {
  3521. $contentObjectTreeNode = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  3522. $tempPathString = $contentObjectTreeNode['path_string'];
  3523. // Create WHERE section
  3524. $pathStringArray[] = "tree.path_string like '$tempPathString%'";
  3525. $path2StringArray[] = "tree2.path_string like '$tempPathString%'";
  3526. }
  3527. $path_strings = '( ' . implode( ' OR ', $pathStringArray ) . ' ) ';
  3528. $path_strings_where = '( ' . implode( ' OR ', $path2StringArray ) . ' ) ';
  3529. // Total count of sub items
  3530. $db = eZDB::instance();
  3531. $countOfItems = $db->arrayQuery( "SELECT COUNT( DISTINCT( tree.node_id ) ) as count
  3532. FROM ezcontentobject_tree tree, ezcontentobject obj,
  3533. ezcontentobject_link link LEFT JOIN ezcontentobject_tree tree2
  3534. ON link.from_contentobject_id = tree2.contentobject_id
  3535. WHERE $path_strings
  3536. and link.to_contentobject_id = tree.contentobject_id
  3537. and obj.id = link.from_contentobject_id
  3538. and obj.current_version = link.from_contentobject_version
  3539. and not $path_strings_where" );
  3540. if ( $countOfItems )
  3541. {
  3542. return $countOfItems[0]['count'];
  3543. }
  3544. }
  3545. /*!
  3546. Will check if you are removing the main node in which case it relocates
  3547. the main node before removing it. It will also remove the object if there
  3548. no more node assignments for it.
  3549. \param $moveToTrash If \c true it will move the object to trash, if \c false
  3550. the object will be purged from the system.
  3551. \note This uses remove() to do the actual node removal but has some extra logic
  3552. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3553. the calls within a db transaction; thus within db->begin and db->commit.
  3554. */
  3555. function removeNodeFromTree( $moveToTrash = true )
  3556. {
  3557. $nodeID = $this->attribute( 'node_id' );
  3558. if ( $nodeID == $this->attribute( 'main_node_id' ) )
  3559. {
  3560. $object = $this->object();
  3561. $assignedNodes = $object->attribute( 'assigned_nodes' );
  3562. if ( count( $assignedNodes ) > 1 )
  3563. {
  3564. $newMainNode = false;
  3565. foreach ( $assignedNodes as $assignedNode )
  3566. {
  3567. $assignedNodeID = $assignedNode->attribute( 'node_id' );
  3568. if ( $assignedNodeID == $nodeID )
  3569. continue;
  3570. $newMainNode = $assignedNode;
  3571. break;
  3572. }
  3573. // We need to change the main node ID before we remove the current node
  3574. $db = eZDB::instance();
  3575. $db->begin();
  3576. eZContentObjectTreeNode::updateMainNodeID( $newMainNode->attribute( 'node_id' ),
  3577. $object->attribute( 'id' ),
  3578. $object->attribute( 'current_version' ),
  3579. $newMainNode->attribute( 'parent_node_id' ) );
  3580. $this->removeThis();
  3581. $db->commit();
  3582. }
  3583. else
  3584. {
  3585. // This is the last assignment so we remove the object too
  3586. $db = eZDB::instance();
  3587. $db->begin();
  3588. $this->removeThis();
  3589. if ( $moveToTrash )
  3590. {
  3591. // saving information about this node in ..trash_node table
  3592. $trashNode = eZContentObjectTrashNode::createFromNode( $this );
  3593. $db = eZDB::instance();
  3594. $db->begin();
  3595. $trashNode->storeToTrash();
  3596. $db->commit();
  3597. $object->removeThis();
  3598. }
  3599. else
  3600. {
  3601. $object->purge();
  3602. }
  3603. $db->commit();
  3604. }
  3605. }
  3606. else
  3607. {
  3608. $this->removeThis();
  3609. }
  3610. }
  3611. /*!
  3612. \return The number of nodes in the current subtree that have no other placements.
  3613. */
  3614. function subtreeSoleNodeCount( $params = array() )
  3615. {
  3616. $nodeID = $this->attribute( 'node_id' );
  3617. $node = $this;
  3618. $depth = false;
  3619. if ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) )
  3620. {
  3621. $depth = $params['Depth'];
  3622. }
  3623. $fromNode = $nodeID;
  3624. $nodePath = null;
  3625. $nodeDepth = 0;
  3626. if ( count( $node ) != 0 )
  3627. {
  3628. $nodePath = $node->attribute( 'path_string' );
  3629. $nodeDepth = $node->attribute( 'depth' );
  3630. }
  3631. $childPath = $nodePath;
  3632. $db = eZDB::instance();
  3633. $pathString = " ezcot.path_string like '$childPath%' and ";
  3634. $notEqParentString = "ezcot.node_id != $fromNode";
  3635. $depthCond = '';
  3636. if ( $depth )
  3637. {
  3638. $nodeDepth += $depth;
  3639. if ( isset( $params[ 'DepthOperator' ] ) && $params[ 'DepthOperator' ] == 'eq' )
  3640. {
  3641. $depthCond = ' ezcot.depth = ' . $nodeDepth . '';
  3642. $notEqParentString = '';
  3643. }
  3644. else
  3645. $depthCond = ' ezcot.depth <= ' . $nodeDepth . ' and ';
  3646. }
  3647. $tmpTableName = $db->generateUniqueTempTableName( 'eznode_count_%' );
  3648. $db->createTempTable( "CREATE TEMPORARY TABLE $tmpTableName ( count int )" );
  3649. $query = "INSERT INTO $tmpTableName
  3650. SELECT
  3651. count( ezcot.main_node_id ) AS count
  3652. FROM
  3653. ezcontentobject_tree ezcot,
  3654. ezcontentobject_tree ezcot_all
  3655. WHERE
  3656. $pathString
  3657. $depthCond
  3658. $notEqParentString
  3659. and ezcot.contentobject_id = ezcot_all.contentobject_id
  3660. GROUP BY ezcot_all.main_node_id
  3661. HAVING count( ezcot.main_node_id ) <= 1";
  3662. $db->query( $query, eZDBInterface::SERVER_SLAVE );
  3663. $query = "SELECT count( * ) AS count
  3664. FROM $tmpTableName";
  3665. $rows = $db->arrayQuery( $query, array(), eZDBInterface::SERVER_SLAVE );
  3666. $db->dropTempTable( "DROP TABLE $tmpTableName" );
  3667. return $rows[0]['count'];
  3668. }
  3669. /*!
  3670. Moves the node to the given node.
  3671. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3672. the calls within a db transaction; thus within db->begin and db->commit.
  3673. */
  3674. function move( $newParentNodeID, $nodeID = 0 )
  3675. {
  3676. if ( $nodeID == 0 )
  3677. {
  3678. $node = $this;
  3679. $nodeID = $node->attribute( 'node_id' );
  3680. }
  3681. else
  3682. {
  3683. $node = eZContentObjectTreeNode::fetch( $nodeID );
  3684. }
  3685. $oldPath = $node->attribute( 'path_string' );
  3686. $oldParentNodeID = $node->attribute( 'parent_node_id' );
  3687. $newParentNodeID =(int) $newParentNodeID;
  3688. if ( $oldParentNodeID != $newParentNodeID )
  3689. {
  3690. $node->updateAndStoreModified();
  3691. // Who moves which content should be logged.
  3692. $object = $node->object();
  3693. eZAudit::writeAudit( 'content-move', array( 'Node ID' => $node->attribute( 'node_id' ),
  3694. 'Old parent node ID' => $oldParentNodeID, 'New parent node ID' => $newParentNodeID,
  3695. 'Object ID' => $object->attribute( 'id' ), 'Content Name' => $object->attribute( 'name' ),
  3696. 'Comment' => 'Moved the node to the given node: eZContentObjectTreeNode::move()' ) );
  3697. $newParentNode = eZContentObjectTreeNode::fetch( $newParentNodeID );
  3698. $newParentPath = $newParentNode->attribute( 'path_string' );
  3699. $newParentDepth = $newParentNode->attribute( 'depth' );
  3700. $newPath = $newParentPath . $nodeID;
  3701. $oldDepth = $node->attribute( 'depth' );
  3702. $oldPathLength = strlen( $oldPath );
  3703. $moveQuery = "UPDATE
  3704. ezcontentobject_tree
  3705. SET
  3706. parent_node_id = $newParentNodeID
  3707. WHERE
  3708. node_id = $nodeID";
  3709. $db = eZDB::instance();
  3710. $subStringString = $db->subString( 'path_string', $oldPathLength );
  3711. $newPathString = $db->concatString( array( "'$newPath'", $subStringString ) );
  3712. $moveQuery1 = "UPDATE
  3713. ezcontentobject_tree
  3714. SET
  3715. path_string = $newPathString,
  3716. depth = depth + $newParentDepth - $oldDepth + 1
  3717. WHERE
  3718. path_string LIKE '$oldPath%'";
  3719. $db->begin();
  3720. $db->query( $moveQuery );
  3721. $db->query( $moveQuery1 );
  3722. /// role system clean up
  3723. // Clean up policies and limitations
  3724. $expireRoleCache = false;
  3725. $limitationsToFix = eZPolicyLimitation::findByType( 'SubTree', $node->attribute( 'path_string' ), false );
  3726. if ( count( $limitationsToFix ) > 0 )
  3727. {
  3728. $limitationIDString = $db->generateSQLINStatement( $limitationsToFix, 'limitation_id' );
  3729. $subStringString = $db->subString( 'value', $oldPathLength );
  3730. $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );
  3731. $query = "UPDATE
  3732. ezpolicy_limitation_value
  3733. SET
  3734. value = $newValue
  3735. WHERE
  3736. value LIKE '$oldPath%' AND $limitationIDString";
  3737. $db->query( $query );
  3738. $expireRoleCache = true;
  3739. }
  3740. // clean up limitations on role assignment level
  3741. $countRows = $db->arrayQuery( "SELECT COUNT(*) AS row_count FROM ezuser_role WHERE limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'" );
  3742. $assignmentsToFixCount = $countRows[0]['row_count'];
  3743. if ( $assignmentsToFixCount > 0 )
  3744. {
  3745. $subStringString = $db->subString( 'limit_value', $oldPathLength );
  3746. $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );
  3747. $db->query( "UPDATE
  3748. ezuser_role
  3749. SET
  3750. limit_value = $newValue
  3751. WHERE
  3752. limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'"
  3753. );
  3754. $expireRoleCache = true;
  3755. }
  3756. if ( $expireRoleCache )
  3757. {
  3758. eZRole::expireCache();
  3759. }
  3760. // Update "is_invisible" node attribute.
  3761. $newNode = eZContentObjectTreeNode::fetch( $nodeID );
  3762. eZContentObjectTreeNode::updateNodeVisibility( $newNode, $newParentNode );
  3763. $db->commit();
  3764. }
  3765. }
  3766. function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
  3767. {
  3768. $classID = $originalClassID;
  3769. $user = eZUser::currentUser();
  3770. $userID = $user->attribute( 'contentobject_id' );
  3771. // Fetch the ID of the language if we get a string with a language code
  3772. // e.g. 'eng-GB'
  3773. $originalLanguage = $language;
  3774. if ( is_string( $language ) && strlen( $language ) > 0 )
  3775. {
  3776. $language = eZContentLanguage::idByLocale( $language );
  3777. }
  3778. else
  3779. {
  3780. $language = false;
  3781. }
  3782. // This will be filled in with the available languages of the object
  3783. // if a Language check is performed.
  3784. $languageList = false;
  3785. // This will be filled if parent object is needed.
  3786. $parentObject = false;
  3787. $origFunctionName = $functionName;
  3788. // The 'move' function simply reuses 'edit' for generic access
  3789. // but adds another top-level check below
  3790. // The original function is still available in $origFunctionName
  3791. if ( $functionName == 'move' )
  3792. $functionName = 'edit';
  3793. // Manage locations depends if it's removal or not.
  3794. if ( $functionName == 'can_add_location' ||
  3795. $functionName == 'can_remove_location' )
  3796. {
  3797. $functionName = 'manage_locations';
  3798. }
  3799. $accessResult = $user->hasAccessTo( 'content' , $functionName );
  3800. $accessWord = $accessResult['accessWord'];
  3801. if ( $origFunctionName == 'can_remove_location' )
  3802. {
  3803. if ( $this->ParentNodeID <= 1 )
  3804. {
  3805. return 0;
  3806. }
  3807. $currentNode = eZContentObjectTreeNode::fetch( $this->ParentNodeID );
  3808. if ( !$currentNode instanceof eZContentObjectTreeNode )
  3809. {
  3810. return 0;
  3811. }
  3812. $contentObject = $currentNode->attribute( 'object' );
  3813. }
  3814. else
  3815. {
  3816. $currentNode = $this;
  3817. $contentObject = $this->attribute( 'object' );
  3818. }
  3819. /*
  3820. // Uncomment this part if 'create' permissions should become implied 'edit'.
  3821. // Merges in 'create' policies with 'edit'
  3822. if ( $functionName == 'edit' &&
  3823. !in_array( $accessWord, array( 'yes', 'no' ) ) )
  3824. {
  3825. // Add in create policies.
  3826. $accessExtraResult = $user->hasAccessTo( 'content', 'create' );
  3827. if ( $accessExtraResult['accessWord'] != 'no' )
  3828. {
  3829. $accessWord = $accessExtraResult['accessWord'];
  3830. if ( isset( $accessExtraResult['policies'] ) )
  3831. {
  3832. $accessResult['policies'] = array_merge( $accessResult['policies'],
  3833. $accessExtraResult['policies'] );
  3834. }
  3835. if ( isset( $accessExtraResult['accessList'] ) )
  3836. {
  3837. $accessResult['accessList'] = array_merge( $accessResult['accessList'],
  3838. $accessExtraResult['accessList'] );
  3839. }
  3840. }
  3841. }
  3842. */
  3843. if ( $origFunctionName == 'remove' or
  3844. $origFunctionName == 'move' or
  3845. $origFunctionName == 'can_remove_location' )
  3846. {
  3847. // We do not allow these actions on top-level nodes
  3848. // - remove
  3849. // - move
  3850. if ( $this->ParentNodeID <= 1 )
  3851. {
  3852. return 0;
  3853. }
  3854. }
  3855. if ( $classID === false )
  3856. {
  3857. $classID = $contentObject->attribute( 'contentclass_id' );
  3858. }
  3859. if ( $accessWord == 'yes' )
  3860. {
  3861. return 1;
  3862. }
  3863. else if ( $accessWord == 'no' )
  3864. {
  3865. if ( $functionName == 'edit' )
  3866. {
  3867. // Check if we have 'create' access under the main parent
  3868. $object = $currentNode->object();
  3869. if ( $object && $object->attribute( 'current_version' ) == 1 && !$object->attribute( 'status' ) )
  3870. {
  3871. $mainNode = eZNodeAssignment::fetchForObject( $object->attribute( 'id' ), $object->attribute( 'current_version' ) );
  3872. $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
  3873. $result = $parentObj->checkAccess( 'create', $object->attribute( 'contentclass_id' ),
  3874. $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
  3875. return $result;
  3876. }
  3877. else
  3878. {
  3879. return 0;
  3880. }
  3881. }
  3882. return 0;
  3883. }
  3884. else
  3885. {
  3886. $policies = $accessResult['policies'];
  3887. $access = 'denied';
  3888. foreach ( $policies as $pkey => $limitationArray )
  3889. {
  3890. if ( $access == 'allowed' )
  3891. {
  3892. break;
  3893. }
  3894. $limitationList = array();
  3895. if ( isset( $limitationArray['Subtree' ] ) )
  3896. {
  3897. $checkedSubtree = false;
  3898. }
  3899. else
  3900. {
  3901. $checkedSubtree = true;
  3902. $accessSubtree = false;
  3903. }
  3904. if ( isset( $limitationArray['Node'] ) )
  3905. {
  3906. $checkedNode = false;
  3907. }
  3908. else
  3909. {
  3910. $checkedNode = true;
  3911. $accessNode = false;
  3912. }
  3913. foreach ( $limitationArray as $key => $valueList )
  3914. {
  3915. $access = 'denied';
  3916. switch( $key )
  3917. {
  3918. case 'Class':
  3919. {
  3920. if ( $functionName == 'create' and
  3921. !$originalClassID )
  3922. {
  3923. $access = 'allowed';
  3924. }
  3925. else if ( $functionName == 'create' and
  3926. in_array( $classID, $valueList ) )
  3927. {
  3928. $access = 'allowed';
  3929. }
  3930. else if ( $functionName != 'create' and
  3931. in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) )
  3932. {
  3933. $access = 'allowed';
  3934. }
  3935. else
  3936. {
  3937. $access = 'denied';
  3938. $limitationList = array( 'Limitation' => $key,
  3939. 'Required' => $valueList );
  3940. }
  3941. } break;
  3942. case 'ParentClass':
  3943. {
  3944. if ( in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) )
  3945. {
  3946. $access = 'allowed';
  3947. }
  3948. else
  3949. {
  3950. $access = 'denied';
  3951. $limitationList = array( 'Limitation' => $key,
  3952. 'Required' => $valueList );
  3953. }
  3954. } break;
  3955. case 'Section':
  3956. case 'User_Section':
  3957. {
  3958. if ( in_array( $contentObject->attribute( 'section_id' ), $valueList ) )
  3959. {
  3960. $access = 'allowed';
  3961. }
  3962. else
  3963. {
  3964. $access = 'denied';
  3965. $limitationList = array( 'Limitation' => $key,
  3966. 'Required' => $valueList );
  3967. }
  3968. } break;
  3969. case 'Language':
  3970. {
  3971. $languageMask = 0;
  3972. // If we don't have a language list yet we need to fetch it
  3973. // and optionally filter out based on $language.
  3974. if ( $functionName == 'create' )
  3975. {
  3976. // If the function is 'create' we do not use the language_mask for matching.
  3977. if ( $language !== false )
  3978. {
  3979. $languageMask = $language;
  3980. }
  3981. else
  3982. {
  3983. // If the create is used and no language specified then
  3984. // we need to match against all possible languages (which
  3985. // is all bits set, ie. -1).
  3986. $languageMask = -1;
  3987. }
  3988. }
  3989. else
  3990. {
  3991. if ( $language !== false )
  3992. {
  3993. if ( $languageList === false )
  3994. {
  3995. $languageMask = $contentObject->attribute( 'language_mask' );
  3996. // We are restricting language check to just one language
  3997. $languageMask &= $language;
  3998. // If the resulting mask is 0 it means that the user is trying to
  3999. // edit a language which does not exist, ie. translating.
  4000. // The mask will then become the language trying to edit.
  4001. if ( $languageMask == 0 )
  4002. {
  4003. $languageMask = $language;
  4004. }
  4005. }
  4006. }
  4007. else
  4008. {
  4009. $languageMask = -1;
  4010. }
  4011. }
  4012. // Fetch limit mask for limitation list
  4013. $limitMask = eZContentLanguage::maskByLocale( $valueList );
  4014. if ( ( $languageMask & $limitMask ) != 0 )
  4015. {
  4016. $access = 'allowed';
  4017. }
  4018. else
  4019. {
  4020. $access = 'denied';
  4021. $limitationList = array( 'Limitation' => $key,
  4022. 'Required' => $valueList );
  4023. }
  4024. } break;
  4025. case 'Owner':
  4026. case 'ParentOwner':
  4027. {
  4028. // if limitation value == 2, anonymous limited to current session.
  4029. if ( in_array( 2, $valueList ) &&
  4030. $user->isAnonymous() )
  4031. {
  4032. $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
  4033. if ( $createdObjectIDList &&
  4034. in_array( $contentObject->attribute( 'id' ), unserialize( $createdObjectIDList ) ) )
  4035. {
  4036. $access = 'allowed';
  4037. }
  4038. }
  4039. else if ( $contentObject->attribute( 'owner_id' ) == $userID || $contentObject->attribute( 'id' ) == $userID )
  4040. {
  4041. $access = 'allowed';
  4042. }
  4043. if ( $access != 'allowed' )
  4044. {
  4045. $access = 'denied';
  4046. $limitationList = array ( 'Limitation' => $key );
  4047. }
  4048. } break;
  4049. case 'Group':
  4050. case 'ParentGroup':
  4051. {
  4052. $access = $contentObject->checkGroupLimitationAccess( $valueList, $userID );
  4053. if ( $access != 'allowed' )
  4054. {
  4055. $access = 'denied';
  4056. $limitationList = array ( 'Limitation' => $key,
  4057. 'Required' => $valueList );
  4058. }
  4059. } break;
  4060. case 'State':
  4061. {
  4062. if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 )
  4063. {
  4064. $access = 'denied';
  4065. $limitationList = array ( 'Limitation' => $key,
  4066. 'Required' => $valueList );
  4067. }
  4068. else
  4069. {
  4070. $access = 'allowed';
  4071. }
  4072. } break;
  4073. case 'ParentDepth':
  4074. {
  4075. if ( in_array( $currentNode->attribute( 'depth' ), $valueList ) )
  4076. {
  4077. $access = 'allowed';
  4078. }
  4079. else
  4080. {
  4081. $access = 'denied';
  4082. $limitationList = array( 'Limitation' => $key,
  4083. 'Required' => $valueList );
  4084. }
  4085. } break;
  4086. case 'Node':
  4087. {
  4088. $accessNode = false;
  4089. $mainNodeID = $currentNode->attribute( 'main_node_id' );
  4090. foreach ( $valueList as $nodeID )
  4091. {
  4092. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  4093. $limitationNodeID = $node['main_node_id'];
  4094. if ( $mainNodeID == $limitationNodeID )
  4095. {
  4096. $access = 'allowed';
  4097. $accessNode = true;
  4098. break;
  4099. }
  4100. }
  4101. if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree )
  4102. {
  4103. $access = 'denied';
  4104. // ??? TODO: if there is a limitation on Subtree, return two limitations?
  4105. $limitationList = array( 'Limitation' => $key,
  4106. 'Required' => $valueList );
  4107. }
  4108. else
  4109. {
  4110. $access = 'allowed';
  4111. }
  4112. $checkedNode = true;
  4113. } break;
  4114. case 'Subtree':
  4115. {
  4116. $accessSubtree = false;
  4117. $path = $currentNode->attribute( 'path_string' );
  4118. $subtreeArray = $valueList;
  4119. foreach ( $subtreeArray as $subtreeString )
  4120. {
  4121. if ( strstr( $path, $subtreeString ) )
  4122. {
  4123. $access = 'allowed';
  4124. $accessSubtree = true;
  4125. break;
  4126. }
  4127. }
  4128. if ( $access != 'allowed' && $checkedNode && !$accessNode )
  4129. {
  4130. $access = 'denied';
  4131. // ??? TODO: if there is a limitation on Node, return two limitations?
  4132. $limitationList = array( 'Limitation' => $key,
  4133. 'Required' => $valueList );
  4134. }
  4135. else
  4136. {
  4137. $access = 'allowed';
  4138. }
  4139. $checkedSubtree = true;
  4140. } break;
  4141. case 'User_Subtree':
  4142. {
  4143. $path = $currentNode->attribute( 'path_string' );
  4144. $subtreeArray = $valueList;
  4145. foreach ( $subtreeArray as $subtreeString )
  4146. {
  4147. if ( strstr( $path, $subtreeString ) )
  4148. {
  4149. $access = 'allowed';
  4150. }
  4151. }
  4152. if ( $access != 'allowed' )
  4153. {
  4154. $access = 'denied';
  4155. $limitationList = array( 'Limitation' => $key,
  4156. 'Required' => $valueList );
  4157. }
  4158. } break;
  4159. default:
  4160. {
  4161. if ( strncmp( $key, 'StateGroup_', 11 ) === 0 )
  4162. {
  4163. if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 )
  4164. {
  4165. $access = 'denied';
  4166. $limitationList = array ( 'Limitation' => $key,
  4167. 'Required' => $valueList );
  4168. }
  4169. else
  4170. {
  4171. $access = 'allowed';
  4172. }
  4173. }
  4174. }
  4175. }
  4176. if ( $access == 'denied' )
  4177. {
  4178. break;
  4179. }
  4180. }
  4181. $policyList[] = array( 'PolicyID' => $pkey,
  4182. 'LimitationList' => $limitationList );
  4183. }
  4184. if ( $access == 'denied' )
  4185. {
  4186. $accessList = array( 'FunctionRequired' => array ( 'Module' => 'content',
  4187. 'Function' => $origFunctionName,
  4188. 'ClassID' => $classID,
  4189. 'MainNodeID' => $currentNode->attribute( 'main_node_id' ) ),
  4190. 'PolicyList' => $policyList );
  4191. return 0;
  4192. }
  4193. else
  4194. {
  4195. return 1;
  4196. }
  4197. }
  4198. }
  4199. // code-template::create-block: class-list-from-policy, is-node
  4200. // code-template::auto-generated:START class-list-from-policy
  4201. // This code is automatically generated from templates/classlistfrompolicy.ctpl
  4202. // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
  4203. function classListFromPolicy( $policy, $allowedLanguageCodes = false )
  4204. {
  4205. $canCreateClassIDListPart = array();
  4206. $hasClassIDLimitation = false;
  4207. $user = eZUser::currentUser();
  4208. $userID = $user->attribute( 'contentobject_id' );
  4209. $object = false;
  4210. if ( isset( $policy['ParentOwner'] ) )
  4211. {
  4212. if ( $object === false )
  4213. $object = $this->attribute( 'object' );
  4214. // if limitation value == 2, anonymous limited to current session.
  4215. if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() )
  4216. {
  4217. $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
  4218. if ( !$createdObjectIDList ||
  4219. !in_array( $object->ID, unserialize( $createdObjectIDList ) ) )
  4220. {
  4221. return array();
  4222. }
  4223. }
  4224. else if ( $object->attribute( 'owner_id' ) != $userID &&
  4225. $object->ID != $userID )
  4226. {
  4227. return array();
  4228. }
  4229. }
  4230. if ( isset( $policy['ParentGroup'] ) )
  4231. {
  4232. if ( $object === false )
  4233. $object = $this->attribute( 'object' );
  4234. $access = $object->checkGroupLimitationAccess( $policy['ParentGroup'], $userID );
  4235. if ( $access !== 'allowed' )
  4236. {
  4237. return array();
  4238. }
  4239. }
  4240. if ( isset( $policy['Class'] ) )
  4241. {
  4242. $canCreateClassIDListPart = $policy['Class'];
  4243. $hasClassIDLimitation = true;
  4244. }
  4245. if ( isset( $policy['User_Section'] ) )
  4246. {
  4247. if ( $object === false )
  4248. $object = $this->attribute( 'object' );
  4249. if ( !in_array( $object->attribute( 'section_id' ), $policy['User_Section'] ) )
  4250. {
  4251. return array();
  4252. }
  4253. }
  4254. if ( isset( $policy['User_Subtree'] ) )
  4255. {
  4256. $allowed = false;
  4257. if ( $object === false )
  4258. $object = $this->attribute( 'object' );
  4259. $assignedNodes = $object->attribute( 'assigned_nodes' );
  4260. foreach ( $assignedNodes as $assignedNode )
  4261. {
  4262. $path = $assignedNode->attribute( 'path_string' );
  4263. foreach ( $policy['User_Subtree'] as $subtreeString )
  4264. {
  4265. if ( strstr( $path, $subtreeString ) )
  4266. {
  4267. $allowed = true;
  4268. break;
  4269. }
  4270. }
  4271. }
  4272. if( !$allowed )
  4273. {
  4274. return array();
  4275. }
  4276. }
  4277. if ( isset( $policy['Section'] ) )
  4278. {
  4279. if ( $object === false )
  4280. $object = $this->attribute( 'object' );
  4281. if ( !in_array( $object->attribute( 'section_id' ), $policy['Section'] ) )
  4282. {
  4283. return array();
  4284. }
  4285. }
  4286. if ( isset( $policy['ParentClass'] ) )
  4287. {
  4288. if ( $object === false )
  4289. $object = $this->attribute( 'object' );
  4290. if ( !in_array( $object->attribute( 'contentclass_id' ), $policy['ParentClass'] ) )
  4291. {
  4292. return array();
  4293. }
  4294. }
  4295. if ( isset( $policy['ParentDepth'] ) && is_array( $policy['ParentDepth'] ) )
  4296. {
  4297. $NodeDepth = $this->attribute( 'depth' );
  4298. if ( !in_array( '*', $policy['ParentDepth'] ) && !in_array( $NodeDepth, $policy['ParentDepth'] ) )
  4299. {
  4300. return array();
  4301. }
  4302. }
  4303. if ( isset( $policy['Assigned'] ) )
  4304. {
  4305. if ( $object === false )
  4306. $object = $this->attribute( 'object' );
  4307. if ( $object->attribute( 'owner_id' ) != $userID )
  4308. {
  4309. return array();
  4310. }
  4311. }
  4312. $allowedNode = false;
  4313. if ( isset( $policy['Node'] ) )
  4314. {
  4315. $allowed = false;
  4316. foreach( $policy['Node'] as $nodeID )
  4317. {
  4318. $mainNodeID = $this->attribute( 'main_node_id' );
  4319. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  4320. if ( $mainNodeID == $node['main_node_id'] )
  4321. {
  4322. $allowed = true;
  4323. $allowedNode = true;
  4324. break;
  4325. }
  4326. }
  4327. if ( !$allowed && !isset( $policy['Subtree'] ) )
  4328. {
  4329. return array();
  4330. }
  4331. }
  4332. if ( isset( $policy['Subtree'] ) )
  4333. {
  4334. $allowed = false;
  4335. if ( $object === false )
  4336. $object = $this->attribute( 'object' );
  4337. $assignedNodes = $object->attribute( 'assigned_nodes' );
  4338. foreach ( $assignedNodes as $assignedNode )
  4339. {
  4340. $path = $assignedNode->attribute( 'path_string' );
  4341. foreach ( $policy['Subtree'] as $subtreeString )
  4342. {
  4343. if ( strstr( $path, $subtreeString ) )
  4344. {
  4345. $allowed = true;
  4346. break;
  4347. }
  4348. }
  4349. }
  4350. if ( !$allowed && !$allowedNode )
  4351. {
  4352. return array();
  4353. }
  4354. }
  4355. if ( isset( $policy['Language'] ) )
  4356. {
  4357. if ( $allowedLanguageCodes )
  4358. {
  4359. $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] );
  4360. }
  4361. else
  4362. {
  4363. $allowedLanguageCodes = $policy['Language'];
  4364. }
  4365. }
  4366. if ( $hasClassIDLimitation )
  4367. {
  4368. return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes );
  4369. }
  4370. return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes );
  4371. }
  4372. // This code is automatically generated from templates/classlistfrompolicy.ctpl
  4373. // code-template::auto-generated:END class-list-from-policy
  4374. // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation
  4375. // code-template::auto-generated:START can-instantiate-class-list
  4376. // This code is automatically generated from templates/classcreatelist.ctpl
  4377. // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
  4378. /*!
  4379. Finds all classes that the current user can create objects from and returns.
  4380. It is also possible to filter the list event more with \a $includeFilter and \a $groupList.
  4381. \param $asObject If \c true then it return eZContentClass objects, if not it will
  4382. be an associative array with \c name and \c id keys.
  4383. \param $includeFilter If \c true then it will include only from class groups defined in
  4384. \a $groupList, if not it will exclude those groups.
  4385. \param $groupList An array with class group IDs that should be used in filtering, use
  4386. \c false if you do not wish to filter at all.
  4387. \param $fetchID A unique name for the current fetch, this must be supplied when filtering is
  4388. used if you want caching to work.
  4389. */
  4390. function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false )
  4391. {
  4392. $ini = eZINI::instance();
  4393. $groupArray = array();
  4394. $languageCodeList = eZContentLanguage::fetchLocaleList();
  4395. $allowedLanguages = array( '*' => array() );
  4396. $user = eZUser::currentUser();
  4397. $accessResult = $user->hasAccessTo( 'content' , 'create' );
  4398. $accessWord = $accessResult['accessWord'];
  4399. $classIDArray = array();
  4400. $classList = array();
  4401. $fetchAll = false;
  4402. if ( $accessWord == 'yes' )
  4403. {
  4404. $fetchAll = true;
  4405. $allowedLanguages['*'] = $languageCodeList;
  4406. }
  4407. else if ( $accessWord == 'no' )
  4408. {
  4409. // Cannot create any objects, return empty list.
  4410. return $classList;
  4411. }
  4412. else
  4413. {
  4414. $policies = $accessResult['policies'];
  4415. foreach ( $policies as $policyKey => $policy )
  4416. {
  4417. $policyArray = $this->classListFromPolicy( $policy, $languageCodeList );
  4418. if ( count( $policyArray ) == 0 )
  4419. {
  4420. continue;
  4421. }
  4422. $classIDArrayPart = $policyArray['classes'];
  4423. $languageCodeArrayPart = $policyArray['language_codes'];
  4424. if ( $classIDArrayPart == '*' )
  4425. {
  4426. $fetchAll = true;
  4427. $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) );
  4428. }
  4429. else
  4430. {
  4431. foreach( $classIDArrayPart as $class )
  4432. {
  4433. if ( isset( $allowedLanguages[$class] ) )
  4434. {
  4435. $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) );
  4436. }
  4437. else
  4438. {
  4439. $allowedLanguages[$class] = $languageCodeArrayPart;
  4440. }
  4441. }
  4442. $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) );
  4443. }
  4444. }
  4445. }
  4446. $db = eZDB::instance();
  4447. $filterTableSQL = '';
  4448. $filterSQL = '';
  4449. // Create extra SQL statements for the class group filters.
  4450. if ( is_array( $groupList ) )
  4451. {
  4452. if ( count( $groupList ) == 0 )
  4453. {
  4454. return $classList;
  4455. }
  4456. $filterTableSQL = ', ezcontentclass_classgroup ccg';
  4457. $filterSQL = ( " AND" .
  4458. " cc.id = ccg.contentclass_id AND" .
  4459. " " );
  4460. $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' );
  4461. }
  4462. $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
  4463. if ( $fetchAll )
  4464. {
  4465. // If $asObject is true we fetch all fields in class
  4466. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  4467. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  4468. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  4469. "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
  4470. "ORDER BY $classNameFilter[nameField] ASC" );
  4471. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  4472. }
  4473. else
  4474. {
  4475. // If the constrained class list is empty we are not allowed to create any class
  4476. if ( count( $classIDArray ) == 0 )
  4477. {
  4478. return $classList;
  4479. }
  4480. $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' );
  4481. // If $asObject is true we fetch all fields in class
  4482. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  4483. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  4484. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  4485. "WHERE $classIDCondition AND" .
  4486. " cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
  4487. "ORDER BY $classNameFilter[nameField] ASC" );
  4488. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  4489. }
  4490. if ( $asObject )
  4491. {
  4492. foreach ( $classList as $key => $class )
  4493. {
  4494. $id = $class->attribute( 'id' );
  4495. if ( isset( $allowedLanguages[$id] ) )
  4496. {
  4497. $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) );
  4498. }
  4499. else
  4500. {
  4501. $languageCodes = $allowedLanguages['*'];
  4502. }
  4503. $classList[$key]->setCanInstantiateLanguages( $languageCodes );
  4504. }
  4505. }
  4506. eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" );
  4507. return $classList;
  4508. }
  4509. // This code is automatically generated from templates/classcreatelist.ctpl
  4510. // code-template::auto-generated:END can-instantiate-class-list
  4511. static function makeObjectsArray( $array , $with_contentobject = true )
  4512. {
  4513. $retNodes = array();
  4514. if ( !is_array( $array ) )
  4515. return $retNodes;
  4516. foreach ( $array as $node )
  4517. {
  4518. unset( $object );
  4519. if( $node['node_id'] == 1 )
  4520. {
  4521. if( !array_key_exists( 'name', $node ) || !$node['name'] )
  4522. $node['name'] = ezpI18n::tr( 'kernel/content', 'Top Level Nodes' );
  4523. }
  4524. $object = new eZContentObjectTreeNode( $node );
  4525. // If the name is not set it will be fetched later on when
  4526. // getName()/attribute( 'name' ) is accessed.
  4527. if ( isset( $node['name'] ) )
  4528. {
  4529. $object->setName( $node['name'] );
  4530. }
  4531. if ( isset( $node['class_serialized_name_list'] ) )
  4532. {
  4533. $node['class_name'] = eZContentClass::nameFromSerializedString( $node['class_serialized_name_list'] );
  4534. $object->ClassName = $node['class_name'];
  4535. }
  4536. if ( isset( $node['class_identifier'] ) )
  4537. $object->ClassIdentifier = $node['class_identifier'];
  4538. if ( isset( $node['is_container'] ) )
  4539. $object->ClassIsContainer = $node['is_container'];
  4540. if ( $with_contentobject )
  4541. {
  4542. if ( isset( $node['class_name'] ) )
  4543. {
  4544. unset( $node['remote_id'] );
  4545. $contentObject = new eZContentObject( $node );
  4546. $permissions = array();
  4547. $contentObject->setPermissions( $permissions );
  4548. $contentObject->setClassName( $node['class_name'] );
  4549. if ( isset( $node['class_identifier'] ) )
  4550. $contentObject->ClassIdentifier = $node['class_identifier'];
  4551. }
  4552. else
  4553. {
  4554. $contentObject = new eZContentObject( array());
  4555. if ( isset( $node['name'] ) )
  4556. $contentObject->setCachedName( $node['name'] );
  4557. }
  4558. if ( isset( $node['real_translation'] ) && $node['real_translation'] != '' )
  4559. {
  4560. $object->CurrentLanguage = $node['real_translation'];
  4561. $contentObject->CurrentLanguage = $node['real_translation'];
  4562. }
  4563. if ( $node['node_id'] == 1 )
  4564. {
  4565. $contentObject->ClassName = 'Folder';
  4566. $contentObject->ClassIdentifier = 'folder';
  4567. $contentObject->ClassID = 1;
  4568. $contentObject->SectionID = 1;
  4569. }
  4570. $object->setContentObject( $contentObject );
  4571. }
  4572. $retNodes[] = $object;
  4573. }
  4574. return $retNodes;
  4575. }
  4576. /*!
  4577. Get parent node id by node id
  4578. \param $nodeID the node id you want parent node id for.
  4579. */
  4580. static function getParentNodeId( $nodeID )
  4581. {
  4582. if ( !isset( $nodeID ) )
  4583. return null;
  4584. $db = eZDB::instance();
  4585. $nodeID =(int) $nodeID;
  4586. $parentArr = $db->arrayQuery( "SELECT
  4587. parent_node_id
  4588. FROM
  4589. ezcontentobject_tree
  4590. WHERE
  4591. node_id = $nodeID");
  4592. return $parentArr[0]['parent_node_id'];
  4593. }
  4594. /**
  4595. * Get parent node id's by content object id's.
  4596. *
  4597. * @static
  4598. * @since Version 4.1
  4599. * @param int|array $objectIDs
  4600. * @param bool $groupByObjectId groups parent node ids by object id they belong to.
  4601. * @param bool $onlyMainNode limits result to parent node id of main node.
  4602. *
  4603. * @return array Returns array of parent node id's
  4604. */
  4605. static function getParentNodeIdListByContentObjectID( $objectIDs, $groupByObjectId = false, $onlyMainNode = false )
  4606. {
  4607. if ( !$objectIDs )
  4608. return null;
  4609. if ( !is_array( $objectIDs ) )
  4610. $objectIDs = array( $objectIDs );
  4611. $db = eZDB::instance();
  4612. $query = 'SELECT
  4613. parent_node_id, contentobject_id
  4614. FROM
  4615. ezcontentobject_tree
  4616. WHERE ' . $db->generateSQLINStatement( $objectIDs, 'contentobject_id', false, false, 'int' );
  4617. if ( $onlyMainNode )
  4618. {
  4619. $query .= ' AND node_id = main_node_id';
  4620. }
  4621. $list = $db->arrayQuery( $query );
  4622. $parentNodeIDs = array();
  4623. if ( $groupByObjectId )
  4624. {
  4625. foreach( $list as $item )
  4626. {
  4627. $objectID = $item['contentobject_id'];
  4628. if ( !isset( $parentNodeIDs[$objectID] ) )
  4629. {
  4630. $parentNodeIDs[$objectID] = array();
  4631. }
  4632. $parentNodeIDs[$objectID][] = $item['parent_node_id'];
  4633. }
  4634. }
  4635. else
  4636. {
  4637. foreach( $list as $item )
  4638. {
  4639. $parentNodeIDs[] = $item['parent_node_id'];
  4640. }
  4641. }
  4642. return $parentNodeIDs;
  4643. }
  4644. /*!
  4645. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  4646. the calls within a db transaction; thus within db->begin and db->commit.
  4647. */
  4648. static function deleteNodeWhereParent( $node, $id )
  4649. {
  4650. eZContentObjectTreeNode::removeNode( eZContentObjectTreeNode::findNode( $node, $id ) );
  4651. }
  4652. static function findNode( $parentNode, $id, $asObject = false, $remoteID = false )
  4653. {
  4654. if ( !isset( $parentNode) || $parentNode == NULL )
  4655. {
  4656. $parentNode = 2;
  4657. }
  4658. $parentNode =(int) $parentNode;
  4659. $db = eZDB::instance();
  4660. if( $asObject )
  4661. {
  4662. if ( $remoteID )
  4663. {
  4664. $objectIDFilter = 'ezcontentobject.remote_id = ' . (string) $id;
  4665. }
  4666. else
  4667. {
  4668. $objectIDFilter = 'contentobject_id = ' . (int) $id;
  4669. }
  4670. $query="SELECT ezcontentobject.*,
  4671. ezcontentobject_tree.*,
  4672. ezcontentclass.serialized_name_list as class_serialized_name_list,
  4673. ezcontentclass.identifier as class_identifier,
  4674. ezcontentclass.is_container as is_container
  4675. FROM ezcontentobject_tree,
  4676. ezcontentobject,
  4677. ezcontentclass
  4678. WHERE parent_node_id = $parentNode AND
  4679. $objectIDFilter AND
  4680. ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
  4681. ezcontentclass.version=0 AND
  4682. ezcontentclass.id = ezcontentobject.contentclass_id ";
  4683. $nodeListArray = $db->arrayQuery( $query );
  4684. $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
  4685. if ( count( $retNodeArray ) > 0 )
  4686. {
  4687. return $retNodeArray[0];
  4688. }
  4689. else
  4690. {
  4691. return null;
  4692. }
  4693. }
  4694. else
  4695. {
  4696. $id = (int) $id;
  4697. $getNodeQuery = "SELECT node_id
  4698. FROM ezcontentobject_tree
  4699. WHERE
  4700. parent_node_id=$parentNode AND
  4701. contentobject_id = $id ";
  4702. $nodeArr = $db->arrayQuery( $getNodeQuery );
  4703. if ( isset( $nodeArr[0] ) )
  4704. {
  4705. return $nodeArr[0]['node_id'];
  4706. }
  4707. else
  4708. {
  4709. return false;
  4710. }
  4711. }
  4712. }
  4713. function getName( $language = false )
  4714. {
  4715. // If the name is not set yet we fetch it from the object table
  4716. if ( $this->Name === null || $language !== false )
  4717. {
  4718. if ( $this->CurrentLanguage || $language !== false )
  4719. {
  4720. $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 ) . "'";
  4721. }
  4722. else
  4723. {
  4724. $sql = "SELECT name FROM ezcontentobject WHERE id=" . (int) $this->ContentObjectID;
  4725. }
  4726. $db = eZDB::instance();
  4727. $rows = $db->arrayQuery( $sql );
  4728. if ( count( $rows ) > 0 )
  4729. {
  4730. if ( $language !== false )
  4731. {
  4732. return $rows[0]['name'];
  4733. }
  4734. $this->Name = $rows[0]['name'];
  4735. }
  4736. else
  4737. {
  4738. if ( $language !== false )
  4739. {
  4740. return false;
  4741. }
  4742. $this->Name = false;
  4743. }
  4744. }
  4745. return $this->Name;
  4746. }
  4747. function setName( $name )
  4748. {
  4749. $this->Name = $name;
  4750. }
  4751. /*!
  4752. \static
  4753. Creates propper nodeassigment from contentNodeDOMNode specification
  4754. \param contentobjecttreenode DOMNode
  4755. \param contentobject.
  4756. \param version
  4757. \param isMain
  4758. \param options
  4759. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  4760. the calls within a db transaction; thus within db->begin and db->commit.
  4761. */
  4762. static function unserialize( $contentNodeDOMNode, $contentObject, $version, $isMain, &$nodeList, &$options, $handlerType = 'ezcontentobject' )
  4763. {
  4764. $parentNodeID = -1;
  4765. $remoteID = $contentNodeDOMNode->getAttribute( 'remote-id' );
  4766. $parentNodeRemoteID = $contentNodeDOMNode->getAttribute( 'parent-node-remote-id' );
  4767. $node = eZContentObjectTreeNode::fetchByRemoteID( $remoteID );
  4768. if ( is_object( $node ) )
  4769. {
  4770. $description = "Node with remote ID $remoteID already exists.";
  4771. $choosenAction = eZPackageHandler::errorChoosenAction( eZContentObject::PACKAGE_ERROR_EXISTS,
  4772. $options, $description, $handlerType, false );
  4773. switch( $choosenAction )
  4774. {
  4775. // In case user have choosen "Keep existing object and create new"
  4776. case eZContentObject::PACKAGE_NEW:
  4777. {
  4778. $newRemoteID = eZRemoteIdUtility::generate( 'node' );
  4779. $node->setAttribute( 'remote_id', $newRemoteID );
  4780. $node->store();
  4781. $nodeInfo = array( 'contentobject_id' => $node->attribute( 'contentobject_id' ),
  4782. 'contentobject_version' => $node->attribute( 'contentobject_version' ),
  4783. 'parent_remote_id' => $remoteID );
  4784. $nodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(),
  4785. null,
  4786. $nodeInfo );
  4787. if ( is_object( $nodeAssignment ) )
  4788. {
  4789. $nodeAssignment->setAttribute( 'parent_remote_id', $newRemoteID );
  4790. $nodeAssignment->store();
  4791. }
  4792. } break;
  4793. // When running non-interactively with ezpm.php
  4794. case eZPackage::NON_INTERACTIVE:
  4795. case eZContentObject::PACKAGE_UPDATE:
  4796. {
  4797. // Update existing node settigns.
  4798. if ( !$parentNodeRemoteID )
  4799. {
  4800. // when top node of subtree export, only update node sort field and sort order
  4801. $node->setAttribute( 'sort_field', eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ) );
  4802. $node->setAttribute( 'sort_order', $contentNodeDOMNode->getAttribute( 'sort-order' ) );
  4803. $node->store();
  4804. return true;
  4805. }
  4806. } break;
  4807. default:
  4808. {
  4809. // This error may occur only if data integrity is broken
  4810. $options['error'] = array( 'error_code' => eZContentObject::PACKAGE_ERROR_NODE_EXISTS,
  4811. 'element_id' => $remoteID,
  4812. 'description' => $description );
  4813. return false;
  4814. } break;
  4815. }
  4816. }
  4817. if ( $parentNodeRemoteID )
  4818. {
  4819. $parentNode = eZContentObjectTreeNode::fetchByRemoteID( $parentNodeRemoteID );
  4820. if ( $parentNode !== null )
  4821. {
  4822. $parentNodeID = $parentNode->attribute( 'node_id' );
  4823. }
  4824. }
  4825. else
  4826. {
  4827. if ( isset( $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id'] ) )
  4828. {
  4829. $parentNodeID = $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id'];
  4830. }
  4831. else if ( isset( $options['top_nodes_map']['*'] ) )
  4832. {
  4833. $parentNodeID = $options['top_nodes_map']['*'];
  4834. }
  4835. else
  4836. {
  4837. eZDebug::writeError( 'New parent node not set ' . $contentNodeDOMNode->getAttribute( 'name' ), __METHOD__ );
  4838. }
  4839. }
  4840. $isMain = ( $isMain && $contentNodeDOMNode->getAttribute( 'is-main-node' ) );
  4841. $nodeInfo = array( 'contentobject_id' => $contentObject->attribute( 'id' ),
  4842. 'contentobject_version' => $version,
  4843. 'is_main' => $isMain,
  4844. 'parent_node' => $parentNodeID,
  4845. 'parent_remote_id' => $remoteID, // meaning processed node remoteID (not parent)
  4846. 'sort_field' => eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ),
  4847. 'sort_order' => $contentNodeDOMNode->getAttribute( 'sort-order' ) );
  4848. if ( $parentNodeID == -1 && $parentNodeRemoteID )
  4849. {
  4850. if ( !isset( $options['suspended-nodes'] ) )
  4851. {
  4852. $options['suspended-nodes'] = array();
  4853. }
  4854. $options['suspended-nodes'][$parentNodeRemoteID] = array( 'nodeinfo' => $nodeInfo,
  4855. 'priority' => $contentNodeDOMNode->getAttribute( 'priority' ) );
  4856. return true;
  4857. }
  4858. $existNodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(),
  4859. null,
  4860. $nodeInfo );
  4861. $nodeInfo['priority'] = $contentNodeDOMNode->getAttribute( 'priority' );
  4862. if( !is_object( $existNodeAssignment ) )
  4863. {
  4864. $nodeAssignment = eZNodeAssignment::create( $nodeInfo );
  4865. $nodeList[] = $nodeInfo;
  4866. $nodeAssignment->store();
  4867. }
  4868. return true;
  4869. }
  4870. /*!
  4871. Serialize ContentObjectTreeNode
  4872. \params $options
  4873. \params contentNodeIDArray
  4874. \params topNodeIDArray
  4875. */
  4876. function serialize( $options, $contentNodeIDArray, $topNodeIDArray )
  4877. {
  4878. if ( $options['node_assignment'] == 'main' &&
  4879. $this->attribute( 'main_node_id' ) != $this->attribute( 'node_id' ) )
  4880. {
  4881. return false;
  4882. }
  4883. if ( ! in_array( $this->attribute( 'node_id' ), array_keys( $contentNodeIDArray ) ) )
  4884. {
  4885. return false;
  4886. }
  4887. $dom = new DOMDocument( '1.0', 'utf-8' );
  4888. $nodeAssignmentNode = $dom->createElement( 'node-assignment' );
  4889. if ( $this->attribute( 'main_node_id' ) == $this->attribute( 'node_id' ) )
  4890. {
  4891. $nodeAssignmentNode->setAttribute( 'is-main-node', 1 );
  4892. }
  4893. if( !in_array( $this->attribute( 'node_id'), $topNodeIDArray ) )
  4894. {
  4895. $parentNode = $this->attribute( 'parent' );
  4896. $nodeAssignmentNode->setAttribute( 'parent-node-remote-id', $parentNode->attribute( 'remote_id' ) );
  4897. }
  4898. $nodeAssignmentNode->setAttribute( 'name', $this->attribute( 'name' ) );
  4899. $nodeAssignmentNode->setAttribute( 'node-id', $this->attribute( 'node_id' ) );
  4900. $nodeAssignmentNode->setAttribute( 'remote-id', $this->attribute( 'remote_id' ) );
  4901. $nodeAssignmentNode->setAttribute( 'sort-field', eZContentObjectTreeNode::sortFieldName( $this->attribute( 'sort_field' ) ) );
  4902. $nodeAssignmentNode->setAttribute( 'sort-order', $this->attribute( 'sort_order' ) );
  4903. $nodeAssignmentNode->setAttribute( 'priority', $this->attribute( 'priority' ) );
  4904. return $nodeAssignmentNode;
  4905. }
  4906. /*!
  4907. Update and store modified_subnode value for this node and all super nodes.
  4908. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  4909. the calls within a db transaction; thus within db->begin and db->commit.
  4910. */
  4911. function updateAndStoreModified()
  4912. {
  4913. $pathString = trim( $this->attribute( 'path_string' ), '/' );
  4914. // during publishing, a temporary path is generated. The update query shouldn't be executed as it doesn't affect anything
  4915. if ( $pathString == 'TEMPPATH' )
  4916. return;
  4917. $pathArray = explode( '/', $pathString );
  4918. if ( count( $pathArray ) > 0 )
  4919. {
  4920. $db = eZDB::instance();
  4921. $db->query( 'UPDATE ezcontentobject_tree SET modified_subnode=' . time() .
  4922. ' WHERE ' . $db->generateSQLINStatement( $pathArray, 'node_id', false, true, 'int' ) );
  4923. }
  4924. }
  4925. /*!
  4926. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  4927. the calls within a db transaction; thus within db->begin and db->commit.
  4928. */
  4929. function store( $fieldFilters = null )
  4930. {
  4931. $db = eZDB::instance();
  4932. $db->begin();
  4933. eZPersistentObject::store( $fieldFilters );
  4934. $this->updateAndStoreModified();
  4935. $db->commit();
  4936. }
  4937. function object()
  4938. {
  4939. if ( $this->hasContentObject() )
  4940. {
  4941. return $this->ContentObject;
  4942. }
  4943. $contentobject_id = $this->attribute( 'contentobject_id' );
  4944. $obj = eZContentObject::fetch( $contentobject_id );
  4945. $obj->setCurrentLanguage( $this->CurrentLanguage );
  4946. $this->ContentObject = $obj;
  4947. return $obj;
  4948. }
  4949. function hasContentObject()
  4950. {
  4951. if ( isset( $this->ContentObject ) && $this->ContentObject instanceof eZContentObject )
  4952. return true;
  4953. else
  4954. return false;
  4955. }
  4956. /*!
  4957. Sets the current content object for this node.
  4958. */
  4959. function setContentObject( $object )
  4960. {
  4961. $this->ContentObject = $object;
  4962. }
  4963. /*!
  4964. \return the creator of the version published in the node.
  4965. */
  4966. function creator()
  4967. {
  4968. $db = eZDB::instance();
  4969. $query = "SELECT creator_id
  4970. FROM ezcontentobject_version
  4971. WHERE
  4972. contentobject_id = '$this->ContentObjectID' AND
  4973. version = '$this->ContentObjectVersion' ";
  4974. $creatorArray = $db->arrayQuery( $query );
  4975. return eZContentObject::fetch( $creatorArray[0]['creator_id'] );
  4976. }
  4977. function contentObjectVersionObject( $asObject = true )
  4978. {
  4979. $version = eZContentObjectVersion::fetchVersion( $this->ContentObjectVersion, $this->ContentObjectID, $asObject );
  4980. if ( $this->CurrentLanguage != false )
  4981. {
  4982. $version->CurrentLanguage = $this->CurrentLanguage;
  4983. }
  4984. return $version;
  4985. }
  4986. function urlAlias()
  4987. {
  4988. $useURLAlias =& $GLOBALS['eZContentObjectTreeNodeUseURLAlias'];
  4989. $ini = eZINI::instance();
  4990. $cleanURL = '';
  4991. if ( !isset( $useURLAlias ) )
  4992. {
  4993. $useURLAlias = $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled';
  4994. }
  4995. if ( $useURLAlias )
  4996. {
  4997. $path = $this->pathWithNames();
  4998. if ( $ini->hasVariable( 'SiteAccessSettings', 'PathPrefix' ) &&
  4999. $ini->variable( 'SiteAccessSettings', 'PathPrefix' ) != '' )
  5000. {
  5001. $prepend = $ini->variable( 'SiteAccessSettings', 'PathPrefix' );
  5002. $pathIdenStr = substr( $prepend, strlen( $prepend ) -1 ) == '/'
  5003. ? $path . '/'
  5004. : $path;
  5005. if ( strncasecmp( $pathIdenStr, $prepend, strlen( $prepend ) ) == 0 )
  5006. $cleanURL = eZURLAliasML::cleanURL( substr( $path, strlen( $prepend ) ) );
  5007. else
  5008. $cleanURL = eZURLAliasML::cleanURL( $path );
  5009. }
  5010. else
  5011. {
  5012. $cleanURL = eZURLAliasML::cleanURL( $path );
  5013. }
  5014. }
  5015. else
  5016. {
  5017. $cleanURL = eZURLAliasML::cleanURL( 'content/view/full/' . $this->NodeID );
  5018. }
  5019. return $cleanURL;
  5020. }
  5021. function url()
  5022. {
  5023. $ini = eZINI::instance();
  5024. if ( $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled' )
  5025. {
  5026. return $this->urlAlias();
  5027. }
  5028. return 'content/view/full/' . $this->NodeID;
  5029. }
  5030. /*!
  5031. \return the cached value of the class identifier if it exists, if not it's fetched dynamically
  5032. */
  5033. public function classIdentifier()
  5034. {
  5035. if ( $this->ClassIdentifier === null )
  5036. {
  5037. $object = $this->object();
  5038. $this->ClassIdentifier = $object->contentClassIdentifier();
  5039. }
  5040. return $this->ClassIdentifier;
  5041. }
  5042. /*!
  5043. \return the cached value of the class name if it exists, if not it's fetched dynamically
  5044. */
  5045. public function className()
  5046. {
  5047. if ( $this->ClassName === null )
  5048. {
  5049. $object = $this->object();
  5050. $class = $object->contentClass();
  5051. $this->ClassName = $class->attribute( 'name' );
  5052. }
  5053. return $this->ClassName;
  5054. }
  5055. /*!
  5056. \return the cached value of the class is_container flag if it exists, if not it's fetched dynamically
  5057. */
  5058. public function classIsContainer()
  5059. {
  5060. if ( $this->ClassIsContainer === null )
  5061. {
  5062. $object = $this->object();
  5063. $class = $object->contentClass();
  5064. $this->ClassIsContainer = $class->attribute( 'is_container' );
  5065. }
  5066. return $this->ClassIsContainer;
  5067. }
  5068. /*!
  5069. \return combined string representation of both "is_hidden" and "is_invisible" attributes
  5070. Used in the node view templates.
  5071. FIXME: this method probably should be removed in the future.
  5072. */
  5073. function hiddenInvisibleString()
  5074. {
  5075. return ( $this->IsHidden ? 'H' : '-' ) . '/' . ( $this->IsInvisible ? 'X' : '-' );
  5076. }
  5077. /*!
  5078. \return combined string representation of both "is_hidden" and "is_invisible" attributes
  5079. Used in the limitation handling templates.
  5080. */
  5081. function hiddenStatusString()
  5082. {
  5083. if( $this->IsHidden )
  5084. {
  5085. return ezpI18n::tr( 'kernel/content', 'Hidden' );
  5086. }
  5087. else if( $this->IsInvisible )
  5088. {
  5089. return ezpI18n::tr( 'kernel/content', 'Hidden by superior' );
  5090. }
  5091. return ezpI18n::tr( 'kernel/content', 'Visible' );
  5092. }
  5093. /*!
  5094. \a static
  5095. \param $node Root node of the subtree
  5096. \param $modifyRootNode Whether to modify the root node (true/false)
  5097. Hide algorithm:
  5098. if ( root node of the subtree is visible )
  5099. {
  5100. 1) Mark root node as hidden and invisible
  5101. 2) Recursively mark child nodes as invisible except for ones which have been previously marked as invisible
  5102. }
  5103. else
  5104. {
  5105. Mark root node as hidden
  5106. }
  5107. In some cases we don't want to touch the root node when (un)hiding a subtree, for example
  5108. after content/move or content/copy.
  5109. That's why $modifyRootNode argument is used.
  5110. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5111. the calls within a db transaction; thus within db->begin and db->commit.
  5112. */
  5113. static function hideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true )
  5114. {
  5115. $nodeID = $node->attribute( 'node_id' );
  5116. $time = time();
  5117. $db = eZDB::instance();
  5118. $db->begin();
  5119. if ( !$node->attribute( 'is_invisible' ) ) // if root node is visible
  5120. {
  5121. // 1) Mark root node as hidden and invisible.
  5122. if ( $modifyRootNode )
  5123. $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, is_invisible=1, modified_subnode=$time WHERE node_id=$nodeID" );
  5124. // 2) Recursively mark child nodes as invisible, except for ones which have been previously marked as invisible.
  5125. $nodePath = $node->attribute( 'path_string' );
  5126. $db->query( "UPDATE ezcontentobject_tree SET is_invisible=1, modified_subnode=$time WHERE is_invisible=0 AND path_string LIKE '$nodePath%'" );
  5127. }
  5128. else
  5129. {
  5130. // Mark root node as hidden
  5131. if ( $modifyRootNode )
  5132. $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, modified_subnode=$time WHERE node_id=$nodeID" );
  5133. }
  5134. $node->updateAndStoreModified();
  5135. $db->commit();
  5136. eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode );
  5137. }
  5138. /*!
  5139. \a static
  5140. \param $node Root node of the subtree
  5141. \param $modifyRootNode Whether to modify the root node (true/false)
  5142. Unhide algorithm:
  5143. if ( parent node is visible )
  5144. {
  5145. 1) Mark root node as not hidden and visible.
  5146. 2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children).
  5147. }
  5148. else
  5149. {
  5150. Mark root node as not hidden.
  5151. }
  5152. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5153. the calls within a db transaction; thus within db->begin and db->commit.
  5154. */
  5155. static function unhideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true )
  5156. {
  5157. $nodeID = $node->attribute( 'node_id' );
  5158. $nodePath = $node->attribute( 'path_string' );
  5159. $nodeInvisible = $node->attribute( 'is_invisible' );
  5160. $parentNode = $node->attribute( 'parent' );
  5161. $time = time();
  5162. $db = eZDB::instance();
  5163. $db->begin();
  5164. if ( ! $parentNode->attribute( 'is_invisible' ) ) // if parent node is visible
  5165. {
  5166. // 1) Mark root node as not hidden and visible.
  5167. if ( $modifyRootNode )
  5168. $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" );
  5169. // 2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children).
  5170. // 2.1) $hiddenChildren = Fetch all hidden children for the root node
  5171. $hiddenChildren = $db->arrayQuery( "SELECT path_string FROM ezcontentobject_tree " .
  5172. "WHERE node_id <> $nodeID AND is_hidden=1 AND path_string LIKE '$nodePath%'" );
  5173. $skipSubtreesString = '';
  5174. foreach ( $hiddenChildren as $i )
  5175. $skipSubtreesString .= " AND path_string NOT LIKE '" . $i['path_string'] . "%'";
  5176. // 2.2) Mark those children as visible which are not under nodes in $hiddenChildren
  5177. $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, modified_subnode=$time WHERE path_string LIKE '$nodePath%' $skipSubtreesString" );
  5178. }
  5179. else
  5180. {
  5181. // Mark root node as not hidden.
  5182. if ( $modifyRootNode )
  5183. $db->query( "UPDATE ezcontentobject_tree SET is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" );
  5184. }
  5185. $node->updateAndStoreModified();
  5186. $db->commit();
  5187. eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode );
  5188. }
  5189. /*!
  5190. \a static
  5191. Depending on the new parent node visibility, recompute "is_invisible" attribute for the given node and its children.
  5192. (used after content/move or content/copy)
  5193. */
  5194. static function updateNodeVisibility( $node, $parentNode, $recursive = true )
  5195. {
  5196. if ( !$node )
  5197. {
  5198. eZDebug::writeWarning( 'No such node to update visibility for.' );
  5199. return;
  5200. }
  5201. if ( !$parentNode )
  5202. {
  5203. eZDebug::writeWarning( 'No parent node found when updating node visibility' );
  5204. return;
  5205. }
  5206. if ( $node->attribute( 'is_hidden' ) == 0 &&
  5207. $parentNode->attribute( 'is_invisible' ) != $node->attribute( 'is_invisible' ) )
  5208. {
  5209. $parentNodeIsVisible = $parentNode->attribute( 'is_invisible' );
  5210. $nodeID = $node->attribute( 'node_id' );
  5211. $db = eZDB::instance();
  5212. $db->begin();
  5213. $db->query( "UPDATE ezcontentobject_tree SET is_invisible=$parentNodeIsVisible WHERE node_id=$nodeID" );
  5214. if ( $recursive )
  5215. {
  5216. // update visibility for children of the node
  5217. if( $parentNodeIsVisible )
  5218. eZContentObjectTreeNode::hideSubTree( $node, $modifyRootNode = false );
  5219. else
  5220. eZContentObjectTreeNode::unhideSubTree( $node, $modifyRootNode = false );
  5221. }
  5222. $db->commit();
  5223. }
  5224. }
  5225. /*!
  5226. \a static
  5227. \return true on success, false otherwise
  5228. */
  5229. static function clearViewCacheForSubtree( eZContentObjectTreeNode $node, $clearForRootNode = true )
  5230. {
  5231. // Max nodes to fetch at a time
  5232. static $limit = 50;
  5233. if ( $clearForRootNode )
  5234. {
  5235. $objectID = $node->attribute( 'contentobject_id' );
  5236. eZContentCacheManager::clearContentCacheIfNeeded( $objectID );
  5237. }
  5238. $offset = 0;
  5239. $params = array( 'AsObject' => false,
  5240. 'Depth' => false,
  5241. 'Limitation' => array() ); // Empty array means no permission checking
  5242. $subtreeCount = $node->subTreeCount( $params );
  5243. while ( $offset < $subtreeCount )
  5244. {
  5245. $params['Offset'] = $offset;
  5246. $params['Limit'] = $limit;
  5247. $subtreeChunk = $node->subTree( $params );
  5248. $nNodesInChunk = count( $subtreeChunk );
  5249. $offset += $nNodesInChunk;
  5250. if ( $nNodesInChunk == 0 )
  5251. break;
  5252. $objectIDList = array();
  5253. foreach ( $subtreeChunk as $curNode )
  5254. $objectIDList[] = $curNode['contentobject_id'];
  5255. $objectIDList = array_unique( $objectIDList );
  5256. unset( $subtreeChunk );
  5257. foreach ( $objectIDList as $objectID )
  5258. eZContentCacheManager::clearContentCacheIfNeeded( $objectID );
  5259. }
  5260. return true;
  5261. }
  5262. static function setVersionByObjectID( $objectID, $newVersion )
  5263. {
  5264. $db = eZDB::instance();
  5265. $db->query( "UPDATE ezcontentobject_tree SET contentobject_version='$newVersion' WHERE contentobject_id='$objectID'" );
  5266. }
  5267. function currentLanguage()
  5268. {
  5269. return $this->CurrentLanguage;
  5270. }
  5271. function setCurrentLanguage( $languageCode )
  5272. {
  5273. $this->CurrentLanguage = $languageCode;
  5274. if ( $this->hasContentObject() )
  5275. {
  5276. $this->ContentObject->setCurrentLanguage( $languageCode );
  5277. }
  5278. $this->Name = null;
  5279. }
  5280. /*!
  5281. \static
  5282. */
  5283. static function parentDepthLimitationList()
  5284. {
  5285. $maxLevel = 0;
  5286. $ini = eZINI::instance();
  5287. if ( $ini->hasVariable( 'RoleSettings', 'MaxParentDepthLimitation' ) )
  5288. $maxLevel = $ini->variable( 'RoleSettings', 'MaxParentDepthLimitation' );
  5289. $depthArray = array();
  5290. for ( $i = 1; $i <= $maxLevel; $i++ )
  5291. {
  5292. $depthArray[] = array( 'name' => $i, 'id' => $i );
  5293. }
  5294. return $depthArray;
  5295. }
  5296. /*
  5297. Returns available classes as Js array.
  5298. Checks if the node is container, if yes emptyStr will be returned.
  5299. */
  5300. function availableClassesJsArray()
  5301. {
  5302. return eZContentObjectTreeNode::availableClassListJsArray( array( 'node' => $this ) );
  5303. }
  5304. /*
  5305. Returns available classes as Js array.
  5306. Checks for ini settings.
  5307. */
  5308. static function availableClassListJsArray( $parameters = false )
  5309. {
  5310. $iniMenu = eZINI::instance( 'contentstructuremenu.ini' );
  5311. $falseValue = "''";
  5312. if ( $iniMenu->hasVariable( 'TreeMenu', 'CreateHereMenu' ) )
  5313. {
  5314. $createHereMenu = $iniMenu->variable( 'TreeMenu', 'CreateHereMenu' );
  5315. }
  5316. else
  5317. {
  5318. $createHereMenu = 'simplified';
  5319. }
  5320. if ( $createHereMenu != 'simplified' and $createHereMenu != 'full' )
  5321. return $falseValue;
  5322. $ini = eZINI::instance( 'content.ini' );
  5323. list( $usersClassGroupID, $setupClassGroupID ) = $ini->variableMulti( 'ClassGroupIDs', array( 'Users', 'Setup' ) );
  5324. $userRootNode = $ini->variable( 'NodeSettings', 'UserRootNode' );
  5325. $groupIDs = false;
  5326. $filterType = 'include';
  5327. if ( !is_array( $parameters ) )
  5328. return $falseValue;
  5329. $node = isset( $parameters['node'] ) ? $parameters['node'] : false;
  5330. if ( is_object( $node ) )
  5331. {
  5332. if ( $createHereMenu == 'full' and !$node->canCreate() )
  5333. return $falseValue;
  5334. $obj = $node->object();
  5335. $contentClass = $obj->attribute( 'content_class' );
  5336. if ( !$contentClass->attribute( 'is_container' ) )
  5337. {
  5338. return $falseValue;
  5339. }
  5340. $pathArray = $node->pathArray();
  5341. }
  5342. else
  5343. {
  5344. // If current object is not container we should not return class list, should not display "create here" menu.
  5345. if ( isset( $parameters['is_container'] ) and !$parameters['is_container'] )
  5346. return $falseValue;
  5347. // Check if current user can create under this node
  5348. if ( $createHereMenu == 'full' and isset( $parameters['node_id'] ) )
  5349. {
  5350. $node = eZContentObjectTreeNode::fetch( $parameters['node_id'] );
  5351. if ( is_object( $node ) and !$node->canCreate() )
  5352. return $falseValue;
  5353. }
  5354. $pathString = isset( $parameters['path_string'] ) ? $parameters['path_string'] : false;
  5355. if ( !$pathString )
  5356. return $falseValue;
  5357. $pathItems = explode( '/', $pathString );
  5358. $pathArray = array();
  5359. foreach ( $pathItems as $pathItem )
  5360. {
  5361. if ( $pathItem != '' )
  5362. $pathArray[] = (int) $pathItem;
  5363. }
  5364. }
  5365. if ( in_array( $userRootNode, $pathArray ) )
  5366. {
  5367. $groupIDs = array( $usersClassGroupID );
  5368. }
  5369. else
  5370. {
  5371. $groupIDs = array( $usersClassGroupID, $setupClassGroupID );
  5372. $filterType = 'exclude';
  5373. }
  5374. if ( $createHereMenu == 'simplified' )
  5375. {
  5376. $classes = eZContentClass::fetchAllClasses( false, $filterType == 'include', $groupIDs );
  5377. return eZContentObjectTreeNode::getClassesJsArray( false, $filterType == 'include', $groupIDs, false, $classes );
  5378. }
  5379. return eZContentObjectTreeNode::getClassesJsArray( $node, $filterType == 'include', $groupIDs );
  5380. }
  5381. /*
  5382. Returns available classes as Js array.
  5383. \note building js array.
  5384. */
  5385. static function getClassesJsArray( $node = false, $includeFilter = true, $groupList = false, $fetchID = false, $classes = false )
  5386. {
  5387. $falseValue = "''";
  5388. // If $classes is false we should check $node and fetch class list
  5389. if ( $classes === false )
  5390. {
  5391. // If $node is object we should fetch available classes from node, from ezcontentclass otherwise
  5392. $classes = ( $node instanceOf eZContentObjectTreeNode )
  5393. ? $node->canCreateClassList( false, $includeFilter, $groupList, $fetchID )
  5394. : eZContentClass::canInstantiateClassList( false, $includeFilter, $groupList, $fetchID );
  5395. }
  5396. if ( !is_array( $classes ) )
  5397. return $falseValue;
  5398. // Create javascript array
  5399. $classList = array();
  5400. foreach ( $classes as $class )
  5401. {
  5402. if ( $class instanceOf eZContentClass )
  5403. {
  5404. $classID = $class->attribute( 'id' );
  5405. $className = $class->attribute( 'name' );
  5406. }
  5407. elseif ( is_array( $class ) )
  5408. {
  5409. $classID = $class['id'];
  5410. $className = $class['name'];
  5411. }
  5412. $classList[] = array(
  5413. 'classID' => (int) $classID,
  5414. 'name' => $className
  5415. );
  5416. }
  5417. if ( $classList )
  5418. return json_encode( $classList );
  5419. return $falseValue;
  5420. }
  5421. /// The current language for the node
  5422. public $CurrentLanguage = false;
  5423. /// Name of the node
  5424. public $Name;
  5425. /// Contains the cached value of the class identifier
  5426. public $ClassIdentifier = null;
  5427. public $ClassName = null;
  5428. protected $ClassIsContainer = null;
  5429. }
  5430. ?>