PageRenderTime 91ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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 $f…

Large files files are truncated, but you can click here to view the full file