PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/kernel/classes/ezcontentobject.php

http://github.com/ezsystems/ezpublish
PHP | 6749 lines | 4665 code | 620 blank | 1464 comment | 641 complexity | ba99b8ac2d292de877c0dd752e7090bc MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * File containing the eZContentObject class.
  4. *
  5. * @copyright Copyright (C) eZ Systems AS. All rights reserved.
  6. * @license For full copyright and license information view LICENSE file distributed with this source code.
  7. * @version //autogentag//
  8. * @package kernel
  9. */
  10. /**
  11. * Encapsulates data about and methods to work with content objects
  12. *
  13. * @package kernel
  14. */
  15. class eZContentObject extends eZPersistentObject
  16. {
  17. const STATUS_DRAFT = 0;
  18. const STATUS_PUBLISHED = 1;
  19. const STATUS_ARCHIVED = 2;
  20. const PACKAGE_ERROR_NO_CLASS = 1;
  21. const PACKAGE_ERROR_EXISTS = 2;
  22. const PACKAGE_ERROR_NODE_EXISTS = 3;
  23. const PACKAGE_ERROR_MODIFIED = 101;
  24. const PACKAGE_ERROR_HAS_CHILDREN = 102;
  25. const PACKAGE_REPLACE = 1;
  26. const PACKAGE_SKIP = 2;
  27. const PACKAGE_NEW = 3;
  28. const PACKAGE_UPDATE = 6;
  29. const PACKAGE_DELETE = 4;
  30. const PACKAGE_KEEP = 5;
  31. const RELATION_COMMON = 1;
  32. const RELATION_EMBED = 2;
  33. const RELATION_LINK = 4;
  34. const RELATION_ATTRIBUTE = 8;
  35. /**
  36. * Initializes the object with $row.
  37. *
  38. * If $row is an integer, it will try to fetch it from the database using it as the unique ID.
  39. *
  40. * @param int|array $row
  41. */
  42. public function __construct( $row )
  43. {
  44. parent::__construct( $row );
  45. $this->ClassIdentifier = false;
  46. if ( isset( $row['contentclass_identifier'] ) )
  47. $this->ClassIdentifier = $row['contentclass_identifier'];
  48. if ( isset( $row['class_identifier'] ) )
  49. $this->ClassIdentifier = $row['class_identifier'];
  50. $this->ClassName = false;
  51. // Depending on how the information is retrieved, the "serialized_name_list" is sometimes available in "class_serialized_name_list" key
  52. if ( isset( $row['class_serialized_name_list'] ) )
  53. $row['serialized_name_list'] = $row['class_serialized_name_list'];
  54. // Depending on how the information is retrieved, the "contentclass_name" is sometimes available in "class_name" key
  55. if ( isset( $row['class_name'] ) )
  56. $row['contentclass_name'] = $row['class_name'];
  57. if ( isset( $row['contentclass_name'] ) )
  58. $this->ClassName = $row['contentclass_name'];
  59. if ( isset( $row['serialized_name_list'] ) )
  60. $this->ClassName = eZContentClass::nameFromSerializedString( $row['serialized_name_list'] );
  61. $this->CurrentLanguage = false;
  62. if ( isset( $row['content_translation'] ) )
  63. {
  64. $this->CurrentLanguage = $row['content_translation'];
  65. }
  66. else if ( isset( $row['real_translation'] ) )
  67. {
  68. $this->CurrentLanguage = $row['real_translation'];
  69. }
  70. else if ( isset( $row['language_mask'] ) )
  71. {
  72. $topPriorityLanguage = eZContentLanguage::topPriorityLanguageByMask( $row['language_mask'] );
  73. if ( $topPriorityLanguage )
  74. {
  75. $this->CurrentLanguage = $topPriorityLanguage->attribute( 'locale' );
  76. }
  77. }
  78. // Initialize the permission array cache
  79. $this->Permissions = array();
  80. }
  81. /**
  82. * @deprecated Use eZContentObject::__construct() instead
  83. * @param int|array $row
  84. */
  85. function eZContentObject( $row )
  86. {
  87. self::__construct( $row );
  88. }
  89. static function definition()
  90. {
  91. static $definition = array( "fields" => array( "id" => array( 'name' => 'ID',
  92. 'datatype' => 'integer',
  93. 'default' => 0,
  94. 'required' => true ),
  95. "section_id" => array( 'name' => "SectionID",
  96. 'datatype' => 'integer',
  97. 'default' => 0,
  98. 'required' => true,
  99. 'foreign_class' => 'eZSection',
  100. 'foreign_attribute' => 'id',
  101. 'multiplicity' => '1..*' ),
  102. "owner_id" => array( 'name' => "OwnerID",
  103. 'datatype' => 'integer',
  104. 'default' => 0,
  105. 'required' => true,
  106. 'foreign_class' => 'eZUser',
  107. 'foreign_attribute' => 'contentobject_id',
  108. 'multiplicity' => '1..*'),
  109. "contentclass_id" => array( 'name' => "ClassID",
  110. 'datatype' => 'integer',
  111. 'default' => 0,
  112. 'required' => true,
  113. 'foreign_class' => 'eZContentClass',
  114. 'foreign_attribute' => 'id',
  115. 'multiplicity' => '1..*' ),
  116. "name" => array( 'name' => "Name",
  117. 'datatype' => 'string',
  118. 'default' => '',
  119. 'required' => true ),
  120. "published" => array( 'name' => "Published",
  121. 'datatype' => 'integer',
  122. 'default' => 0,
  123. 'required' => true ),
  124. "modified" => array( 'name' => "Modified",
  125. 'datatype' => 'integer',
  126. 'default' => 0,
  127. 'required' => true ),
  128. "current_version" => array( 'name' => "CurrentVersion",
  129. 'datatype' => 'integer',
  130. 'default' => 0,
  131. 'required' => true ),
  132. "status" => array( 'name' => "Status",
  133. 'datatype' => 'integer',
  134. 'default' => 0,
  135. 'required' => true ),
  136. 'remote_id' => array( 'name' => "RemoteID",
  137. 'datatype' => 'string',
  138. 'default' => '',
  139. 'required' => true ),
  140. 'language_mask' => array( 'name' => 'LanguageMask',
  141. 'datatype' => 'integer',
  142. 'default' => 0,
  143. 'required' => true ),
  144. 'initial_language_id' => array( 'name' => 'InitialLanguageID',
  145. 'datatype' => 'integer',
  146. 'default' => 0,
  147. 'required' => true,
  148. 'foreign_class' => 'eZContentLanguage',
  149. 'foreign_attribute' => 'id',
  150. 'multiplicity' => '1..*' ) ),
  151. "keys" => array( "id" ),
  152. "function_attributes" => array( "current" => "currentVersion",
  153. "published_version" => "publishedVersion",
  154. 'versions' => 'versions',
  155. 'author_array' => 'authorArray',
  156. "class_name" => "className",
  157. "content_class" => "contentClass",
  158. "contentobject_attributes" => "contentObjectAttributes",
  159. "owner" => "owner",
  160. "related_contentobject_array" => "relatedContentObjectList",
  161. "related_contentobject_count" => "relatedContentObjectCount",
  162. 'reverse_related_contentobject_array' => 'reverseRelatedObjectList',
  163. 'reverse_related_contentobject_count' => 'reverseRelatedObjectCount',
  164. "linked_contentobject_array" => "linkedContentObjectList",
  165. "linked_contentobject_count" => "linkedContentObjectCount",
  166. 'reverse_linked_contentobject_array' => 'reverseLinkedObjectList',
  167. 'reverse_linked_contentobject_count' => 'reverseLinkedObjectCount',
  168. "embedded_contentobject_array" => "embeddedContentObjectList",
  169. "embedded_contentobject_count" => "embeddedContentObjectCount",
  170. 'reverse_embedded_contentobject_array' => 'reverseEmbeddedObjectList',
  171. 'reverse_embedded_contentobject_count' => 'reverseEmbeddedObjectCount',
  172. "can_read" => "canRead",
  173. "can_pdf" => "canPdf",
  174. "can_diff" => "canDiff",
  175. "can_create" => "canCreate",
  176. "can_create_class_list" => "canCreateClassList",
  177. "can_edit" => "canEdit",
  178. "can_translate" => "canTranslate",
  179. "can_remove" => "canRemove",
  180. "can_move" => "canMoveFrom",
  181. "can_move_from" => "canMoveFrom",
  182. 'can_view_embed' => 'canViewEmbed',
  183. "data_map" => "dataMap",
  184. "grouped_data_map" => "groupedDataMap",
  185. "main_parent_node_id" => "mainParentNodeID",
  186. "assigned_nodes" => "assignedNodes",
  187. "visible_nodes" => "visibleNodes",
  188. "has_visible_node" => "hasVisibleNode",
  189. "parent_nodes" => "parentNodeIDArray",
  190. "main_node_id" => "mainNodeID",
  191. "main_node" => "mainNode",
  192. "default_language" => "defaultLanguage",
  193. "content_action_list" => "contentActionList",
  194. "class_identifier" => "contentClassIdentifier",
  195. 'class_group_id_list' => 'contentClassGroupIDList',
  196. 'name' => 'name',
  197. 'match_ingroup_id_list' => 'matchIngroupIDList',
  198. 'remote_id' => 'remoteID',
  199. 'current_language' => 'currentLanguage',
  200. 'current_language_object' => 'currentLanguageObject',
  201. 'initial_language' => 'initialLanguage',
  202. 'initial_language_code' => 'initialLanguageCode',
  203. 'available_languages' => 'availableLanguages',
  204. 'language_codes' => 'availableLanguages',
  205. 'language_js_array' => 'availableLanguagesJsArray',
  206. 'languages' => 'languages',
  207. 'all_languages' => 'allLanguages',
  208. 'can_edit_languages' => 'canEditLanguages',
  209. 'can_create_languages' => 'canCreateLanguages',
  210. 'always_available' => 'isAlwaysAvailable',
  211. 'allowed_assign_section_list' => 'allowedAssignSectionList',
  212. 'allowed_assign_state_id_list' => 'allowedAssignStateIDList',
  213. 'allowed_assign_state_list' => 'allowedAssignStateList',
  214. 'state_id_array' => 'stateIDArray',
  215. 'state_identifier_array' => 'stateIdentifierArray',
  216. 'section_identifier' => 'sectionIdentifier' ),
  217. "increment_key" => "id",
  218. "class_name" => "eZContentObject",
  219. "sort" => array( "id" => "asc" ),
  220. "name" => "ezcontentobject" );
  221. return $definition;
  222. }
  223. /**
  224. * Get class groups this object's class belongs to if match for class groups is enabled, otherwise false
  225. *
  226. * @return array|bool
  227. */
  228. function matchIngroupIDList()
  229. {
  230. $contentINI = eZINI::instance( 'content.ini' );
  231. $inList = false;
  232. if( $contentINI->variable( 'ContentOverrideSettings', 'EnableClassGroupOverride' ) == 'true' )
  233. {
  234. $contentClass = $this->contentClass();
  235. $inList = $contentClass->attribute( 'ingroup_id_list' );
  236. }
  237. return $inList;
  238. }
  239. /**
  240. * Stores the object
  241. *
  242. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  243. * the calls within a db transaction; thus within db->begin and db->commit.
  244. *
  245. * @param array $fieldFilters
  246. */
  247. function store( $fieldFilters = null )
  248. {
  249. // Unset the cache
  250. global $eZContentObjectContentObjectCache;
  251. unset( $eZContentObjectContentObjectCache[$this->ID] );
  252. global $eZContentObjectDataMapCache;
  253. unset( $eZContentObjectDataMapCache[$this->ID] );
  254. global $eZContentObjectVersionCache;
  255. unset( $eZContentObjectVersionCache[$this->ID] );
  256. $db = eZDB::instance();
  257. $db->begin();
  258. $this->storeNodeModified();
  259. parent::store( $fieldFilters );
  260. $db->commit();
  261. }
  262. /**
  263. * Clear in-memory caches.
  264. *
  265. * If $idArray is ommitted, the caches are cleared for all objects.
  266. *
  267. * @param array $idArray Objects to clear caches for.
  268. */
  269. static function clearCache( $idArray = array() )
  270. {
  271. if ( is_numeric( $idArray ) )
  272. $idArray = array( $idArray );
  273. // clear in-memory cache for all objects
  274. if ( count( $idArray ) == 0 )
  275. {
  276. unset( $GLOBALS['eZContentObjectContentObjectCache'] );
  277. unset( $GLOBALS['eZContentObjectDataMapCache'] );
  278. unset( $GLOBALS['eZContentObjectVersionCache'] );
  279. return;
  280. }
  281. // clear in-memory cache for specified object(s)
  282. global $eZContentObjectContentObjectCache;
  283. global $eZContentObjectDataMapCache;
  284. global $eZContentObjectVersionCache;
  285. foreach ( $idArray as $objectID )
  286. {
  287. unset( $eZContentObjectContentObjectCache[$objectID] );
  288. unset( $eZContentObjectDataMapCache[$objectID] );
  289. unset( $eZContentObjectVersionCache[$objectID] );
  290. }
  291. }
  292. /**
  293. * Update all nodes to set modified_subnode value
  294. *
  295. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  296. * the calls within a db transaction; thus within db->begin and db->commit.
  297. */
  298. function storeNodeModified()
  299. {
  300. if ( is_numeric( $this->ID ) )
  301. {
  302. $nodeArray = $this->assignedNodes();
  303. $db = eZDB::instance();
  304. $db->begin();
  305. foreach ( array_keys( $nodeArray ) as $key )
  306. {
  307. $nodeArray[$key]->updateAndStoreModified();
  308. }
  309. $db->commit();
  310. }
  311. }
  312. /**
  313. * Returns an object's name for the version given by $version in the language given by $lang
  314. *
  315. * @param int|bool $version If omitted, the current version will be used
  316. * @param bool $lang If omitted, the initial object language will be used
  317. * @return bool|string
  318. */
  319. function name( $version = false , $lang = false )
  320. {
  321. // if the object id is null, we can't read data from the database
  322. // and return the locally known name
  323. if ( $this->attribute( 'id' ) === null )
  324. {
  325. return $this->Name;
  326. }
  327. if ( !$version )
  328. {
  329. $version = $this->attribute( 'current_version' );
  330. }
  331. if ( !$lang && $this->CurrentLanguage )
  332. {
  333. $lang = $this->CurrentLanguage;
  334. }
  335. return $this->versionLanguageName( $version, $lang );
  336. }
  337. /**
  338. * Returns all translations of the current object's name
  339. *
  340. * @return string[]
  341. */
  342. function names()
  343. {
  344. $version = $this->attribute( 'current_version' );
  345. $id = $this->attribute( 'id' );
  346. $db = eZDB::instance();
  347. $rows = $db->arrayQuery( "SELECT name, real_translation FROM ezcontentobject_name WHERE contentobject_id = '$id' AND content_version='$version'" );
  348. $names = array();
  349. foreach ( $rows as $row )
  350. {
  351. $names[$row['real_translation']] = $row['name'];
  352. }
  353. return $names;
  354. }
  355. /**
  356. * Returns the object name for version $version in the language $lang
  357. *
  358. * @param int $version
  359. * @param string|bool $lang If omitted, the initial language of the object is used
  360. * @return string|bool
  361. */
  362. function versionLanguageName( $version, $lang = false )
  363. {
  364. $name = false;
  365. if ( !$version > 0 )
  366. {
  367. eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ );
  368. return $name;
  369. }
  370. $db = eZDB::instance();
  371. $contentObjectID = $this->attribute( 'id' );
  372. if ( !$lang )
  373. {
  374. // If $lang not given we will use the initial language of the object
  375. $query = "SELECT initial_language_id FROM ezcontentobject WHERE id='$contentObjectID'";
  376. $rows = $db->arrayQuery( $query );
  377. if ( $rows )
  378. {
  379. $languageID = $rows[0]['initial_language_id'];
  380. $language = eZContentLanguage::fetch( $languageID );
  381. if ( $language )
  382. {
  383. $lang = $language->attribute( 'locale' );
  384. }
  385. else
  386. {
  387. return $name;
  388. }
  389. }
  390. else
  391. {
  392. return $name;
  393. }
  394. }
  395. $lang = $db->escapeString( $lang );
  396. $version = (int) $version;
  397. $languageID = $this->attribute( 'initial_language_id' );
  398. if ( $this->attribute( 'always_available' ) )
  399. {
  400. $languageID = (int) $languageID | 1;
  401. }
  402. $query= "SELECT name, content_translation
  403. FROM ezcontentobject_name
  404. WHERE contentobject_id = '$contentObjectID'
  405. AND content_version = '$version'
  406. AND ( content_translation = '$lang' OR language_id = '$languageID' )";
  407. $result = $db->arrayQuery( $query );
  408. $resCount = count( $result );
  409. if( $resCount < 1 )
  410. {
  411. eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ );
  412. }
  413. else if( $resCount > 1 )
  414. {
  415. // we have name in requested language => find and return it
  416. foreach( $result as $row )
  417. {
  418. if( $row['content_translation'] == $lang )
  419. {
  420. $name = $row['name'];
  421. break;
  422. }
  423. }
  424. }
  425. else
  426. {
  427. // we don't have name in requested language(or requested language is the same as initial language) => use name in initial language
  428. $name = $result[0]['name'];
  429. }
  430. return $name;
  431. }
  432. /**
  433. * Sets the name of the object, in memory only. Use {@see setName()} to change it permanently
  434. *
  435. * @param string $name
  436. */
  437. function setCachedName( $name )
  438. {
  439. $this->Name = $name;
  440. }
  441. /**
  442. * Sets the name of the object in all translations.
  443. *
  444. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  445. * the calls within a db transaction; thus within db->begin and db->commit.
  446. *
  447. * @param string $objectName
  448. * @param int|bool $versionNum
  449. * @param int|bool $languageCode
  450. */
  451. function setName( $objectName, $versionNum = false, $languageCode = false )
  452. {
  453. $initialLanguageCode = false;
  454. if ( $initialLanguage = $this->initialLanguage() )
  455. {
  456. $initialLanguageCode = $initialLanguage->attribute( 'locale' );
  457. }
  458. $db = eZDB::instance();
  459. if ( $languageCode == false )
  460. {
  461. $languageCode = $initialLanguageCode;
  462. }
  463. $languageCode = $db->escapeString( $languageCode );
  464. if ( $languageCode == $initialLanguageCode )
  465. {
  466. $this->Name = $objectName;
  467. }
  468. if ( !$versionNum )
  469. {
  470. $versionNum = $this->attribute( 'current_version' );
  471. }
  472. $objectID =(int) $this->attribute( 'id' );
  473. $versionNum =(int) $versionNum;
  474. $languageID =(int) eZContentLanguage::idByLocale( $languageCode );
  475. $objectName = $db->escapeString( $objectName );
  476. $db->begin();
  477. // Check if name is already set before setting/changing it.
  478. // This helps to avoid deadlocks in mysql: a pair of DELETE/INSERT might cause deadlock here
  479. // in case of concurrent execution.
  480. $rows = $db->arrayQuery( "SELECT COUNT(*) AS count FROM ezcontentobject_name WHERE contentobject_id = '$objectID'
  481. AND content_version = '$versionNum' AND content_translation ='$languageCode'" );
  482. if ( $rows[0]['count'] )
  483. {
  484. $db->query( "UPDATE ezcontentobject_name SET name='$objectName'
  485. WHERE
  486. contentobject_id = '$objectID' AND
  487. content_version = '$versionNum' AND
  488. content_translation ='$languageCode'" );
  489. }
  490. else
  491. {
  492. $db->query( "INSERT INTO ezcontentobject_name( contentobject_id,
  493. name,
  494. content_version,
  495. content_translation,
  496. real_translation,
  497. language_id )
  498. VALUES( '$objectID',
  499. '$objectName',
  500. '$versionNum',
  501. '$languageCode',
  502. '$languageCode',
  503. '$languageID' )" );
  504. }
  505. $db->commit();
  506. }
  507. /**
  508. * Return a map with all the content object attributes where the keys are the attribute identifiers.
  509. *
  510. * @return eZContentObjectAttribute[]
  511. */
  512. function dataMap()
  513. {
  514. return $this->fetchDataMap();
  515. }
  516. /**
  517. * Generates a map with all the content object attributes where the keys are
  518. * the attribute identifiers grouped by class attribute category.
  519. *
  520. * Result is not cached, so make sure you don't call this over and over.
  521. *
  522. * @return array
  523. */
  524. public function groupedDataMap()
  525. {
  526. return self::createGroupedDataMap( $this->fetchDataMap() );
  527. }
  528. /**
  529. * Generates a map with all the content object attributes where the keys are
  530. * the attribute identifiers grouped by class attribute category.
  531. *
  532. * Result is not cached, so make sure you don't call this over and over.
  533. *
  534. * @param eZContentObjectAttribute[] $contentObjectAttributes Array of eZContentObjectAttribute objects
  535. * @return array
  536. */
  537. public static function createGroupedDataMap( $contentObjectAttributes )
  538. {
  539. $groupedDataMap = array();
  540. $contentINI = eZINI::instance( 'content.ini' );
  541. $categorys = $contentINI->variable( 'ClassAttributeSettings', 'CategoryList' );
  542. $defaultCategory = $contentINI->variable( 'ClassAttributeSettings', 'DefaultCategory' );
  543. foreach( $contentObjectAttributes as $attribute )
  544. {
  545. $classAttribute = $attribute->contentClassAttribute();
  546. $attributeCategory = $classAttribute->attribute('category');
  547. $attributeIdentifier = $classAttribute->attribute( 'identifier' );
  548. if ( !isset( $categorys[ $attributeCategory ] ) || !$attributeCategory )
  549. $attributeCategory = $defaultCategory;
  550. if ( !isset( $groupedDataMap[ $attributeCategory ] ) )
  551. $groupedDataMap[ $attributeCategory ] = array();
  552. $groupedDataMap[ $attributeCategory ][$attributeIdentifier] = $attribute;
  553. }
  554. return $groupedDataMap;
  555. }
  556. /**
  557. * Returns a map with all the content object attributes where the keys are the attribute identifiers.
  558. *
  559. * @param int|bool $version
  560. * @param string|bool $language
  561. * @return eZContentObjectAttribute[]
  562. */
  563. function fetchDataMap( $version = false, $language = false )
  564. {
  565. // Global variable to cache datamaps
  566. global $eZContentObjectDataMapCache;
  567. if ( $version == false )
  568. $version = $this->attribute( 'current_version' );
  569. if ( $language == false )
  570. {
  571. $language = $this->CurrentLanguage;
  572. }
  573. if ( !$language || !isset( $eZContentObjectDataMapCache[$this->ID][$version][$language] ) )
  574. {
  575. $data = $this->contentObjectAttributes( true, $version, $language );
  576. if ( !$language )
  577. {
  578. $language = $this->CurrentLanguage;
  579. }
  580. // Store the attributes for later use
  581. $this->ContentObjectAttributeArray[$version][$language] = $data;
  582. $eZContentObjectDataMapCache[$this->ID][$version][$language] = $data;
  583. }
  584. else
  585. {
  586. $data = $eZContentObjectDataMapCache[$this->ID][$version][$language];
  587. }
  588. if ( !isset( $this->DataMap[$version][$language] ) )
  589. {
  590. $ret = array();
  591. /* @var eZContentObjectAttribute[] $data */
  592. foreach( $data as $key => $item )
  593. {
  594. $identifier = $item->contentClassAttributeIdentifier();
  595. $ret[$identifier] = $data[$key];
  596. }
  597. $this->DataMap[$version][$language] = $ret;
  598. }
  599. else
  600. {
  601. $ret = $this->DataMap[$version][$language];
  602. }
  603. return $ret;
  604. }
  605. /**
  606. * Resets (empties) the current object's data map
  607. *
  608. * @return array
  609. */
  610. function resetDataMap()
  611. {
  612. $this->ContentObjectAttributeArray = array();
  613. $this->ContentObjectAttributes = array();
  614. $this->DataMap = array();
  615. return $this->DataMap;
  616. }
  617. /**
  618. * Fetches a set of content object attributes by their class identifiers.
  619. *
  620. * @param string[] $identifierArray
  621. * @param int|bool $version
  622. * @param string[]|bool $languageArray
  623. * @param bool $asObject If true, returns an array of eZContentObjectAttributes, a normal array otherwise
  624. *
  625. * @return eZContentObjectAttribute[]|array|null
  626. */
  627. function fetchAttributesByIdentifier( $identifierArray, $version = false, $languageArray = false, $asObject = true )
  628. {
  629. if ( count( $identifierArray ) === 0 )
  630. {
  631. return null;
  632. }
  633. $db = eZDB::instance();
  634. $identifierQuotedString = array();
  635. foreach ( $identifierArray as $identifier )
  636. {
  637. $identifierQuotedString[] = "'$identifier'";
  638. }
  639. if ( !$version or !is_numeric( $version ) )
  640. {
  641. $version = $this->CurrentVersion;
  642. }
  643. if ( is_array( $languageArray ) )
  644. {
  645. $langCodeQuotedString = array();
  646. foreach ( $languageArray as $langCode )
  647. {
  648. if ( is_string( $langCode ) )
  649. $langCodeQuotedString[] = "'$langCode'";
  650. }
  651. if ( !empty( $langCodeQuotedString ) )
  652. {
  653. $languageText = "AND ";
  654. $languageText .= $db->generateSQLINStatement( $langCodeQuotedString, 'ezcontentobject_attribute.language_code' );
  655. }
  656. }
  657. if ( !isset( $languageText ) )
  658. {
  659. $languageText = "AND " . eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' );
  660. }
  661. $versionText = "AND ezcontentobject_attribute.version = '$version'";
  662. $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier
  663. FROM ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version
  664. WHERE
  665. ezcontentclass_attribute.version = ". eZContentClass::VERSION_STATUS_DEFINED . " AND
  666. ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
  667. ezcontentobject_version.contentobject_id = {$this->ID} AND
  668. ezcontentobject_version.version = {$version} AND
  669. ezcontentobject_attribute.contentobject_id = {$this->ID}
  670. {$languageText}
  671. {$versionText}
  672. AND
  673. ";
  674. $query .= $db->generateSQLINStatement( $identifierQuotedString, 'identifier' );
  675. $rows = $db->arrayQuery( $query );
  676. if ( count( $rows ) > 0 )
  677. {
  678. if ( $asObject )
  679. {
  680. $returnArray = array();
  681. foreach( $rows as $row )
  682. {
  683. $returnArray[$row['id']] = new eZContentObjectAttribute( $row );
  684. }
  685. return $returnArray;
  686. }
  687. else
  688. {
  689. return $rows;
  690. }
  691. }
  692. return null;
  693. }
  694. /**
  695. * Returns the owner of the object as a content object.
  696. *
  697. * @return eZContentObject|null
  698. */
  699. function owner()
  700. {
  701. if ( $this->OwnerID != 0 )
  702. {
  703. return eZContentObject::fetch( $this->OwnerID );
  704. }
  705. return null;
  706. }
  707. /**
  708. * Returns the content class group identifiers for the current content object
  709. *
  710. * @return array
  711. */
  712. function contentClassGroupIDList()
  713. {
  714. $contentClass = $this->contentClass();
  715. return $contentClass->attribute( 'ingroup_id_list' );
  716. }
  717. /**
  718. * Returns the content class identifer for the current content object
  719. *
  720. * The object will cache the class name information so multiple calls will be fast.
  721. *
  722. * @return string|bool|null
  723. */
  724. function contentClassIdentifier()
  725. {
  726. if ( !is_numeric( $this->ClassID ) )
  727. {
  728. $retValue = null;
  729. return $retValue;
  730. }
  731. if ( $this->ClassIdentifier !== false )
  732. return $this->ClassIdentifier;
  733. $this->ClassIdentifier = eZContentClass::classIdentifierByID( $this->ClassID );
  734. return $this->ClassIdentifier;
  735. }
  736. /**
  737. * Returns the content class for the current content object
  738. *
  739. * @return eZContentClass|null
  740. */
  741. function contentClass()
  742. {
  743. if ( !is_numeric( $this->ClassID ) )
  744. {
  745. $retValue = null;
  746. return $retValue;
  747. }
  748. return eZContentClass::fetch( $this->ClassID );
  749. }
  750. /**
  751. * Returns the remote id of the current content object
  752. *
  753. * @return string
  754. */
  755. function remoteID()
  756. {
  757. $remoteID = $this->attribute( 'remote_id', true );
  758. if ( !$remoteID )
  759. {
  760. $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
  761. if ( $this->attribute( 'id' ) !== null )
  762. $this->sync( array( 'remote_id' ) );
  763. $remoteID = $this->attribute( 'remote_id', true );
  764. }
  765. return $remoteID;
  766. }
  767. /**
  768. * Returns the ID of the the current object's main node
  769. *
  770. * @return int|null
  771. */
  772. function mainParentNodeID()
  773. {
  774. $list = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID, false, true );
  775. return isset( $list[0] ) ? $list[0] : null;
  776. }
  777. /**
  778. * Returns a contentobject by remote ID
  779. *
  780. * @param string $remoteID
  781. * @param bool $asObject
  782. * @return eZContentObject|array|null
  783. */
  784. static function fetchByRemoteID( $remoteID, $asObject = true )
  785. {
  786. $db = eZDB::instance();
  787. $remoteID =$db->escapeString( $remoteID );
  788. $resultArray = $db->arrayQuery( 'SELECT id FROM ezcontentobject WHERE remote_id=\'' . $remoteID . '\'' );
  789. if ( count( $resultArray ) != 1 )
  790. $object = null;
  791. else
  792. $object = eZContentObject::fetch( $resultArray[0]['id'], $asObject );
  793. return $object;
  794. }
  795. /**
  796. * Fetches a content object by ID
  797. *
  798. * @param int $id ID of the content object to fetch
  799. * @param bool $asObject Return the result as an object (true) or an assoc. array (false)
  800. *
  801. * @return eZContentObject
  802. */
  803. static function fetch( $id, $asObject = true )
  804. {
  805. global $eZContentObjectContentObjectCache;
  806. // If the object given by its id is not cached or should be returned as array
  807. // then we fetch it from the DB (objects are always cached as arrays).
  808. if ( !isset( $eZContentObjectContentObjectCache[$id] ) or $asObject === false )
  809. {
  810. $db = eZDB::instance();
  811. $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) );
  812. $objectArray = array();
  813. if ( count( $resArray ) == 1 && $resArray !== false )
  814. {
  815. $objectArray = $resArray[0];
  816. }
  817. else
  818. {
  819. eZDebug::writeError( "Object not found ($id)", __METHOD__ );
  820. $retValue = null;
  821. return $retValue;
  822. }
  823. if ( $asObject )
  824. {
  825. $obj = new eZContentObject( $objectArray );
  826. $eZContentObjectContentObjectCache[$id] = $obj;
  827. }
  828. else
  829. {
  830. return $objectArray;
  831. }
  832. return $obj;
  833. }
  834. else
  835. {
  836. return $eZContentObjectContentObjectCache[$id];
  837. }
  838. }
  839. /**
  840. * Fetches a content object by ID, using SELECT ... FOR UPDATE
  841. *
  842. * Ensures the table row is locked, blocking other transactions from locking it (read or write).
  843. * Usage must be within a transaction, or it will be locked for the current session.
  844. *
  845. * @param int $id ID of the content object to fetch
  846. * @param bool $asObject Return the result as an object (true) or an assoc. array (false)
  847. *
  848. * @return eZContentObject|mixed|null
  849. */
  850. static function fetchForUpdate( $id, $asObject = true )
  851. {
  852. global $eZContentObjectContentObjectCache;
  853. $db = eZDB::instance();
  854. $id = (int) $id;
  855. // Select for update, to lock the row
  856. $resArray = $db->arrayQuery( "SELECT * FROM ezcontentobject WHERE id='$id' FOR UPDATE" );
  857. if ( !is_array( $resArray ) || count( $resArray ) !== 1 )
  858. {
  859. eZDebug::writeError( "Object not found ($id)", __METHOD__ );
  860. return null;
  861. }
  862. $objectArray = $resArray[0];
  863. $classId = $objectArray['contentclass_id'];
  864. $contentClassResArray = $db->arrayQuery(
  865. "SELECT
  866. ezcontentclass.serialized_name_list as serialized_name_list,
  867. ezcontentclass.identifier as contentclass_identifier,
  868. ezcontentclass.is_container as is_container
  869. FROM
  870. ezcontentclass
  871. WHERE
  872. ezcontentclass.id='$classId' AND
  873. ezcontentclass.version=0"
  874. );
  875. $objectArray = array_merge($objectArray, $contentClassResArray[0]);
  876. if ( !$asObject )
  877. {
  878. return $objectArray;
  879. }
  880. $obj = new eZContentObject( $objectArray );
  881. $eZContentObjectContentObjectCache[$id] = $obj;
  882. return $obj;
  883. }
  884. /**
  885. * Returns true, if a content object with the ID $id exists, false otherwise
  886. *
  887. * @param int $id
  888. * @return bool
  889. */
  890. static function exists( $id )
  891. {
  892. global $eZContentObjectContentObjectCache;
  893. // Check the global cache
  894. if ( isset( $eZContentObjectContentObjectCache[$id] ) )
  895. return true;
  896. // If the object is not cached we need to check the DB
  897. $db = eZDB::instance();
  898. $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) );
  899. if ( $resArray !== false and count( $resArray ) == 1 )
  900. {
  901. return true;
  902. }
  903. return false;
  904. }
  905. /**
  906. * Creates the SQL for fetching the object with ID $id and returns the string.
  907. *
  908. * @param int $id
  909. * @return string
  910. */
  911. static function createFetchSQLString( $id )
  912. {
  913. $id = (int) $id;
  914. $fetchSQLString = "SELECT ezcontentobject.*,
  915. ezcontentclass.serialized_name_list as serialized_name_list,
  916. ezcontentclass.identifier as contentclass_identifier,
  917. ezcontentclass.is_container as is_container
  918. FROM
  919. ezcontentobject,
  920. ezcontentclass
  921. WHERE
  922. ezcontentobject.id='$id' AND
  923. ezcontentclass.id = ezcontentobject.contentclass_id AND
  924. ezcontentclass.version=0";
  925. return $fetchSQLString;
  926. }
  927. /**
  928. * Creates the SQL for filtering objects by visibility, used by IgnoreVisibility on some fetches.
  929. * The object is visible if 1 or more assigned nodes are visible.
  930. *
  931. * @since Version 4.1
  932. * @param bool $IgnoreVisibility ignores visibility if true
  933. * @param string $ezcontentobjectTable name of ezcontentobject table used in sql
  934. * @return string with sql condition for node filtering by visibility
  935. */
  936. static function createFilterByVisibilitySQLString( $IgnoreVisibility = false, $ezcontentobjectTable = 'ezcontentobject' )
  937. {
  938. if ( $IgnoreVisibility )
  939. return '';
  940. return " AND ( SELECT MIN( ezct.is_invisible ) FROM ezcontentobject_tree ezct WHERE ezct.contentobject_id = $ezcontentobjectTable.id ) = 0 ";
  941. }
  942. /**
  943. * Fetches the contentobject which has a node with ID $nodeID
  944. *
  945. * $nodeID can also be an array of NodeIDs. In this case, an array of content objects will be returned
  946. *
  947. * @param int|array $nodeID Single nodeID or array of NodeIDs
  948. * @param bool $asObject If results have to be returned as eZContentObject instances or not
  949. * @return eZContentObject|eZContentObject[]|array|null Content object or array of content objects.
  950. * Content objects can be eZContentObject instances or array result sets
  951. */
  952. static function fetchByNodeID( $nodeID, $asObject = true )
  953. {
  954. global $eZContentObjectContentObjectCache;
  955. $resultAsArray = is_array( $nodeID );
  956. $nodeID = (array)$nodeID;
  957. $db = eZDB::instance();
  958. $resArray = $db->arrayQuery(
  959. "SELECT co.*, con.name as name, con.real_translation, cot.node_id
  960. FROM ezcontentobject co
  961. JOIN ezcontentobject_tree cot ON co.id = cot.contentobject_id AND co.current_version = cot.contentobject_version
  962. JOIN ezcontentobject_name con ON co.id = con.contentobject_id AND co.current_version = con.content_version
  963. WHERE " .
  964. $db->generateSQLINStatement( $nodeID, 'cot.node_id', false, true, 'int' ) . " AND " .
  965. eZContentLanguage::sqlFilter( 'con', 'co' )
  966. );
  967. if ( $resArray === false || empty( $resArray ) )
  968. {
  969. eZDebug::writeError( 'A problem occured while fetching objects with following NodeIDs : ' . implode( ', ', $nodeID ), __METHOD__ );
  970. return $resultAsArray ? array() : null;
  971. }
  972. $objectArray = array();
  973. if ( $asObject )
  974. {
  975. foreach ( $resArray as $res )
  976. {
  977. $objectArray[$res['node_id']] = $eZContentObjectContentObjectCache[$res['id']] = new self( $res );
  978. }
  979. }
  980. else
  981. {
  982. foreach ( $resArray as $res )
  983. {
  984. $objectArray[$res['node_id']] = $res;
  985. }
  986. }
  987. if ( !$resultAsArray )
  988. return $objectArray[$res['node_id']];
  989. return $objectArray;
  990. }
  991. /**
  992. * Fetches a content object list based on an array of content object ids
  993. *
  994. * @param array $idArray array of content object ids
  995. * @param bool $asObject
  996. * Wether to get the result as an array of eZContentObject or an
  997. * array of associative arrays
  998. * @param bool|string $lang A language code to put at the top of the prioritized
  999. * languages list.
  1000. *
  1001. * @return array(contentObjectID => eZContentObject|array)
  1002. * array of eZContentObject (if $asObject = true) or array of
  1003. * associative arrays (if $asObject = false)
  1004. */
  1005. static function fetchIDArray( $idArray, $asObject = true , $lang = false )
  1006. {
  1007. global $eZContentObjectContentObjectCache;
  1008. $db = eZDB::instance();
  1009. $resRowArray = $db->arrayQuery(
  1010. "SELECT ezcontentclass.serialized_name_list as class_serialized_name_list, ezcontentobject.*, ezcontentobject_name.name as name, ezcontentobject_name.real_translation
  1011. FROM
  1012. ezcontentclass,
  1013. ezcontentobject,
  1014. ezcontentobject_name
  1015. WHERE
  1016. ezcontentclass.id=ezcontentobject.contentclass_id AND " .
  1017. // All elements from $idArray should be casted to (int)
  1018. $db->generateSQLINStatement( $idArray, 'ezcontentobject.id', false, true, 'int' ) . " AND
  1019. ezcontentobject.id = ezcontentobject_name.contentobject_id AND
  1020. ezcontentobject.current_version = ezcontentobject_name.content_version AND " .
  1021. eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject', 'language_id', 'language_mask', $lang )
  1022. );
  1023. $objectRetArray = array();
  1024. foreach ( $resRowArray as $resRow )
  1025. {
  1026. $objectID = $resRow['id'];
  1027. $resRow['class_name'] = eZContentClass::nameFromSerializedString( $resRow['class_serialized_name_list'] );
  1028. if ( $asObject )
  1029. {
  1030. $obj = new eZContentObject( $resRow );
  1031. $obj->ClassName = $resRow['class_name'];
  1032. if ( $lang !== false )
  1033. {
  1034. $eZContentObjectContentObjectCache[$objectID] = $obj;
  1035. }
  1036. $objectRetArray[$objectID] = $obj;
  1037. }
  1038. else
  1039. {
  1040. $objectRetArray[$objectID] = $resRow;
  1041. }
  1042. }
  1043. return $objectRetArray;
  1044. }
  1045. /**
  1046. * Returns an array of content objects.
  1047. *
  1048. * @param bool $asObject Whether to return objects or not
  1049. * @param array|null $conditions Optional conditions to limit the fetch
  1050. * @param int|bool $offset Where to start fetch from, set to false to skip it.
  1051. * @param int|bool $limit Maximum number of objects to fetch, set false to skip it.
  1052. * @return eZContentObject[]|array|null
  1053. */
  1054. static function fetchList( $asObject = true, $conditions = null, $offset = false, $limit = false )
  1055. {
  1056. $limitation = null;
  1057. if ( $offset !== false or
  1058. $limit !== false )
  1059. $limitation = array( 'offset' => $offset,
  1060. 'length' => $limit );
  1061. return eZPersistentObject::fetchObjectList( eZContentObject::definition(),
  1062. null,
  1063. $conditions, null, $limitation,
  1064. $asObject );
  1065. }
  1066. /**
  1067. * Returns a filtered array of content objects.
  1068. *
  1069. * @param array|null $conditions Optional conditions to limit the fetch
  1070. * @param int|bool $offset Where to start fetch from, set to false to skip it.
  1071. * @param int|bool $limit Maximum number of objects to fetch, set false to skip it.
  1072. * @param bool $asObject Whether to return objects or not
  1073. * @return array|eZPersistentObject[]|null
  1074. */
  1075. static function fetchFilteredList( $conditions = null, $offset = false, $limit = false, $asObject = true )
  1076. {
  1077. $limits = null;
  1078. if ( $offset or $limit )
  1079. $limits = array( 'offset' => $offset,
  1080. 'length' => $limit );
  1081. return eZPersistentObject::fetchObjectList( eZContentObject::definition(),
  1082. null,
  1083. $conditions, null, $limits,
  1084. $asObject );
  1085. }
  1086. /**
  1087. * Returns the number of objects in the database. Optionally \a $conditions can be used to limit the list count.
  1088. * @param array|null $conditions
  1089. * @return int
  1090. */
  1091. static function fetchListCount( $conditions = null )
  1092. {
  1093. $rows = eZPersistentObject::fetchObjectList( eZContentObject::definition(),
  1094. array(),
  1095. $conditions,
  1096. false/* we don't want any sorting when counting. Sorting leads to error on postgresql 8.x */,
  1097. null,
  1098. false, false,
  1099. array( array( 'operation' => 'count( * )',
  1100. 'name' => 'count' ) ) );
  1101. return $rows[0]['count'];
  1102. }
  1103. /**
  1104. * Returns an array of content objects with the content class id $contentClassID
  1105. *
  1106. * @param int $contentClassID
  1107. * @param bool $asObject Whether to return objects or not
  1108. * @param int|bool $offset Where to start fetch from, set to false to skip it.
  1109. * @param int|bool $limit Maximum number of objects to fetch, set false to skip it.
  1110. * @return eZContentObject[]|array|null
  1111. */
  1112. static function fetchSameClassList( $contentClassID, $asObject = true, $offset = false, $limit = false )
  1113. {
  1114. $conditions = array( 'contentclass_id' => $contentClassID );
  1115. return eZContentObject::fetchFilteredList( $conditions, $offset, $limit, $asObject );
  1116. }
  1117. /**
  1118. * Returns the number of content objects with the content class id $contentClassID
  1119. *
  1120. * @param int $contentClassID
  1121. * @return int
  1122. */
  1123. static function fetchSameClassListCount( $contentClassID )
  1124. {
  1125. $result = eZPersistentObject::fetchObjectList( eZContentObject::definition(),
  1126. array(),
  1127. array( "contentclass_id" => $contentClassID ),
  1128. false, null,
  1129. false, false,
  1130. array( array( 'operation' => 'count( * )',
  1131. 'name' => 'count' ) ) );
  1132. return $result[0]['count'];
  1133. }
  1134. /**
  1135. * Returns the current version of this document.
  1136. *
  1137. * @param bool $asObject If true, returns an eZContentObjectVersion; if false, returns an array
  1138. * @return eZContentObjectVersion|array|bool
  1139. */
  1140. function currentVersion( $asObject = true )
  1141. {
  1142. return eZContentObjectVersion::fetchVersion( $this->attribute( "current_version" ), $this->ID, $asObject );
  1143. }
  1144. /**
  1145. * Returns the published version of the object, or null if not published yet.
  1146. *
  1147. * @return int|null
  1148. */
  1149. public function publishedVersion()
  1150. {
  1151. $params = array(
  1152. 'conditions' => array(
  1153. 'status' => eZContentObjectVersion::STATUS_PUBLISHED
  1154. )
  1155. );
  1156. $versions = $this->versions( false, $params );
  1157. if ( !empty( $versions ) )
  1158. {
  1159. return $versions[0]['version'];
  1160. }
  1161. return null;
  1162. }
  1163. /**
  1164. * Returns the given object version. False is returned if the versions does not exist.
  1165. *
  1166. * @param int $version
  1167. * @param bool $asObject If true, returns an eZContentObjectVersion; if false, returns an array
  1168. * @return eZContentObjectVersion|array|bool
  1169. */
  1170. function version( $version, $asObject = true )
  1171. {
  1172. if ( $asObject )
  1173. {
  1174. global $eZContentObjectVersionCache;
  1175. if ( !isset( $eZContentObjectVersionCache ) ) // prevent PHP warning below
  1176. $eZContentObjectVersionCache = array();
  1177. if ( isset( $eZContentObjectVersionCache[$this->ID][$version] ) )
  1178. {
  1179. return $eZContentObjectVersionCache[$this->ID][$version];
  1180. }
  1181. else
  1182. {
  1183. $eZContentObjectVersionCache[$this->ID][$version] = eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject );
  1184. return $eZContentObjectVersionCache[$this->ID][$version];
  1185. }
  1186. }
  1187. else
  1188. {
  1189. return eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject );
  1190. }
  1191. }
  1192. /**
  1193. * Returns an array of eZContentObjectVersion for the current object according to the conditions in $parameters.
  1194. *
  1195. * @param bool $asObject If true, returns an eZContentObjectVersion; if false, returns an array
  1196. * @param array $parameters
  1197. * @return eZContentObjectVersion[]|array
  1198. */
  1199. function versions( $asObject = true, $parameters = array() )
  1200. {
  1201. $conditions = array( "contentobject_id" => $this->ID );
  1202. if ( isset( $parameters['conditions'] ) )
  1203. {
  1204. if ( isset( $parameters['conditions']['status'] ) )
  1205. $conditions['status'] = $parameters['conditions']['status'];
  1206. if ( isset( $parameters['conditions']['creator_id'] ) )
  1207. $conditions['creator_id'] = $parameters['conditions']['creator_id'];
  1208. if ( isset( $parameters['conditions']['language_code'] ) )
  1209. {
  1210. $conditions['initial_language_id'] = eZContentLanguage::idByLocale( $parameters['conditions']['language_code'] );
  1211. }
  1212. if ( isset( $parameters['conditions']['initial_language_id'] ) )
  1213. {
  1214. $conditions['initial_language_id'] = $parameters['conditions']['initial_language_id'];
  1215. }
  1216. }
  1217. $sort = isset( $parameters['sort'] ) ? $parameters['sort'] : null;
  1218. $limit = isset( $parameters['limit'] ) ? $parameters['limit'] : null;
  1219. return eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
  1220. null, $conditions,
  1221. $sort, $limit,
  1222. $asObject );
  1223. }
  1224. /**
  1225. * Returns true if the object has any versions remaining.
  1226. *
  1227. * @return bool
  1228. */
  1229. function hasRemainingVersions()
  1230. {
  1231. $remainingVersions = $this->versions( false );
  1232. if ( !is_array( $remainingVersions ) or
  1233. count( $remainingVersions ) == 0 )
  1234. {
  1235. return false;
  1236. }
  1237. return true;
  1238. }
  1239. /**
  1240. * Creates an initial content object version
  1241. *
  1242. * @param int $userID
  1243. * @param string|bool $initialLanguageCode
  1244. * @return eZContentObjectVersion
  1245. */
  1246. function createInitialVersion( $userID, $initialLanguageCode = false )
  1247. {
  1248. return eZContentObjectVersion::create( $this->attribute( "id" ), $userID, 1, $initialLanguageCode );
  1249. }
  1250. /**
  1251. * Creates a new version for the language $languageCode
  1252. *
  1253. * @param string $languageCode
  1254. * @param bool $copyFromLanguageCode
  1255. * @param int|bool $copyFromVersion
  1256. * @param bool $versionCheck
  1257. * @param int $status
  1258. *
  1259. * @return eZContentObjectVersion
  1260. */
  1261. function createNewVersionIn( $languageCode, $copyFromLanguageCode = false, $copyFromVersion = false, $versionCheck = true, $status = eZContentObjectVersion::STATUS_DRAFT )
  1262. {
  1263. return $this->createNewVersion( $copyFromVersion, $versionCheck, $languageCode, $copyFromLanguageCode, $status );
  1264. }
  1265. /**
  1266. * Creates a new version and returns it as an eZContentObjectVersion object.
  1267. *
  1268. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1269. * the calls within a db transaction; thus within db->begin and db->commit.
  1270. *
  1271. * @param int|bool $copyFromVersion If given, that version is used to create a copy.
  1272. * @param bool $versionCheck If \c true it will check if there are too many version and remove some of them to make room for a new.
  1273. * @param string|bool $languageCode
  1274. * @param string|bool $copyFromLanguageCode
  1275. * @param int $status
  1276. *
  1277. * @return eZContentObjectVersion
  1278. */
  1279. function createNewVersion( $copyFromVersion = false, $versionCheck = true, $languageCode = false, $copyFromLanguageCode = false, $status = eZContentObjectVersion::STATUS_DRAFT )
  1280. {
  1281. $db = eZDB::instance();
  1282. $db->begin();
  1283. // get the next available version number
  1284. $nextVersionNumber = $this->nextVersion();
  1285. if ( $copyFromVersion == false )
  1286. {
  1287. $version = $this->currentVersion();
  1288. }
  1289. else
  1290. {
  1291. $version = $this->version( $copyFromVersion );
  1292. }
  1293. if ( !$languageCode )
  1294. {
  1295. $initialLanguage = $version->initialLanguage();
  1296. if ( !$initialLanguage )
  1297. {
  1298. $initialLanguage = $this->initialLanguage();
  1299. }
  1300. if ( $initialLanguage )
  1301. {
  1302. $languageCode = $initialLanguage->attribute( 'locale' );
  1303. }
  1304. }
  1305. $copiedVersion = $this->copyVersion( $this, $version, $nextVersionNumber, false, $status, $languageCode, $copyFromLanguageCode );
  1306. // We need to make sure the copied version contains node-assignment for the existing nodes.
  1307. // This is required for BC since scripts might traverse the node-assignments and mark
  1308. // some of them for removal.
  1309. $parentMap = array();
  1310. $copiedNodeAssignmentList = $copiedVersion->attribute( 'node_assignments' );
  1311. foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment )
  1312. {
  1313. $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment;
  1314. }
  1315. $nodes = $this->assignedNodes();
  1316. foreach ( $nodes as $node )
  1317. {
  1318. $remoteID = 0;
  1319. // Remove assignments which conflicts with existing nodes, but keep remote_id
  1320. if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) )
  1321. {
  1322. $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )];
  1323. $remoteID = $copiedNodeAssignment->attribute( 'remote_id' );
  1324. $copiedNodeAssignment->purge();
  1325. }
  1326. $newNodeAssignment = $copiedVersion->assignToNode( $node->attribute( 'parent_node_id' ), $node->attribute( 'is_main' ), 0,
  1327. $node->attribute( 'sort_field' ), $node->attribute( 'sort_order' ),
  1328. $remoteID, $node->attribute( 'remote_id' ) );
  1329. // Reset execution bit
  1330. $newNodeAssignment->setAttribute( 'op_code', $newNodeAssignment->attribute( 'op_code' ) & ~1 );
  1331. $newNodeAssignment->store();
  1332. }
  1333. // Removing last item if we don't have enough space in version list
  1334. if ( $versionCheck )
  1335. {
  1336. $versionlimit = eZContentClass::versionHistoryLimit( $this->attribute( 'contentclass_id' ) );
  1337. $versionCount = $this->getVersionCount();
  1338. if ( $versionCount > $versionlimit )
  1339. {
  1340. // Remove oldest archived version
  1341. $params = array( 'conditions'=> array( 'status' => eZContentObjectVersion::STATUS_ARCHIVED ) );
  1342. $versions = $this->versions( true, $params );
  1343. if ( count( $versions ) > 0 )
  1344. {
  1345. $modified = $versions[0]->attribute( 'modified' );
  1346. $removeVersion = $versions[0];
  1347. foreach ( $versions as $version )
  1348. {
  1349. $currentModified = $version->attribute( 'modified' );
  1350. if ( $currentModified < $modified )
  1351. {
  1352. $modified = $currentModified;
  1353. $removeVersion = $version;
  1354. }
  1355. }
  1356. $removeVersion->removeThis();
  1357. }
  1358. }
  1359. }
  1360. $db->commit();
  1361. return $copiedVersion;
  1362. }
  1363. /**
  1364. * Creates a new version and returns it as an eZContentObjectVersion object.
  1365. *
  1366. * If version number is given as argument that version is used to create a copy.
  1367. *
  1368. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1369. * the calls within a db transaction; thus within db->begin and db->commit.
  1370. *
  1371. * @param eZContentObject $newObject
  1372. * @param eZContentObjectVersion $version
  1373. * @param int $newVersionNumber
  1374. * @param int|bool $contentObjectID
  1375. * @param int $status
  1376. * @param string|bool $languageCode If false all languages will be copied, otherwise only specified by the locale code string or an array of the locale code strings.
  1377. * @param string|bool $copyFromLanguageCode
  1378. *
  1379. * @return eZContentObjectVersion
  1380. */
  1381. function copyVersion( &$newObject, &$version, $newVersionNumber, $contentObjectID = false, $status = eZContentObjectVersion::STATUS_DRAFT, $languageCode = false, $copyFromLanguageCode = false )
  1382. {
  1383. $user = eZUser::currentUser();
  1384. $userID = $user->attribute( 'contentobject_id' );
  1385. $nodeAssignmentList = $version->attribute( 'node_assignments' );
  1386. $db = eZDB::instance();
  1387. $db->begin();
  1388. // This is part of the new 3.8 code.
  1389. foreach ( $nodeAssignmentList as $nodeAssignment )
  1390. {
  1391. // Only copy assignments which has a remote_id since it will be used in template code.
  1392. if ( $nodeAssignment->attribute( 'remote_id' ) == 0 )
  1393. {
  1394. continue;
  1395. }
  1396. $clonedAssignment = $nodeAssignment->cloneNodeAssignment( $newVersionNumber, $contentObjectID );
  1397. $clonedAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_SET ); // Make sure op_code is marked to 'set' the data.
  1398. $clonedAssignment->store();
  1399. }
  1400. $currentVersionNumber = $version->attribute( "version" );
  1401. $contentObjectTranslations = $version->translations();
  1402. $clonedVersion = $version->cloneVersion( $newVersionNumber, $userID, $contentObjectID, $status );
  1403. if ( $contentObjectID != false )
  1404. {
  1405. if ( $clonedVersion->attribute( 'status' ) == eZContentObjectVersion::STATUS_PUBLISHED )
  1406. $clonedVersion->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
  1407. }
  1408. $clonedVersion->store();
  1409. // We copy related objects before the attributes, this means that the related objects
  1410. // are available once the datatype code is run.
  1411. $this->copyContentObjectRelations( $currentVersionNumber, $newVersionNumber, $contentObjectID );
  1412. $languageCodeToCopy = false;
  1413. if ( $languageCode && in_array( $languageCode, $this->availableLanguages() ) )
  1414. {
  1415. $languageCodeToCopy = $languageCode;
  1416. }
  1417. if ( $copyFromLanguageCode && in_array( $copyFromLanguageCode, $this->availableLanguages() ) )
  1418. {
  1419. $languageCodeToCopy = $copyFromLanguageCode;
  1420. }
  1421. $haveCopied = false;
  1422. if ( !$languageCode || $languageCodeToCopy )
  1423. {
  1424. foreach ( $contentObjectTranslations as $contentObjectTranslation )
  1425. {
  1426. if ( $languageCode != false && $contentObjectTranslation->attribute( 'language_code' ) != $languageCodeToCopy )
  1427. {
  1428. continue;
  1429. }
  1430. $contentObjectAttributes = $contentObjectTranslation->objectAttributes();
  1431. foreach ( $contentObjectAttributes as $attribute )
  1432. {
  1433. $clonedAttribute = $attribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode );
  1434. $clonedAttribute->sync();
  1435. eZDebugSetting::writeDebug( 'kernel-content-object-copy', $clonedAttribute, 'copyVersion:cloned attribute' );
  1436. }
  1437. $haveCopied = true;
  1438. }
  1439. }
  1440. if ( !$haveCopied && $languageCode )
  1441. {
  1442. $class = $this->contentClass();
  1443. $classAttributes = $class->fetchAttributes();
  1444. foreach ( $classAttributes as $classAttribute )
  1445. {
  1446. if ( $classAttribute->attribute( 'can_translate' ) == 1 )
  1447. {
  1448. $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber );
  1449. }
  1450. else
  1451. {
  1452. // If attribute is NOT Translatable we should check isAlwaysAvailable(),
  1453. // For example,
  1454. // if initial_language_id is 4 and the attribute is always available
  1455. // language_id will be 5 in ezcontentobject_version/ezcontentobject_attribute,
  1456. // this means it uses language ID 4 but also has the bit 0 set to 1 (a reservered bit),
  1457. // You can read about this in the document in doc/features/3.8/.
  1458. $initialLangID = !$this->isAlwaysAvailable() ? $this->attribute( 'initial_language_id' ) : $this->attribute( 'initial_language_id' ) | 1;
  1459. $contentAttribute = eZContentObjectAttribute::fetchByClassAttributeID( $classAttribute->attribute( 'id' ),
  1460. $this->attribute( 'id' ),
  1461. $this->attribute( 'current_version' ),
  1462. $initialLangID );
  1463. if ( $contentAttribute )
  1464. {
  1465. $newAttribute = $contentAttribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode );
  1466. $newAttribute->sync();
  1467. }
  1468. else
  1469. {
  1470. $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber );
  1471. }
  1472. }
  1473. }
  1474. }
  1475. if ( $languageCode )
  1476. {
  1477. $clonedVersion->setAttribute( 'initial_language_id', eZContentLanguage::idByLocale( $languageCode ) );
  1478. $clonedVersion->updateLanguageMask();
  1479. }
  1480. $db->commit();
  1481. return $clonedVersion;
  1482. }
  1483. /**
  1484. * Creates a new content object instance and stores it.
  1485. *
  1486. * @param string $name
  1487. * @param int $contentclassID
  1488. * @param int $userID
  1489. * @param int $sectionID
  1490. * @param int $version
  1491. * @param string|bool $languageCode
  1492. * @return eZContentObject
  1493. */
  1494. static function create( $name, $contentclassID, $userID, $sectionID = 1, $version = 1, $languageCode = false )
  1495. {
  1496. if ( $languageCode == false )
  1497. {
  1498. $languageCode = eZContentObject::defaultLanguage();
  1499. }
  1500. $languageID = eZContentLanguage::idByLocale( $languageCode );
  1501. $row = array(
  1502. "name" => $name,
  1503. "current_version" => $version,
  1504. 'initial_language_id' => $languageID,
  1505. 'language_mask' => $languageID,
  1506. "contentclass_id" => $contentclassID,
  1507. "permission_id" => 1,
  1508. "parent_id" => 0,
  1509. "main_node_id" => 0,
  1510. "owner_id" => $userID,
  1511. "section_id" => $sectionID,
  1512. 'remote_id' => eZRemoteIdUtility::generate( 'object' ) );
  1513. return new eZContentObject( $row );
  1514. }
  1515. /**
  1516. * Resets certain attributes of the content object on clone and resets the data map
  1517. */
  1518. function __clone()
  1519. {
  1520. $this->setAttribute( 'id', null );
  1521. $this->setAttribute( 'published', time() );
  1522. $this->setAttribute( 'modified', time() );
  1523. $this->resetDataMap();
  1524. }
  1525. /**
  1526. * Makes a copy of the object which is stored and then returns it.
  1527. *
  1528. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1529. * the calls within a db transaction; thus within db->begin and db->commit.
  1530. *
  1531. * @param bool $allVersions If true, all versions are copied. If false, only the latest version is copied
  1532. * @return eZContentObject
  1533. */
  1534. function copy( $allVersions = true )
  1535. {
  1536. eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy start, all versions=' . ( $allVersions ? 'true' : 'false' ), 'copy' );
  1537. $user = eZUser::currentUser();
  1538. $userID = $user->attribute( 'contentobject_id' );
  1539. $contentObject = clone $this;
  1540. $contentObject->setAttribute( 'current_version', 1 );
  1541. $contentObject->setAttribute( 'owner_id', $userID );
  1542. $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
  1543. $db = eZDB::instance();
  1544. $db->begin();
  1545. $contentObject->store();
  1546. $originalObjectID = $this->attribute( 'id' );
  1547. $contentObjectID = $contentObject->attribute( 'id' );
  1548. $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id)
  1549. SELECT contentobject_state_id, $contentObjectID FROM ezcobj_state_link WHERE contentobject_id = $originalObjectID" );
  1550. $contentObject->setName( $this->attribute('name') );
  1551. eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObject, 'contentObject' );
  1552. $versionList = array();
  1553. if ( $allVersions )
  1554. {
  1555. $versions = $this->versions();
  1556. foreach( $versions as $version )
  1557. {
  1558. $versionList[$version->attribute( 'version' )] = $version;
  1559. }
  1560. }
  1561. else
  1562. {
  1563. $versionList[1] = $this->currentVersion();
  1564. }
  1565. foreach ( $versionList as $versionNumber => $currentContentObjectVersion )
  1566. {
  1567. $currentVersionNumber = $currentContentObjectVersion->attribute( 'version' );
  1568. $contentObject->setName( $currentContentObjectVersion->name(), $versionNumber );
  1569. foreach( $contentObject->translationStringList() as $languageCode )
  1570. {
  1571. $contentObject->setName( $currentContentObjectVersion->name( false, $languageCode ), $versionNumber, $languageCode );
  1572. }
  1573. $contentObjectVersion = $this->copyVersion( $contentObject, $currentContentObjectVersion,
  1574. $versionNumber, $contentObject->attribute( 'id' ),
  1575. false );
  1576. if ( $currentVersionNumber == $this->attribute( 'current_version' ) )
  1577. {
  1578. $parentMap = array();
  1579. $copiedNodeAssignmentList = $contentObjectVersion->attribute( 'node_assignments' );
  1580. foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment )
  1581. {
  1582. $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment;
  1583. }
  1584. // Create node-assignment from all current published nodes
  1585. $nodes = $this->assignedNodes();
  1586. foreach( $nodes as $node )
  1587. {
  1588. $remoteID = eZRemoteIdUtility::generate( 'object' );
  1589. // Remove assignments which conflicts with existing nodes, but keep remote_id
  1590. if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) )
  1591. {
  1592. $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )];
  1593. unset( $parentMap[$node->attribute( 'parent_node_id' )] );
  1594. $remoteID = $copiedNodeAssignment->attribute( 'remote_id' );
  1595. $copiedNodeAssignment->purge();
  1596. }
  1597. $contentObjectVersion->assignToNode(
  1598. $node->attribute( 'parent_node_id' ),
  1599. $node->attribute( 'is_main' ),
  1600. 0,
  1601. $node->attribute( 'sort_field' ),
  1602. $node->attribute( 'sort_order' ),
  1603. $remoteID
  1604. );
  1605. }
  1606. }
  1607. eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObjectVersion, 'Copied version' );
  1608. }
  1609. // Set version number
  1610. if ( $allVersions )
  1611. $contentObject->setAttribute( 'current_version', $this->attribute( 'current_version' ) );
  1612. $contentObject->setAttribute( 'status', eZContentObject::STATUS_DRAFT );
  1613. $contentObject->store();
  1614. $db->commit();
  1615. eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy done', 'copy' );
  1616. return $contentObject;
  1617. }
  1618. /**
  1619. * Copies the given version of the object and creates a new current version.
  1620. *
  1621. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1622. * the calls within a db transaction; thus within db->begin and db->commit.
  1623. *
  1624. * @param int $version
  1625. * @param string|bool $language
  1626. *
  1627. * @return int The new version number
  1628. */
  1629. function copyRevertTo( $version, $language = false )
  1630. {
  1631. $versionObject = $this->createNewVersionIn( $language, false, $version );
  1632. return $versionObject->attribute( 'version' );
  1633. }
  1634. /**
  1635. * Fixes reverse relations
  1636. *
  1637. * @see eZObjectRelationListType::fixRelatedObjectItem()
  1638. *
  1639. * @param int $objectID
  1640. * @param string|bool $mode See eZObjectRelationListType::fixRelatedObjectItem() for valid modes
  1641. * @param bool $clearCacheIfEnabled If set to false cache won't be cleared
  1642. */
  1643. static function fixReverseRelations( $objectID, $mode = false, $clearCacheIfEnabled = true )
  1644. {
  1645. $db = eZDB::instance();
  1646. $objectID = (int) $objectID;
  1647. // Finds all the attributes that store relations to the given object.
  1648. $result = $db->arrayQuery( "SELECT attr.*
  1649. FROM ezcontentobject_link link,
  1650. ezcontentobject_attribute attr
  1651. WHERE link.from_contentobject_id=attr.contentobject_id AND
  1652. link.from_contentobject_version=attr.version AND
  1653. link.contentclassattribute_id=attr.contentclassattribute_id AND
  1654. link.to_contentobject_id=$objectID" );
  1655. if ( count( $result ) > 0 )
  1656. {
  1657. $objectIDList = array();
  1658. foreach( $result as $row )
  1659. {
  1660. $attr = new eZContentObjectAttribute( $row );
  1661. $dataType = $attr->dataType();
  1662. $dataType->fixRelatedObjectItem( $attr, $objectID, $mode );
  1663. $objectIDList[] = $attr->attribute( 'contentobject_id' );
  1664. }
  1665. if ( $clearCacheIfEnabled && eZINI::instance()->variable( 'ContentSettings', 'ViewCaching' ) === 'enabled' )
  1666. eZContentCacheManager::clearObjectViewCacheArray( $objectIDList );
  1667. }
  1668. }
  1669. /**
  1670. * Deletes the current object, all versions and translations, and corresponding tree nodes from the database
  1671. *
  1672. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1673. * the calls within a db transaction; thus within db->begin and db->commit.
  1674. */
  1675. function purge()
  1676. {
  1677. $delID = $this->ID;
  1678. // Who deletes which content should be logged.
  1679. eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ),
  1680. 'Comment' => 'Purged the current object: eZContentObject::purge()' ) );
  1681. $db = eZDB::instance();
  1682. $db->begin();
  1683. $attrOffset = 0;
  1684. $attrLimit = 20;
  1685. while (
  1686. $contentobjectAttributes = $this->allContentObjectAttributes(
  1687. $delID, true, array( 'limit' => $attrLimit, 'offset' => $attrOffset )
  1688. )
  1689. )
  1690. {
  1691. foreach ( $contentobjectAttributes as $contentobjectAttribute )
  1692. {
  1693. $dataType = $contentobjectAttribute->dataType();
  1694. if ( !$dataType )
  1695. continue;
  1696. $dataType->deleteStoredObjectAttribute( $contentobjectAttribute );
  1697. }
  1698. $attrOffset += $attrLimit;
  1699. }
  1700. eZInformationCollection::removeContentObject( $delID );
  1701. eZContentObjectTrashNode::purgeForObject( $delID );
  1702. $db->query( "DELETE FROM ezcontentobject_tree
  1703. WHERE contentobject_id='$delID'" );
  1704. $db->query( "DELETE FROM ezcontentobject_attribute
  1705. WHERE contentobject_id='$delID'" );
  1706. $db->query( "DELETE FROM ezcontentobject_version
  1707. WHERE contentobject_id='$delID'" );
  1708. $db->query( "DELETE FROM ezcontentobject_name
  1709. WHERE contentobject_id='$delID'" );
  1710. $db->query( "DELETE FROM ezcobj_state_link WHERE contentobject_id=$delID" );
  1711. $db->query( "DELETE FROM ezcontentobject
  1712. WHERE id='$delID'" );
  1713. $db->query( "DELETE FROM eznode_assignment
  1714. WHERE contentobject_id = '$delID'" );
  1715. $db->query( "DELETE FROM ezuser_role
  1716. WHERE contentobject_id = '$delID'" );
  1717. $db->query( "DELETE FROM ezuser_discountrule
  1718. WHERE contentobject_id = '$delID'" );
  1719. eZContentObject::fixReverseRelations( $delID, 'remove' );
  1720. eZSearch::removeObjectById( $delID );
  1721. // Check if deleted object is in basket/wishlist
  1722. $sql = 'SELECT DISTINCT ezproductcollection_item.productcollection_id
  1723. FROM ezbasket, ezwishlist, ezproductcollection_item
  1724. WHERE ( ezproductcollection_item.productcollection_id=ezbasket.productcollection_id OR
  1725. ezproductcollection_item.productcollection_id=ezwishlist.productcollection_id ) AND
  1726. ezproductcollection_item.contentobject_id=' . $delID;
  1727. $rows = $db->arrayQuery( $sql );
  1728. if ( count( $rows ) > 0 )
  1729. {
  1730. $countElements = 50;
  1731. $deletedArray = array();
  1732. // Create array of productCollectionID will be removed from ezwishlist and ezproductcollection_item
  1733. foreach ( $rows as $row )
  1734. {
  1735. $deletedArray[] = $row['productcollection_id'];
  1736. }
  1737. // Split $deletedArray into several arrays with $countElements values
  1738. $splitted = array_chunk( $deletedArray, $countElements );
  1739. // Remove eZProductCollectionItem and eZWishList
  1740. foreach ( $splitted as $value )
  1741. {
  1742. eZPersistentObject::removeObject( eZProductCollectionItem::definition(), array( 'productcollection_id' => array( $value, '' ) ) );
  1743. eZPersistentObject::removeObject( eZWishList::definition(), array( 'productcollection_id' => array( $value, '' ) ) );
  1744. }
  1745. }
  1746. $db->query( 'UPDATE ezproductcollection_item
  1747. SET contentobject_id = 0
  1748. WHERE contentobject_id = ' . $delID );
  1749. // Cleanup relations in two steps to avoid locking table for to long
  1750. $db->query( "DELETE FROM ezcontentobject_link
  1751. WHERE from_contentobject_id = '$delID'" );
  1752. $db->query( "DELETE FROM ezcontentobject_link
  1753. WHERE to_contentobject_id = '$delID'" );
  1754. // Cleanup properties: LastVisit, Creator, Owner
  1755. $db->query( "DELETE FROM ezuservisit
  1756. WHERE user_id = '$delID'" );
  1757. $db->query( "UPDATE ezcontentobject_version
  1758. SET creator_id = 0
  1759. WHERE creator_id = '$delID'" );
  1760. $db->query( "UPDATE ezcontentobject
  1761. SET owner_id = 0
  1762. WHERE owner_id = '$delID'" );
  1763. if ( isset( $GLOBALS["eZWorkflowTypeObjects"] ) and is_array( $GLOBALS["eZWorkflowTypeObjects"] ) )
  1764. {
  1765. $registeredTypes =& $GLOBALS["eZWorkflowTypeObjects"];
  1766. }
  1767. else
  1768. {
  1769. $registeredTypes = eZWorkflowType::fetchRegisteredTypes();
  1770. }
  1771. // Cleanup ezworkflow_event etc...
  1772. foreach ( array_keys( $registeredTypes ) as $registeredTypeKey )
  1773. {
  1774. $registeredType = $registeredTypes[$registeredTypeKey];
  1775. $registeredType->cleanupAfterRemoving( array( 'DeleteContentObject' => $delID ) );
  1776. }
  1777. $db->commit();
  1778. }
  1779. /**
  1780. * Archives the current object and removes assigned nodes
  1781. *
  1782. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  1783. * the calls within a db transaction; thus within db->begin and db->commit.
  1784. *
  1785. * @param int $nodeID
  1786. */
  1787. function removeThis( $nodeID = null )
  1788. {
  1789. $delID = $this->ID;
  1790. // Who deletes which content should be logged.
  1791. eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ),
  1792. 'Comment' => 'Setted archived status for the current object: eZContentObject::remove()' ) );
  1793. $nodes = $this->attribute( 'assigned_nodes' );
  1794. if ( $nodeID === null or count( $nodes ) <= 1 )
  1795. {
  1796. $db = eZDB::instance();
  1797. $db->begin();
  1798. $mainNodeKey = false;
  1799. foreach ( $nodes as $key => $node )
  1800. {
  1801. if ( $node->attribute( 'main_node_id' ) == $node->attribute( 'node_id' ) )
  1802. {
  1803. $mainNodeKey = $key;
  1804. }
  1805. else
  1806. {
  1807. $node->removeThis();
  1808. }
  1809. }
  1810. if ( $mainNodeKey !== false )
  1811. {
  1812. $nodes[$mainNodeKey]->removeNodeFromTree( true );
  1813. }
  1814. $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED );
  1815. eZSearch::removeObjectById( $delID );
  1816. $this->store();
  1817. eZContentObject::fixReverseRelations( $delID, 'trash' );
  1818. // Delete stored attribute from other tables
  1819. $db->commit();
  1820. }
  1821. else if ( $nodeID !== null )
  1822. {
  1823. $node = eZContentObjectTreeNode::fetch( $nodeID , false );
  1824. if ( is_object( $node ) )
  1825. {
  1826. if ( $node->attribute( 'main_node_id' ) == $nodeID )
  1827. {
  1828. $db = eZDB::instance();
  1829. $db->begin();
  1830. foreach ( $nodes as $additionalNode )
  1831. {
  1832. if ( $additionalNode->attribute( 'node_id' ) != $node->attribute( 'main_node_id' ) )
  1833. {
  1834. $additionalNode->remove();
  1835. }
  1836. }
  1837. $node->removeNodeFromTree( true );
  1838. $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED );
  1839. eZSearch::removeObjectById( $delID );
  1840. $this->store();
  1841. eZContentObject::fixReverseRelations( $delID, 'trash' );
  1842. $db->commit();
  1843. }
  1844. else
  1845. {
  1846. eZContentObjectTreeNode::removeNode( $nodeID );
  1847. }
  1848. }
  1849. }
  1850. else
  1851. {
  1852. eZContentObjectTreeNode::removeNode( $nodeID );
  1853. }
  1854. }
  1855. /**
  1856. * Removes old internal drafts by the specified user associated for the past time span given by $timeDuration
  1857. *
  1858. * @param int|bool $userID The ID of the user to cleanup for, if false it will use the current user.
  1859. * @param int $timeDuration default time duration for internal drafts 60*60*24 seconds (1 day)
  1860. */
  1861. function cleanupInternalDrafts( $userID = false, $timeDuration = 86400 )
  1862. {
  1863. if ( !is_numeric( $timeDuration ) ||
  1864. $timeDuration < 0 )
  1865. {
  1866. eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ );
  1867. return;
  1868. }
  1869. if ( $userID === false )
  1870. {
  1871. $userID = eZUser::currentUserID();
  1872. }
  1873. // Fetch all draft/temporary versions by specified user
  1874. $parameters = array( 'conditions' => array( 'status' => eZContentObjectVersion::STATUS_INTERNAL_DRAFT,
  1875. 'creator_id' => $userID ) );
  1876. // Remove temporary drafts which are old.
  1877. $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day)
  1878. foreach ( $this->versions( true, $parameters ) as $possibleVersion )
  1879. {
  1880. if ( $possibleVersion->attribute( 'modified' ) < $expiryTime )
  1881. {
  1882. $possibleVersion->removeThis();
  1883. }
  1884. }
  1885. }
  1886. /**
  1887. * Removes all old internal drafts by the specified user for the past time span given by $timeDuration
  1888. *
  1889. * @param int|bool $userID The ID of the user to cleanup for, if false it will use the current user.
  1890. * @param int $timeDuration default time duration for internal drafts 60*60*24 seconds (1 day)
  1891. */
  1892. static function cleanupAllInternalDrafts( $userID = false, $timeDuration = 86400 ) //
  1893. {
  1894. if ( !is_numeric( $timeDuration ) ||
  1895. $timeDuration < 0 )
  1896. {
  1897. eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ );
  1898. return;
  1899. }
  1900. if ( $userID === false )
  1901. {
  1902. $userID = eZUser::currentUserID();
  1903. }
  1904. // Remove all internal drafts
  1905. $untouchedDrafts = eZContentObjectVersion::fetchForUser( $userID, eZContentObjectVersion::STATUS_INTERNAL_DRAFT );
  1906. $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day)
  1907. foreach ( $untouchedDrafts as $untouchedDraft )
  1908. {
  1909. if ( $untouchedDraft->attribute( 'modified' ) < $expiryTime )
  1910. {
  1911. $untouchedDraft->removeThis();
  1912. }
  1913. }
  1914. }
  1915. /**
  1916. * Fetches all attributes from any versions of the content object
  1917. *
  1918. * @param int $contentObjectID
  1919. * @param bool $asObject
  1920. * @param array|null $limit the limit array passed to {@see eZPersistentObject::fetchObjectList}
  1921. * @return eZContentObjectAttribute[]|array|null
  1922. */
  1923. function allContentObjectAttributes( $contentObjectID, $asObject = true, $limit = null )
  1924. {
  1925. return eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(),
  1926. null,
  1927. array("contentobject_id" => $contentObjectID ),
  1928. null,
  1929. $limit,
  1930. $asObject );
  1931. }
  1932. /**
  1933. * Fetches the attributes for the current published version of the object.
  1934. *
  1935. * @todo fix using of $asObject parameter
  1936. * @todo fix condition for getting attribute from cache
  1937. * @todo probably need to move method to eZContentObjectVersion class
  1938. *
  1939. * @param bool $asObject
  1940. * @param int|bool $version
  1941. * @param string|bool $language
  1942. * @param int|bool $contentObjectAttributeID
  1943. * @param bool $distinctItemsOnly
  1944. * @return eZContentObjectAttribute[]|array
  1945. */
  1946. function contentObjectAttributes( $asObject = true, $version = false, $language = false, $contentObjectAttributeID = false, $distinctItemsOnly = false )
  1947. {
  1948. $db = eZDB::instance();
  1949. if ( $version == false )
  1950. {
  1951. $version = $this->CurrentVersion;
  1952. }
  1953. else
  1954. {
  1955. $version = (int) $version;
  1956. }
  1957. if ( $language === false )
  1958. {
  1959. $language = $this->CurrentLanguage;
  1960. }
  1961. if ( is_string( $language ) )
  1962. $language = $db->escapeString( $language );
  1963. if ( $contentObjectAttributeID !== false )
  1964. $contentObjectAttributeID =(int) $contentObjectAttributeID;
  1965. if ( !$language || !isset( $this->ContentObjectAttributes[$version][$language] ) )
  1966. {
  1967. $versionText = "AND ezcontentobject_attribute.version = '$version'";
  1968. if ( $language )
  1969. {
  1970. $languageText = "AND ezcontentobject_attribute.language_code = '$language'";
  1971. }
  1972. else
  1973. {
  1974. $languageText = "AND ".eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' );
  1975. }
  1976. $attributeIDText = false;
  1977. if ( $contentObjectAttributeID )
  1978. $attributeIDText = "AND ezcontentobject_attribute.id = '$contentObjectAttributeID'";
  1979. $distinctText = false;
  1980. if ( $distinctItemsOnly )
  1981. $distinctText = "GROUP BY ezcontentobject_attribute.id";
  1982. $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM
  1983. ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version
  1984. WHERE
  1985. ezcontentclass_attribute.version = '0' AND
  1986. ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
  1987. ezcontentobject_version.contentobject_id = '$this->ID' AND
  1988. ezcontentobject_version.version = '$version' AND
  1989. ezcontentobject_attribute.contentobject_id = '$this->ID' $versionText $languageText $attributeIDText
  1990. $distinctText
  1991. ORDER BY
  1992. ezcontentclass_attribute.placement ASC,
  1993. ezcontentobject_attribute.language_code ASC";
  1994. $attributeArray = $db->arrayQuery( $query );
  1995. if ( !$language && $attributeArray )
  1996. {
  1997. $language = $attributeArray[0]['language_code'];
  1998. $this->CurrentLanguage = $language;
  1999. }
  2000. $returnAttributeArray = array();
  2001. foreach ( $attributeArray as $attribute )
  2002. {
  2003. $attr = new eZContentObjectAttribute( $attribute );
  2004. $attr->setContentClassAttributeIdentifier( $attribute['identifier'] );
  2005. $returnAttributeArray[] = $attr;
  2006. }
  2007. if ( $language !== null and $version !== null )
  2008. {
  2009. $this->ContentObjectAttributes[$version][$language] = $returnAttributeArray;
  2010. }
  2011. }
  2012. else
  2013. {
  2014. $returnAttributeArray = $this->ContentObjectAttributes[$version][$language];
  2015. }
  2016. return $returnAttributeArray;
  2017. }
  2018. /**
  2019. * Initializes the cached copy of the content object attributes for the given version and language
  2020. *
  2021. * @param eZContentObjectAttribute[] $attributes
  2022. * @param int $version
  2023. * @param string $language
  2024. */
  2025. function setContentObjectAttributes( &$attributes, $version, $language )
  2026. {
  2027. $this->ContentObjectAttributes[$version][$language] = $attributes;
  2028. }
  2029. /**
  2030. * Fetches the attributes for an array of objects or nodes
  2031. *
  2032. * @param eZContentObject[]|eZContentObjectTreeNode[] $objList
  2033. * @param bool $asObject
  2034. */
  2035. static function fillNodeListAttributes( $objList, $asObject = true )
  2036. {
  2037. $db = eZDB::instance();
  2038. if ( count( $objList ) > 0 )
  2039. {
  2040. $objectArray = array();
  2041. $tmpLanguageObjectList = array();
  2042. $whereSQL = '';
  2043. $count = count( $objList );
  2044. $i = 0;
  2045. foreach ( $objList as $obj )
  2046. {
  2047. if ( $obj instanceOf eZContentObject )
  2048. $object = $obj;
  2049. else
  2050. $object = $obj->attribute( 'object' );
  2051. $language = $object->currentLanguage();
  2052. $tmpLanguageObjectList[$object->attribute( 'id' )] = $language;
  2053. $objectArray = array( 'id' => $object->attribute( 'id' ),
  2054. 'language' => $language,
  2055. 'version' => $object->attribute( 'current_version' ) );
  2056. $whereSQL .= "( ezcontentobject_attribute.version = '" . $object->attribute( 'current_version' ) . "' AND
  2057. ezcontentobject_attribute.contentobject_id = '" . $object->attribute( 'id' ) . "' AND
  2058. ezcontentobject_attribute.language_code = '" . $language . "' ) ";
  2059. $i++;
  2060. if ( $i < $count )
  2061. $whereSQL .= ' OR ';
  2062. }
  2063. $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM
  2064. ezcontentobject_attribute, ezcontentclass_attribute
  2065. WHERE
  2066. ezcontentclass_attribute.version = '0' AND
  2067. ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND
  2068. ( $whereSQL )
  2069. ORDER BY
  2070. ezcontentobject_attribute.contentobject_id, ezcontentclass_attribute.placement ASC";
  2071. $attributeArray = $db->arrayQuery( $query );
  2072. $tmpAttributeObjectList = array();
  2073. $returnAttributeArray = array();
  2074. foreach ( $attributeArray as $attribute )
  2075. {
  2076. $attr = new eZContentObjectAttribute( $attribute );
  2077. $attr->setContentClassAttributeIdentifier( $attribute['identifier'] );
  2078. $tmpAttributeObjectList[$attr->attribute( 'contentobject_id' )][] = $attr;
  2079. }
  2080. foreach ( $objList as $obj )
  2081. {
  2082. if ( $obj instanceOf eZContentObject )
  2083. {
  2084. $obj->setContentObjectAttributes( $tmpAttributeObjectList[$obj->attribute( 'id' )],
  2085. $obj->attribute( 'current_version' ),
  2086. $tmpLanguageObjectList[$obj->attribute( 'id' )] );
  2087. }
  2088. else
  2089. {
  2090. $object = $obj->attribute( 'object' );
  2091. $object->setContentObjectAttributes( $tmpAttributeObjectList[$object->attribute( 'id' )],
  2092. $object->attribute( 'current_version' ),
  2093. $tmpLanguageObjectList[$object->attribute( 'id' )] );
  2094. $obj->setContentObject( $object );
  2095. }
  2096. }
  2097. }
  2098. }
  2099. /**
  2100. * Resets the object's input relation list
  2101. */
  2102. function resetInputRelationList()
  2103. {
  2104. $this->InputRelationList = array( eZContentObject::RELATION_EMBED => array(),
  2105. eZContentObject::RELATION_LINK => array() );
  2106. }
  2107. /**
  2108. * @param int[] $addingIDList
  2109. * @param int $relationType
  2110. */
  2111. function appendInputRelationList( $addingIDList, $relationType )
  2112. {
  2113. if ( !is_array( $addingIDList ) )
  2114. {
  2115. $addingIDList = array( ( int ) $addingIDList );
  2116. }
  2117. elseif ( !count( $addingIDList ) )
  2118. {
  2119. return;
  2120. }
  2121. $relationType = ( int ) $relationType;
  2122. if ( !$this->InputRelationList )
  2123. {
  2124. $this->resetInputRelationList();
  2125. }
  2126. foreach ( array_keys( $this->InputRelationList ) as $inputRelationType )
  2127. {
  2128. if ( $inputRelationType & $relationType )
  2129. {
  2130. $this->InputRelationList[$inputRelationType] = array_merge( $this->InputRelationList[$inputRelationType], $addingIDList );
  2131. }
  2132. }
  2133. }
  2134. /**
  2135. * @param int|bool $editVersion
  2136. * @return bool
  2137. */
  2138. function commitInputRelations( $editVersion )
  2139. {
  2140. foreach ( $this->InputRelationList as $relationType => $relatedObjectIDArray )
  2141. {
  2142. $oldRelatedObjectArray = $this->relatedObjects( $editVersion, false, 0, false, array( 'AllRelations' => $relationType ) );
  2143. foreach ( $oldRelatedObjectArray as $oldRelatedObject )
  2144. {
  2145. $oldRelatedObjectID = $oldRelatedObject->ID;
  2146. if ( !in_array( $oldRelatedObjectID, $relatedObjectIDArray ) )
  2147. {
  2148. $this->removeContentObjectRelation( $oldRelatedObjectID, $editVersion, 0, $relationType );
  2149. }
  2150. $relatedObjectIDArray = array_diff( $relatedObjectIDArray, array( $oldRelatedObjectID ) );
  2151. }
  2152. foreach ( $relatedObjectIDArray as $relatedObjectID )
  2153. {
  2154. $this->addContentObjectRelation( $relatedObjectID, $editVersion, 0, $relationType );
  2155. }
  2156. }
  2157. return true;
  2158. }
  2159. /**
  2160. * @param eZContentObjectAttribute[] $contentObjectAttributes
  2161. * @param string $attributeDataBaseName
  2162. * @param array|bool $inputParameters
  2163. * @param array $parameters
  2164. * @return array
  2165. */
  2166. function validateInput( $contentObjectAttributes, $attributeDataBaseName,
  2167. $inputParameters = false, $parameters = array() )
  2168. {
  2169. $result = array( 'unvalidated-attributes' => array(),
  2170. 'validated-attributes' => array(),
  2171. 'status-map' => array(),
  2172. 'require-fixup' => false,
  2173. 'input-validated' => true );
  2174. $parameters = array_merge( array( 'prefix-name' => false ),
  2175. $parameters );
  2176. if ( $inputParameters )
  2177. {
  2178. $result['unvalidated-attributes'] =& $inputParameters['unvalidated-attributes'];
  2179. $result['validated-attributes'] =& $inputParameters['validated-attributes'];
  2180. }
  2181. $unvalidatedAttributes =& $result['unvalidated-attributes'];
  2182. $validatedAttributes =& $result['validated-attributes'];
  2183. $statusMap =& $result['status-map'];
  2184. if ( !$inputParameters )
  2185. $inputParameters = array( 'unvalidated-attributes' => &$unvalidatedAttributes,
  2186. 'validated-attributes' => &$validatedAttributes );
  2187. $requireFixup =& $result['require-fixup'];
  2188. $inputValidated =& $result['input-validated'];
  2189. $http = eZHTTPTool::instance();
  2190. $this->resetInputRelationList();
  2191. $editVersion = null;
  2192. $defaultLanguage = $this->initialLanguageCode();
  2193. foreach( $contentObjectAttributes as $contentObjectAttribute )
  2194. {
  2195. $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
  2196. $editVersion = $contentObjectAttribute->attribute('version');
  2197. // Check if this is a translation
  2198. $currentLanguage = $contentObjectAttribute->attribute( 'language_code' );
  2199. $isTranslation = false;
  2200. if ( $currentLanguage != $defaultLanguage )
  2201. $isTranslation = true;
  2202. // If current attribute is a translation
  2203. // Check if this attribute can be translated
  2204. // If not do not validate, since the input will be copyed from the original
  2205. $doNotValidate = false;
  2206. if ( $isTranslation )
  2207. {
  2208. if ( !$contentClassAttribute->attribute( 'can_translate' ) )
  2209. $doNotValidate = true;
  2210. }
  2211. if ( $doNotValidate == true )
  2212. {
  2213. $status = eZInputValidator::STATE_ACCEPTED;
  2214. }
  2215. else
  2216. {
  2217. $status = $contentObjectAttribute->validateInput( $http, $attributeDataBaseName,
  2218. $inputParameters, $parameters );
  2219. }
  2220. $statusMap[$contentObjectAttribute->attribute( 'id' )] = array( 'value' => $status,
  2221. 'attribute' => $contentObjectAttribute );
  2222. if ( $status == eZInputValidator::STATE_INTERMEDIATE )
  2223. $requireFixup = true;
  2224. else if ( $status == eZInputValidator::STATE_INVALID )
  2225. {
  2226. $inputValidated = false;
  2227. $dataType = $contentObjectAttribute->dataType();
  2228. $attributeName = $dataType->attribute( 'information' );
  2229. $attributeName = $attributeName['name'];
  2230. $description = $contentObjectAttribute->attribute( 'validation_error' );
  2231. $validationNameArray[] = $contentClassAttribute->attribute( 'name' );
  2232. $validationName = implode( '->', $validationNameArray );
  2233. $hasValidationError = $contentObjectAttribute->attribute( 'has_validation_error' );
  2234. if ( $hasValidationError )
  2235. {
  2236. if ( !$description )
  2237. $description = false;
  2238. $validationNameArray = array();
  2239. if ( $parameters['prefix-name'] )
  2240. $validationNameArray = $parameters['prefix-name'];
  2241. }
  2242. else
  2243. {
  2244. if ( !$description )
  2245. $description = 'unknown error';
  2246. }
  2247. $unvalidatedAttributes[] = array( 'id' => $contentObjectAttribute->attribute( 'id' ),
  2248. 'identifier' => $contentClassAttribute->attribute( 'identifier' ),
  2249. 'name' => $validationName,
  2250. 'description' => $description );
  2251. }
  2252. else if ( $status == eZInputValidator::STATE_ACCEPTED )
  2253. {
  2254. $dataType = $contentObjectAttribute->dataType();
  2255. $attributeName = $dataType->attribute( 'information' );
  2256. $attributeName = $attributeName['name'];
  2257. if ( $contentObjectAttribute->attribute( 'validation_log' ) != null )
  2258. {
  2259. $description = $contentObjectAttribute->attribute( 'validation_log' );
  2260. if ( !$description )
  2261. $description = false;
  2262. $validationName = $contentClassAttribute->attribute( 'name' );
  2263. if ( $parameters['prefix-name'] )
  2264. $validationName = $parameters['prefix-name'] . '->' . $validationName;
  2265. $validatedAttributes[] = array( 'id' => $contentObjectAttribute->attribute( 'id' ),
  2266. 'identifier' => $contentClassAttribute->attribute( 'identifier' ),
  2267. 'name' => $validationName,
  2268. 'description' => $description );
  2269. }
  2270. }
  2271. }
  2272. if ( $editVersion !== null )
  2273. {
  2274. $this->commitInputRelations( $editVersion );
  2275. }
  2276. $this->resetInputRelationList();
  2277. return $result;
  2278. }
  2279. /**
  2280. * @param eZContentObjectAttribute[] $contentObjectAttributes
  2281. * @param string $attributeDataBaseName
  2282. */
  2283. function fixupInput( $contentObjectAttributes, $attributeDataBaseName )
  2284. {
  2285. $http = eZHTTPTool::instance();
  2286. foreach ( $contentObjectAttributes as $contentObjectAttribute )
  2287. {
  2288. $contentObjectAttribute->fixupInput( $http, $attributeDataBaseName );
  2289. }
  2290. }
  2291. /**
  2292. * @param eZContentObjectAttribute[] $contentObjectAttributes
  2293. * @param string $attributeDataBaseName
  2294. * @param array $customActionAttributeArray
  2295. * @param array $customActionParameters
  2296. * @return array
  2297. */
  2298. function fetchInput( $contentObjectAttributes, $attributeDataBaseName,
  2299. $customActionAttributeArray, $customActionParameters )
  2300. {
  2301. // Global variable to cache datamaps
  2302. global $eZContentObjectDataMapCache;
  2303. $result = array( 'attribute-input-map' => array() );
  2304. $attributeInputMap =& $result['attribute-input-map'];
  2305. $http = eZHTTPTool::instance();
  2306. $defaultLanguage = $this->initialLanguageCode();
  2307. $this->fetchDataMap();
  2308. foreach ( $contentObjectAttributes as $contentObjectAttribute )
  2309. {
  2310. $contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
  2311. // Check if this is a translation
  2312. $currentLanguage = $contentObjectAttribute->attribute( 'language_code' );
  2313. $isTranslation = false;
  2314. if ( $currentLanguage != $defaultLanguage )
  2315. $isTranslation = true;
  2316. // If current attribute is an un-translateable translation, input should not be fetched
  2317. $fetchInput = true;
  2318. if ( $isTranslation == true )
  2319. {
  2320. if ( !$contentClassAttribute->attribute( 'can_translate' ) )
  2321. {
  2322. $fetchInput = false;
  2323. }
  2324. }
  2325. // Do not handle input for non-translateable attributes.
  2326. // Input will be copyed from the std. translation on storage
  2327. if ( $fetchInput )
  2328. {
  2329. if ( $contentObjectAttribute->fetchInput( $http, $attributeDataBaseName ) )
  2330. {
  2331. $attributeInputMap[$contentObjectAttribute->attribute('id')] = true;
  2332. // we fill the internal data map cache for the current version here with the attributes of the new version
  2333. // this will make the data map cache inconsistent, but this is required to make it possible to use $object.data_map
  2334. // in content/edit templates
  2335. $attributeIdentifier = $contentObjectAttribute->attribute( 'contentclass_attribute_identifier' );
  2336. $eZContentObjectDataMapCache[$this->ID][$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute;
  2337. $this->DataMap[$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute;
  2338. }
  2339. // Custom Action Code
  2340. $this->handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName,
  2341. $customActionAttributeArray, $customActionParameters );
  2342. }
  2343. }
  2344. return $result;
  2345. }
  2346. /**
  2347. * @param eZContentObjectAttribute $contentObjectAttribute
  2348. * @param string $attributeDataBaseName
  2349. * @param array $customActionAttributeArray
  2350. * @param array $customActionParameters
  2351. */
  2352. function handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName,
  2353. $customActionAttributeArray, $customActionParameters )
  2354. {
  2355. $http = eZHTTPTool::instance();
  2356. $customActionParameters['base_name'] = $attributeDataBaseName;
  2357. if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) )
  2358. {
  2359. $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id'];
  2360. $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value'];
  2361. $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters );
  2362. }
  2363. $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName,
  2364. $customActionAttributeArray, $customActionParameters );
  2365. }
  2366. /**
  2367. * @param string $attributeDataBaseName
  2368. * @param array $customActionAttributeArray
  2369. * @param array $customActionParameters
  2370. * @param int|bool $objectVersion
  2371. */
  2372. function handleAllCustomHTTPActions( $attributeDataBaseName,
  2373. $customActionAttributeArray, $customActionParameters,
  2374. $objectVersion = false )
  2375. {
  2376. $http = eZHTTPTool::instance();
  2377. $contentObjectAttributes = $this->contentObjectAttributes( true, $objectVersion );
  2378. $oldAttributeDataBaseName = $customActionParameters['base_name'];
  2379. $customActionParameters['base_name'] = $attributeDataBaseName;
  2380. foreach( $contentObjectAttributes as $contentObjectAttribute )
  2381. {
  2382. if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) )
  2383. {
  2384. $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id'];
  2385. $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value'];
  2386. $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters );
  2387. }
  2388. $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName,
  2389. $customActionAttributeArray, $customActionParameters );
  2390. }
  2391. $customActionParameters['base_name'] = $oldAttributeDataBaseName;
  2392. }
  2393. /**
  2394. *
  2395. */
  2396. static function recursionProtectionStart()
  2397. {
  2398. $GLOBALS["ez_content_object_recursion_protect"] = array();
  2399. }
  2400. /**
  2401. * @param int $id
  2402. * @return bool
  2403. */
  2404. static function recursionProtect( $id )
  2405. {
  2406. if ( isset( $GLOBALS["ez_content_object_recursion_protect"][$id] ) )
  2407. {
  2408. return false;
  2409. }
  2410. else
  2411. {
  2412. $GLOBALS["ez_content_object_recursion_protect"][$id] = true;
  2413. return true;
  2414. }
  2415. }
  2416. /**
  2417. *
  2418. */
  2419. static function recursionProtectionEnd()
  2420. {
  2421. unset( $GLOBALS["ez_content_object_recursion_protect"] );
  2422. }
  2423. /**
  2424. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2425. * the calls within a db transaction; thus within db->begin and db->commit.
  2426. *
  2427. * @param eZContentObjectAttribute[] $contentObjectAttributes
  2428. * @param array $attributeInputMap
  2429. */
  2430. function storeInput( $contentObjectAttributes,
  2431. $attributeInputMap )
  2432. {
  2433. $db = eZDB::instance();
  2434. $db->begin();
  2435. foreach ( $contentObjectAttributes as $contentObjectAttribute )
  2436. {
  2437. if ( isset( $attributeInputMap[$contentObjectAttribute->attribute('id')] ) )
  2438. {
  2439. $contentObjectAttribute->store();
  2440. }
  2441. }
  2442. $db->commit();
  2443. unset( $this->ContentObjectAttributes );
  2444. }
  2445. /**
  2446. * Returns the next available version number for this object.
  2447. *
  2448. * @return int
  2449. */
  2450. function nextVersion()
  2451. {
  2452. $db = eZDB::instance();
  2453. $versions = $db->arrayQuery( "SELECT ( MAX( version ) + 1 ) AS next_id FROM ezcontentobject_version
  2454. WHERE contentobject_id='$this->ID'" );
  2455. return $versions[0]["next_id"];
  2456. }
  2457. /**
  2458. * Returns the previous available version number for this object, if existing, false otherwise ( if the object has only one version )
  2459. *
  2460. * @return int|bool
  2461. */
  2462. function previousVersion()
  2463. {
  2464. $db = eZDB::instance();
  2465. $versions = $db->arrayQuery( "SELECT version FROM ezcontentobject_version
  2466. WHERE contentobject_id='$this->ID'
  2467. ORDER BY version DESC", array( 'limit' => 2 ) );
  2468. if ( count( $versions ) > 1 and isset( $versions[1]['version'] ) )
  2469. {
  2470. return $versions[1]['version'];
  2471. }
  2472. else
  2473. {
  2474. return false;
  2475. }
  2476. }
  2477. /**
  2478. * Returns the number of existing versions.
  2479. *
  2480. * @return mixed
  2481. */
  2482. function getVersionCount()
  2483. {
  2484. $db = eZDB::instance();
  2485. $versionCount = $db->arrayQuery( "SELECT ( COUNT( version ) ) AS version_count FROM ezcontentobject_version
  2486. WHERE contentobject_id='$this->ID'" );
  2487. return $versionCount[0]["version_count"];
  2488. }
  2489. /**
  2490. * Returns the current object's language
  2491. *
  2492. * @return bool|string
  2493. */
  2494. function currentLanguage()
  2495. {
  2496. return $this->CurrentLanguage;
  2497. }
  2498. /**
  2499. * Returns the language object of the current object's language
  2500. *
  2501. * @return eZContentLanguage|bool|
  2502. */
  2503. function currentLanguageObject()
  2504. {
  2505. if ( $this->CurrentLanguage )
  2506. {
  2507. $language = eZContentLanguage::fetchByLocale( $this->CurrentLanguage );
  2508. }
  2509. else
  2510. {
  2511. $language = false;
  2512. }
  2513. return $language;
  2514. }
  2515. /**
  2516. * Sets the current object's language and resets the name
  2517. *
  2518. * @param string $lang
  2519. */
  2520. function setCurrentLanguage( $lang )
  2521. {
  2522. $this->CurrentLanguage = $lang;
  2523. $this->Name = null;
  2524. }
  2525. /**
  2526. * Returns the current object's initial language object, or false if not set
  2527. *
  2528. * @return eZContentLanguage|bool
  2529. */
  2530. function initialLanguage()
  2531. {
  2532. return isset( $this->InitialLanguageID ) ? eZContentLanguage::fetch( $this->InitialLanguageID ) : false;
  2533. }
  2534. /**
  2535. * Returns the current object's initial language code, or false if not set
  2536. *
  2537. * @return string|bool
  2538. */
  2539. function initialLanguageCode()
  2540. {
  2541. $initialLanguage = $this->initialLanguage();
  2542. // If current contentobject is "Top Level Nodes" than it doesn't have "initial Language" and "locale".
  2543. return ( $initialLanguage !== false ) ? $initialLanguage->attribute( 'locale' ) : false;
  2544. }
  2545. /**
  2546. * Adds a new location (node) to the current object.
  2547. *
  2548. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2549. * the calls within a db transaction; thus within db->begin and db->commit.
  2550. *
  2551. * @param int $parentNodeID The id of the node to use as parent.
  2552. * @param bool $asObject If true it will return the new child-node as an object, if not it returns the ID.
  2553. *
  2554. * @return eZContentObjectTreeNode|int
  2555. */
  2556. function addLocation( $parentNodeID, $asObject = false )
  2557. {
  2558. $node = eZContentObjectTreeNode::addChildTo( $this->ID, $parentNodeID, true, $this->CurrentVersion );
  2559. $data = array( 'contentobject_id' => $this->ID,
  2560. 'contentobject_version' => $this->attribute( 'current_version' ),
  2561. 'parent_node' => $parentNodeID,
  2562. // parent_remote_id in node assignment holds remote id of the added location,
  2563. // not of the parent location or of the node assignment itself
  2564. 'parent_remote_id' => $node->attribute( 'remote_id' ),
  2565. 'is_main' => 0 );
  2566. $nodeAssignment = eZNodeAssignment::create( $data );
  2567. $nodeAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_CREATE_NOP );
  2568. $nodeAssignment->store();
  2569. if ( $asObject )
  2570. {
  2571. return $node;
  2572. }
  2573. else
  2574. {
  2575. return $node->attribute( 'node_id' );
  2576. }
  2577. }
  2578. /**
  2579. * Adds a link to the given content object id.
  2580. *
  2581. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2582. * the calls within a db transaction; thus within db->begin and db->commit.
  2583. *
  2584. * @param int $toObjectID
  2585. * @param int|bool $fromObjectVersion
  2586. * @param int $attributeID
  2587. * @param int $relationType
  2588. * @return bool|void
  2589. */
  2590. function addContentObjectRelation( $toObjectID,
  2591. $fromObjectVersion = false,
  2592. $attributeID = 0,
  2593. $relationType = eZContentObject::RELATION_COMMON )
  2594. {
  2595. if ( $attributeID !== 0 )
  2596. {
  2597. $relationType = eZContentObject::RELATION_ATTRIBUTE;
  2598. }
  2599. $relationType =(int) $relationType;
  2600. if ( ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) != 0 &&
  2601. $relationType != eZContentObject::RELATION_ATTRIBUTE )
  2602. {
  2603. eZDebug::writeWarning( "Object relation type conflict", __METHOD__ );
  2604. }
  2605. $db = eZDB::instance();
  2606. if ( !$fromObjectVersion )
  2607. $fromObjectVersion = $this->CurrentVersion;
  2608. $fromObjectID = $this->ID;
  2609. if ( !is_numeric( $toObjectID ) )
  2610. {
  2611. eZDebug::writeError( "Related object ID (toObjectID): '$toObjectID', is not a numeric value.", __METHOD__ );
  2612. return false;
  2613. }
  2614. if ( !eZContentObject::exists( $toObjectID ) )
  2615. {
  2616. eZDebug::writeError( "Related object ID (toObjectID): '$toObjectID', does not refer to any existing object.", __METHOD__ );
  2617. return false;
  2618. }
  2619. $fromObjectID =(int) $fromObjectID;
  2620. $attributeID =(int) $attributeID;
  2621. $fromObjectVersion =(int) $fromObjectVersion;
  2622. $relationBaseType = ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) ?
  2623. eZContentObject::RELATION_ATTRIBUTE :
  2624. eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED | eZContentObject::RELATION_LINK;
  2625. $relationTypeMatch = $db->bitAnd( 'relation_type', $relationBaseType );
  2626. $query = "SELECT count(*) AS count
  2627. FROM ezcontentobject_link
  2628. WHERE from_contentobject_id=$fromObjectID AND
  2629. from_contentobject_version=$fromObjectVersion AND
  2630. to_contentobject_id=$toObjectID AND
  2631. $relationTypeMatch >= 0 AND
  2632. contentclassattribute_id=$attributeID";
  2633. $count = $db->arrayQuery( $query );
  2634. // if current relation does not exist
  2635. if ( !isset( $count[0]['count'] ) || $count[0]['count'] == '0' )
  2636. {
  2637. $db->begin();
  2638. $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, relation_type )
  2639. VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, $relationType )" );
  2640. $db->commit();
  2641. }
  2642. elseif ( isset( $count[0]['count'] ) &&
  2643. $count[0]['count'] != '0' &&
  2644. $attributeID == 0 &&
  2645. (eZContentObject::RELATION_ATTRIBUTE & $relationType) == 0 )
  2646. {
  2647. $db->begin();
  2648. $newRelationType = $db->bitOr( 'relation_type', $relationType );
  2649. $db->query( "UPDATE ezcontentobject_link
  2650. SET relation_type = $newRelationType
  2651. WHERE from_contentobject_id=$fromObjectID AND
  2652. from_contentobject_version=$fromObjectVersion AND
  2653. to_contentobject_id=$toObjectID AND
  2654. contentclassattribute_id=$attributeID" );
  2655. $db->commit();
  2656. }
  2657. }
  2658. /**
  2659. * Removes a link to the given content object id.
  2660. *
  2661. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  2662. * the calls within a db transaction; thus within db->begin and db->commit.
  2663. *
  2664. * @param int|bool $toObjectID If false it will delete relations to all the objects.
  2665. * @param bool $fromObjectVersion
  2666. * @param int $attributeID ID of class attribute.
  2667. * If it is > 0 we remove relations created by a specific objectrelation[list] attribute.
  2668. * If it is set to 0 we remove relations created without using of objectrelation[list] attribute.
  2669. * If it is set to false, we remove all relations, no matter how were they created:
  2670. * using objectrelation[list] attribute or using "Add related objects" functionality in obect editing mode.
  2671. * @param int $relationType
  2672. */
  2673. function removeContentObjectRelation( $toObjectID = false, $fromObjectVersion = false, $attributeID = 0, $relationType = eZContentObject::RELATION_COMMON )
  2674. {
  2675. $db = eZDB::instance();
  2676. if ( !$fromObjectVersion )
  2677. $fromObjectVersion = $this->CurrentVersion;
  2678. $fromObjectVersion = (int) $fromObjectVersion;
  2679. $fromObjectID = $this->ID;
  2680. if ( $toObjectID !== false )
  2681. {
  2682. $toObjectID =(int) $toObjectID;
  2683. $toObjectCondition = "AND to_contentobject_id=$toObjectID";
  2684. }
  2685. else
  2686. $toObjectCondition = '';
  2687. if ( $attributeID !== false )
  2688. {
  2689. $attributeID =(int) $attributeID;
  2690. $classAttributeCondition = "AND contentclassattribute_id=$attributeID";
  2691. }
  2692. else
  2693. $classAttributeCondition = '';
  2694. $lastRelationType = 0;
  2695. $db->begin();
  2696. if ( !$attributeID && ( $fromObjectVersion != $this->CurrentVersion || $this->CurrentVersion == 1 ) )
  2697. {
  2698. // Querying only for object level relations.
  2699. $rows = $db->arrayQuery( "SELECT * FROM ezcontentobject_link
  2700. WHERE from_contentobject_id=$fromObjectID
  2701. AND from_contentobject_version=$fromObjectVersion
  2702. AND contentclassattribute_id='0'
  2703. $toObjectCondition" );
  2704. if ( !empty( $rows ) )
  2705. {
  2706. $lastRow = end( $rows );
  2707. // Remember type in order to later compare with given $relationType and determine if it is composite.
  2708. $lastRelationType = (int) $lastRow['relation_type'];
  2709. }
  2710. }
  2711. if ( 0 !== ( eZContentObject::RELATION_ATTRIBUTE & $relationType ) ||
  2712. 0 != $attributeID ||
  2713. $relationType == $lastRelationType )
  2714. {
  2715. $db->query( "DELETE FROM ezcontentobject_link
  2716. WHERE from_contentobject_id=$fromObjectID AND
  2717. from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition" );
  2718. }
  2719. else
  2720. {
  2721. if ( $db->databaseName() == 'oracle' )
  2722. {
  2723. $notRelationType = - ( $relationType + 1 );
  2724. $db->query( "UPDATE ezcontentobject_link
  2725. SET relation_type = " . $db->bitAnd( 'relation_type', $notRelationType ) . "
  2726. WHERE from_contentobject_id=$fromObjectID AND
  2727. from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition" );
  2728. }
  2729. else
  2730. {
  2731. $db->query( "UPDATE ezcontentobject_link
  2732. SET relation_type = ( relation_type & ".(~$relationType)." )
  2733. WHERE from_contentobject_id=$fromObjectID AND
  2734. from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition" );
  2735. }
  2736. }
  2737. $db->commit();
  2738. }
  2739. /**
  2740. * @param int $currentVersion
  2741. * @param int $newVersion
  2742. * @param int|bool $newObjectID
  2743. */
  2744. function copyContentObjectRelations( $currentVersion, $newVersion, $newObjectID = false )
  2745. {
  2746. $objectID = $this->ID;
  2747. if ( !$newObjectID )
  2748. {
  2749. $newObjectID = $objectID;
  2750. }
  2751. $db = eZDB::instance();
  2752. $db->begin();
  2753. $relations = $db->arrayQuery( "SELECT to_contentobject_id, relation_type FROM ezcontentobject_link
  2754. WHERE contentclassattribute_id='0'
  2755. AND from_contentobject_id='$objectID'
  2756. AND from_contentobject_version='$currentVersion'" );
  2757. foreach ( $relations as $relation )
  2758. {
  2759. $toContentObjectID = $relation['to_contentobject_id'];
  2760. $relationType = $relation['relation_type'];
  2761. $db->query( "INSERT INTO ezcontentobject_link( contentclassattribute_id,
  2762. from_contentobject_id,
  2763. from_contentobject_version,
  2764. to_contentobject_id,
  2765. relation_type )
  2766. VALUES ( '0', '$newObjectID', '$newVersion', '$toContentObjectID', '$relationType' )" );
  2767. }
  2768. $db->commit();
  2769. }
  2770. /**
  2771. * @return bool
  2772. */
  2773. static function isObjectRelationTyped()
  2774. {
  2775. $siteIni = eZINI::instance( 'site.ini' );
  2776. if ( $siteIni->hasVariable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) )
  2777. {
  2778. if ( 'enabled' == $siteIni->variable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) )
  2779. {
  2780. return true;
  2781. }
  2782. }
  2783. return false;
  2784. }
  2785. /**
  2786. * @param bool $allRelations
  2787. * @return int
  2788. */
  2789. static function relationTypeMask( $allRelations = false )
  2790. {
  2791. $relationTypeMask = eZContentObject::RELATION_COMMON |
  2792. eZContentObject::RELATION_EMBED;
  2793. if ( eZContentObject::isObjectRelationTyped() )
  2794. {
  2795. $relationTypeMask |= eZContentObject::RELATION_LINK;
  2796. }
  2797. if ( $allRelations )
  2798. {
  2799. $relationTypeMask |= eZContentObject::RELATION_ATTRIBUTE;
  2800. }
  2801. return $relationTypeMask;
  2802. }
  2803. /**
  2804. * Returns related or reverse related objects
  2805. *
  2806. * @param int|bool $fromObjectVersion If omitted, the current version will be used
  2807. * @param int|bool $objectID If omitted, the current object will be used
  2808. * @param int|bool $attributeID makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE
  2809. * $attributeID = 0|false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE )
  2810. * - return relations made with any attributes
  2811. * $attributeID > 0
  2812. * - return relations made with attribute ID ( "related object(s)" datatype )
  2813. * $attributeID = false ( $params['AllRelations'] not set )
  2814. * - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead)
  2815. * @param bool $groupByAttribute This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true
  2816. * $groupByAttribute = false
  2817. * - return all relations as an array of content objects
  2818. * $groupByAttribute = true
  2819. * - return all relations groupped by attribute ID
  2820. * @param array|bool $params Other parameters from template fetch function
  2821. * $params['AllRelations'] = true
  2822. * - return ALL relations, including attribute-level
  2823. * $params['AllRelations'] = false
  2824. * - return objec level relations only
  2825. * $params['AllRelations'] = int > 0
  2826. * - bit mask of EZ_CONTENT_OBJECT_RELATION_* values
  2827. * $params['SortBy']
  2828. * - Possible values:
  2829. * "class_identifier", "class_name", "modified",
  2830. * "name", "published", "section"
  2831. * $params['IgnoreVisibility'] = true
  2832. * - Include related objects with a 'hidden' state
  2833. * $params['IgnoreVisibility'] = false
  2834. * - Exclude related objects with a 'hidden' state
  2835. * $params['RelatedClassIdentifiers'] = array
  2836. * - limit returned relations to objects of the specified class identifiers
  2837. * @param bool $reverseRelatedObjects true -> returns reverse related objects
  2838. * false -> returns related objects
  2839. * @return eZContentObject[]|array eZContentObject[], if $params['AsObject'] is set to true (default), array otherwise
  2840. */
  2841. function relatedObjects( $fromObjectVersion = false,
  2842. $objectID = false,
  2843. $attributeID = 0,
  2844. $groupByAttribute = false,
  2845. $params = false,
  2846. $reverseRelatedObjects = false )
  2847. {
  2848. if ( $fromObjectVersion == false )
  2849. $fromObjectVersion = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false;
  2850. $fromObjectVersion =(int) $fromObjectVersion;
  2851. if( !$objectID )
  2852. $objectID = $this->ID;
  2853. $objectID =(int) $objectID;
  2854. $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false;
  2855. $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false;
  2856. $asObject = ( isset( $params['AsObject'] ) ) ? $params['AsObject'] : true;
  2857. $loadDataMap = ( isset( $params['LoadDataMap'] ) ) ? $params['LoadDataMap'] : false;
  2858. $db = eZDB::instance();
  2859. $sortingString = '';
  2860. $sortingInfo = array( 'attributeFromSQL' => '',
  2861. 'attributeWhereSQL' => '',
  2862. 'attributeTargetSQL' => '' );
  2863. $relatedClassIdentifiersSQL = '';
  2864. $showInvisibleNodesCond = '';
  2865. // process params (only SortBy and IgnoreVisibility currently supported):
  2866. // Supported sort_by modes:
  2867. // class_identifier, class_name, modified, name, published, section
  2868. if ( is_array( $params ) )
  2869. {
  2870. if ( isset( $params['SortBy'] ) )
  2871. {
  2872. $validSortBy = array( 'class_identifier', 'class_name', 'modified', 'name', 'published', 'section' );
  2873. $sortByParam = array();
  2874. if ( is_array( $params['SortBy'] ) )
  2875. {
  2876. // only one SortBy, as a simple array
  2877. if ( !is_array( $params['SortBy'][0] ) )
  2878. {
  2879. if ( !in_array( $params['SortBy'][0], $validSortBy ) )
  2880. eZDebug::writeWarning( "Unsupported sort_by parameter {$params['SortBy'][0]}; check the online documentation for the list of supported sort types", __METHOD__ );
  2881. else
  2882. $sortByParam[] = $params['SortBy'];
  2883. }
  2884. // multiple SortBy, check each of them one by one, and keep valid ones
  2885. else
  2886. {
  2887. $invalidSortBy = array();
  2888. foreach( $params['SortBy'] as $sortByTuple )
  2889. {
  2890. if ( !in_array( $sortByTuple[0], $validSortBy ) )
  2891. $invalidSortBy[] = $sortByTuple[0];
  2892. else
  2893. $sortByParam[] = $sortByTuple;
  2894. }
  2895. if ( count( $invalidSortBy ) > 0 )
  2896. {
  2897. eZDebug::writeWarning( "Unsupported sort_by parameter(s) " . implode( ', ', $invalidSortBy ) . "; check the online documentation for the list of supported sort types", __METHOD__ );
  2898. }
  2899. }
  2900. }
  2901. if ( count( $sortByParam ) > 0 )
  2902. {
  2903. $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortByParam );
  2904. $sortingString = ' ORDER BY ' . $sortingInfo['sortingFields'];
  2905. }
  2906. }
  2907. if ( isset( $params['IgnoreVisibility'] ) )
  2908. {
  2909. $showInvisibleNodesCond = self::createFilterByVisibilitySQLString( $params['IgnoreVisibility'] );
  2910. }
  2911. // related class identifier filter
  2912. $relatedClassIdentifiersSQL = '';
  2913. if ( isset( $params['RelatedClassIdentifiers'] ) && is_array( $params['RelatedClassIdentifiers'] ) )
  2914. {
  2915. $relatedClassIdentifiers = array();
  2916. foreach( $params['RelatedClassIdentifiers'] as $classIdentifier )
  2917. {
  2918. $relatedClassIdentifiers[] = "'" . $db->escapeString( $classIdentifier ) . "'";
  2919. }
  2920. $relatedClassIdentifiersSQL = $db->generateSQLINStatement( $relatedClassIdentifiers, 'ezcontentclass.identifier', false, true, 'string' ). " AND";
  2921. unset( $classIdentifier, $relatedClassIdentifiers );
  2922. }
  2923. }
  2924. $relationTypeMasking = '';
  2925. $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false );
  2926. if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) )
  2927. {
  2928. $attributeID =(int) $attributeID;
  2929. $relationTypeMasking .= " contentclassattribute_id=$attributeID AND ";
  2930. $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE;
  2931. }
  2932. elseif ( is_bool( $relationTypeMask ) )
  2933. {
  2934. $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask );
  2935. }
  2936. if ( $db->databaseName() == 'oracle' )
  2937. {
  2938. $relationTypeMasking .= " bitand( relation_type, $relationTypeMask ) <> 0 ";
  2939. }
  2940. else
  2941. {
  2942. $relationTypeMasking .= " ( relation_type & $relationTypeMask ) <> 0 ";
  2943. }
  2944. // Create SQL
  2945. $fromOrToContentObjectID = $reverseRelatedObjects == false ? " AND ezcontentobject.id=ezcontentobject_link.to_contentobject_id AND
  2946. ezcontentobject_link.from_contentobject_id='$objectID' AND
  2947. ezcontentobject_link.from_contentobject_version='$fromObjectVersion' "
  2948. : " AND ezcontentobject.id=ezcontentobject_link.from_contentobject_id AND
  2949. ezcontentobject_link.to_contentobject_id=$objectID AND
  2950. ezcontentobject_link.from_contentobject_version=ezcontentobject.current_version ";
  2951. $query = "SELECT ";
  2952. if ( $groupByAttribute )
  2953. {
  2954. $query .= "ezcontentobject_link.contentclassattribute_id, ";
  2955. }
  2956. $query .= "
  2957. ezcontentclass.serialized_name_list AS class_serialized_name_list,
  2958. ezcontentclass.identifier as contentclass_identifier,
  2959. ezcontentclass.is_container as is_container,
  2960. ezcontentobject.*, ezcontentobject_name.name as name, ezcontentobject_name.real_translation
  2961. $sortingInfo[attributeTargetSQL]
  2962. FROM
  2963. ezcontentclass,
  2964. ezcontentobject,
  2965. ezcontentobject_link,
  2966. ezcontentobject_name
  2967. $sortingInfo[attributeFromSQL]
  2968. WHERE
  2969. ezcontentclass.id=ezcontentobject.contentclass_id AND
  2970. ezcontentclass.version=0 AND
  2971. ezcontentobject.status=" . eZContentObject::STATUS_PUBLISHED . " AND
  2972. $sortingInfo[attributeWhereSQL]
  2973. $relatedClassIdentifiersSQL
  2974. $relationTypeMasking
  2975. $fromOrToContentObjectID
  2976. $showInvisibleNodesCond AND
  2977. ezcontentobject.id = ezcontentobject_name.contentobject_id AND
  2978. ezcontentobject.current_version = ezcontentobject_name.content_version AND
  2979. " . eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ) . "
  2980. $sortingString";
  2981. if ( !$offset && !$limit )
  2982. {
  2983. $relatedObjects = $db->arrayQuery( $query );
  2984. }
  2985. else
  2986. {
  2987. $relatedObjects = $db->arrayQuery( $query, array( 'offset' => $offset,
  2988. 'limit' => $limit ) );
  2989. }
  2990. $ret = array();
  2991. $tmp = array();
  2992. foreach ( $relatedObjects as $object )
  2993. {
  2994. if ( $asObject )
  2995. {
  2996. $obj = new eZContentObject( $object );
  2997. $obj->ClassName = eZContentClass::nameFromSerializedString( $object['class_serialized_name_list'] );
  2998. }
  2999. else
  3000. {
  3001. $obj = $object;
  3002. }
  3003. $tmp[] = $obj;
  3004. if ( !$groupByAttribute )
  3005. {
  3006. $ret[] = $obj;
  3007. }
  3008. else
  3009. {
  3010. $classAttrID = $object['contentclassattribute_id'];
  3011. if ( !isset( $ret[$classAttrID] ) )
  3012. $ret[$classAttrID] = array();
  3013. $ret[$classAttrID][] = $obj;
  3014. }
  3015. }
  3016. if ( $loadDataMap && $asObject )
  3017. eZContentObject::fillNodeListAttributes( $tmp );
  3018. return $ret;
  3019. }
  3020. /**
  3021. * Returns related objects.
  3022. *
  3023. * @see relatedObjects()
  3024. *
  3025. * @param int|bool $fromObjectVersion
  3026. * @param int|bool $fromObjectID
  3027. * @param int|bool $attributeID
  3028. * @param bool $groupByAttribute
  3029. * @param array|bool $params
  3030. * @return array|eZContentObject[]
  3031. */
  3032. function relatedContentObjectList( $fromObjectVersion = false,
  3033. $fromObjectID = false,
  3034. $attributeID = 0,
  3035. $groupByAttribute = false,
  3036. $params = false )
  3037. {
  3038. eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $fromObjectID, "objectID" );
  3039. return $this->relatedObjects( $fromObjectVersion, $fromObjectID, $attributeID, $groupByAttribute, $params );
  3040. }
  3041. /**
  3042. * Returns the xml-linked objects.
  3043. *
  3044. * @see relatedObjects()
  3045. *
  3046. * @param int|bool $fromObjectVersion
  3047. * @param int|bool $fromObjectID
  3048. * @return array|eZContentObject[]
  3049. */
  3050. function linkedContentObjectList( $fromObjectVersion = false, $fromObjectID = false )
  3051. {
  3052. return $this->relatedObjects( $fromObjectVersion,
  3053. $fromObjectID,
  3054. 0,
  3055. false,
  3056. array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
  3057. }
  3058. /**
  3059. * Returns the xml-embedded objects.
  3060. *
  3061. * @see relatedObjects()
  3062. *
  3063. * @param int|bool $fromObjectVersion
  3064. * @param int|bool $fromObjectID
  3065. * @return array|eZContentObject[]
  3066. */
  3067. function embeddedContentObjectList( $fromObjectVersion = false, $fromObjectID = false )
  3068. {
  3069. return $this->relatedObjects( $fromObjectVersion,
  3070. $fromObjectID,
  3071. 0,
  3072. false,
  3073. array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
  3074. }
  3075. /**
  3076. * Returns the reverse xml-linked objects.
  3077. *
  3078. * @see relatedObjects()
  3079. *
  3080. * @param int|bool $fromObjectVersion
  3081. * @param int|bool $fromObjectID
  3082. * @return array|eZContentObject[]
  3083. */
  3084. function reverseLinkedObjectList( $fromObjectVersion = false, $fromObjectID = false )
  3085. {
  3086. return $this->relatedObjects( $fromObjectVersion,
  3087. $fromObjectID,
  3088. 0,
  3089. false,
  3090. array( 'AllRelations' => eZContentObject::RELATION_LINK ),
  3091. true );
  3092. }
  3093. /**
  3094. * Returns the reverse xml-embedded objects.
  3095. *
  3096. * @see relatedObjects()
  3097. *
  3098. * @param int|bool $fromObjectVersion
  3099. * @param int|bool $fromObjectID
  3100. * @return array|eZContentObject[]
  3101. */
  3102. function reverseEmbeddedObjectList( $fromObjectVersion = false, $fromObjectID = false )
  3103. {
  3104. return $this->relatedObjects( $fromObjectVersion,
  3105. $fromObjectID,
  3106. 0,
  3107. false,
  3108. array( 'AllRelations' => eZContentObject::RELATION_EMBED ),
  3109. true );
  3110. }
  3111. /**
  3112. * Left for compatibility
  3113. *
  3114. * @see relatedObjects()
  3115. *
  3116. * @deprecated
  3117. * @param bool $fromObjectVersion
  3118. * @param bool $fromObjectID
  3119. * @param int $attributeID
  3120. * @param bool $params
  3121. * @return array
  3122. */
  3123. function relatedContentObjectArray( $fromObjectVersion = false,
  3124. $fromObjectID = false,
  3125. $attributeID = 0,
  3126. $params = false )
  3127. {
  3128. return eZContentObject::relatedContentObjectList( $fromObjectVersion,
  3129. $fromObjectID,
  3130. $attributeID,
  3131. false,
  3132. $params );
  3133. }
  3134. /**
  3135. * Returns the number of related objects
  3136. *
  3137. * @see relatedObjectCount()
  3138. *
  3139. * @param int|bool $fromObjectVersion
  3140. * @param int|bool $attributeID
  3141. * @param array|bool $params
  3142. * @return int
  3143. */
  3144. function relatedContentObjectCount( $fromObjectVersion = false,
  3145. $attributeID = 0,
  3146. $params = false )
  3147. {
  3148. eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $this->ID, "relatedContentObjectCount::objectID" );
  3149. return $this->relatedObjectCount( $fromObjectVersion,
  3150. $attributeID,
  3151. false,
  3152. $params );
  3153. }
  3154. /**
  3155. * Returns the objects to which this object are related .
  3156. *
  3157. * @see relatedObjects()
  3158. *
  3159. * @param int|bool $version
  3160. * @param int|bool $attributeID
  3161. * @param bool $groupByAttribute
  3162. * @param array|bool $params
  3163. * @return array|eZContentObject[]
  3164. */
  3165. function reverseRelatedObjectList( $version = false,
  3166. $attributeID = 0,
  3167. $groupByAttribute = false,
  3168. $params = false )
  3169. {
  3170. return $this->relatedObjects( $version, $this->ID, $attributeID, $groupByAttribute, $params, true );
  3171. }
  3172. /**
  3173. * Returns the xml-linked objects count.
  3174. *
  3175. * @see relatedObjectCount()
  3176. *
  3177. * @param int|bool $fromObjectVersion
  3178. * @return int
  3179. */
  3180. function linkedContentObjectCount( $fromObjectVersion = false )
  3181. {
  3182. return $this->relatedObjectCount( $fromObjectVersion,
  3183. 0,
  3184. false,
  3185. array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
  3186. }
  3187. /**
  3188. * Returns the xml-embedded objects count.
  3189. *
  3190. * @see relatedObjectCount()
  3191. *
  3192. * @param int|bool $fromObjectVersion
  3193. * @return int
  3194. */
  3195. function embeddedContentObjectCount( $fromObjectVersion = false )
  3196. {
  3197. return $this->relatedObjectCount( $fromObjectVersion,
  3198. 0,
  3199. false,
  3200. array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
  3201. }
  3202. /**
  3203. * Returns the reverse xml-linked objects count.
  3204. *
  3205. * @see relatedObjectCount()
  3206. *
  3207. * @param int|bool $fromObjectVersion
  3208. * @return int
  3209. */
  3210. function reverseLinkedObjectCount( $fromObjectVersion = false )
  3211. {
  3212. return $this->relatedObjectCount( $fromObjectVersion,
  3213. 0,
  3214. true,
  3215. array( 'AllRelations' => eZContentObject::RELATION_LINK ) );
  3216. }
  3217. /**
  3218. * Returns the reverse xml-embedded objects count.
  3219. *
  3220. * @see relatedObjectCount()
  3221. *
  3222. * @param int|bool $fromObjectVersion
  3223. * @return int
  3224. */
  3225. function reverseEmbeddedObjectCount( $fromObjectVersion = false )
  3226. {
  3227. return $this->relatedObjectCount( $fromObjectVersion,
  3228. 0,
  3229. true,
  3230. array( 'AllRelations' => eZContentObject::RELATION_EMBED ) );
  3231. }
  3232. /**
  3233. * Fetch the number of (reverse) related objects
  3234. *
  3235. * @param bool|int $version
  3236. * @param int $attributeID
  3237. * This parameter only makes sense if $params[AllRelations] is unset,
  3238. * set to false, or matches eZContentObject::RELATION_ATTRIBUTE
  3239. * Possible values:
  3240. * - 0 or false:
  3241. * Count relations made with any attribute
  3242. * - >0
  3243. * Count relations made with attribute $attributeID
  3244. * @param int|bool $reverseRelatedObjects
  3245. * Wether to count related objects (false) or reverse related
  3246. * objects (false)
  3247. * @param array|bool $params
  3248. * Various params, as an associative array.
  3249. * Possible values:
  3250. * - AllRelations (bool|int)
  3251. * true: count ALL relations, object and attribute level
  3252. * false: only count object level relations
  3253. * other: bit mask of eZContentObject::RELATION_* constants
  3254. * - IgnoreVisibility (bool)
  3255. * If true, 'hidden' status will be ignored
  3256. *
  3257. * @return int The number of (reverse) related objects for the object
  3258. */
  3259. function relatedObjectCount( $version = false, $attributeID = 0, $reverseRelatedObjects = false, $params = false )
  3260. {
  3261. $objectID = $this->ID;
  3262. if ( $version == false )
  3263. $version = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false;
  3264. $version = (int) $version;
  3265. $db = eZDB::instance();
  3266. $showInvisibleNodesCond = '';
  3267. // process params (only IgnoreVisibility currently supported):
  3268. if ( is_array( $params ) )
  3269. {
  3270. if ( isset( $params['IgnoreVisibility'] ) )
  3271. {
  3272. $showInvisibleNodesCond = self::createFilterByVisibilitySQLString(
  3273. $params['IgnoreVisibility'],
  3274. // inner_object represents the source of relation while outer_object represents the target
  3275. $reverseRelatedObjects ? 'inner_object' : 'outer_object'
  3276. );
  3277. }
  3278. }
  3279. $relationTypeMasking = '';
  3280. $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false );
  3281. if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) )
  3282. {
  3283. $attributeID =(int) $attributeID;
  3284. $relationTypeMasking .= " AND inner_link.contentclassattribute_id = $attributeID ";
  3285. $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE;
  3286. }
  3287. elseif ( is_bool( $relationTypeMask ) )
  3288. {
  3289. $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask );
  3290. }
  3291. if ( $db->databaseName() == 'oracle' )
  3292. {
  3293. $relationTypeMasking .= " AND bitand( inner_link.relation_type, $relationTypeMask ) <> 0 ";
  3294. }
  3295. else
  3296. {
  3297. $relationTypeMasking .= " AND ( inner_link.relation_type & $relationTypeMask ) <> 0 ";
  3298. }
  3299. if ( $reverseRelatedObjects )
  3300. {
  3301. $outerObjectIDSQL = 'outer_object.id = outer_link.from_contentobject_id';
  3302. if ( is_array( $objectID ) )
  3303. {
  3304. if ( count( $objectID ) > 0 )
  3305. {
  3306. $objectIDSQL = ' AND ' . $db->generateSQLINStatement( $objectID, 'inner_link.to_contentobject_id', false, false, 'int' ) . ' AND
  3307. inner_link.from_contentobject_version = inner_object.current_version';
  3308. }
  3309. else
  3310. {
  3311. $objectIDSQL = '';
  3312. }
  3313. }
  3314. else
  3315. {
  3316. $objectID = (int) $objectID;
  3317. $objectIDSQL = " AND inner_link.to_contentobject_id = $objectID
  3318. AND inner_link.from_contentobject_version = inner_object.current_version";
  3319. }
  3320. }
  3321. else
  3322. {
  3323. $outerObjectIDSQL = 'outer_object.id = outer_link.to_contentobject_id';
  3324. $objectIDSQL = " AND inner_link.from_contentobject_id = $objectID
  3325. AND inner_link.from_contentobject_version = $version";
  3326. }
  3327. $query = "SELECT
  3328. COUNT( outer_object.id ) AS count
  3329. FROM
  3330. ezcontentobject outer_object, ezcontentobject inner_object, ezcontentobject_link outer_link
  3331. INNER JOIN
  3332. ezcontentobject_link inner_link ON outer_link.id = inner_link.id
  3333. WHERE
  3334. $outerObjectIDSQL
  3335. AND outer_object.status = " . eZContentObject::STATUS_PUBLISHED . "
  3336. AND inner_object.id = inner_link.from_contentobject_id
  3337. AND inner_object.status = " . eZContentObject::STATUS_PUBLISHED . "
  3338. $objectIDSQL
  3339. $relationTypeMasking
  3340. $showInvisibleNodesCond";
  3341. $rows = $db->arrayQuery( $query );
  3342. return $rows[0]['count'];
  3343. }
  3344. /**
  3345. * Returns the number of objects to which this object is related.
  3346. *
  3347. * @see relatedObjectCount()
  3348. *
  3349. * @param int|bool $version
  3350. * @param int|bool $attributeID
  3351. * @param array|bool $params
  3352. * @return int
  3353. */
  3354. function reverseRelatedObjectCount( $version = false, $attributeID = 0, $params = false )
  3355. {
  3356. return $this->relatedObjectCount( $version, $attributeID, true, $params );
  3357. }
  3358. /**
  3359. * Returns the related objects.
  3360. *
  3361. * @see reverseRelatedObjectList()
  3362. *
  3363. * @deprecated This function is a duplicate of reverseRelatedObjectList(), use that function instead.
  3364. * @param int|bool $version
  3365. * @return array|eZContentObject[]
  3366. */
  3367. function contentObjectListRelatingThis( $version = false )
  3368. {
  3369. return $this->reverseRelatedObjectList( $version );
  3370. }
  3371. /**
  3372. * Returns an array of parent node IDs
  3373. *
  3374. * @return int[]
  3375. */
  3376. function parentNodeIDArray()
  3377. {
  3378. return $this->parentNodes( true, false );
  3379. }
  3380. /**
  3381. * Returns the parent nodes for the current object.
  3382. *
  3383. * @param int|bool $version No longer in use, published nodes are used instead.
  3384. * @param bool $asObject If true it fetches PHP objects, otherwise it fetches IDs.
  3385. * @return eZContentObjectTreeNode[]|int[]
  3386. */
  3387. function parentNodes( $version = false, $asObject = true )
  3388. {
  3389. // We no longer use node-assignment table to find the parents but uses
  3390. // the 'published' tree structure.
  3391. $retNodes = array();
  3392. $parentNodeIDs = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID );
  3393. if ( !$parentNodeIDs )
  3394. {
  3395. return $retNodes;
  3396. }
  3397. if ( $asObject )
  3398. {
  3399. $retNodes = eZContentObjectTreeNode::fetch( $parentNodeIDs );
  3400. if ( !is_array( $retNodes ) )
  3401. {
  3402. $retNodes = array( $retNodes );
  3403. }
  3404. }
  3405. else
  3406. {
  3407. $retNodes = $parentNodeIDs;
  3408. }
  3409. return $retNodes;
  3410. }
  3411. /**
  3412. * Creates and returns a new node assignment that will place the object as child of node $nodeID.
  3413. *
  3414. * The returned assignment will already be stored in the database
  3415. *
  3416. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  3417. * the calls within a db transaction; thus within db->begin and db->commit.
  3418. *
  3419. * @param int $parentNodeID The node ID of the parent node
  3420. * @param bool $isMain True if the created node is the main node of the object
  3421. * @param string|bool $remoteID A string denoting the unique remote ID of the assignment or \c false for no remote id.
  3422. * @param int $sortField
  3423. * @param int $sortOrder
  3424. * @return eZNodeAssignment|null
  3425. */
  3426. function createNodeAssignment( $parentNodeID, $isMain, $remoteID = false, $sortField = eZContentObjectTreeNode::SORT_FIELD_PUBLISHED, $sortOrder = eZContentObjectTreeNode::SORT_ORDER_DESC )
  3427. {
  3428. $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $this->attribute( 'id' ),
  3429. 'contentobject_version' => $this->attribute( 'current_version' ),
  3430. 'parent_node' => $parentNodeID,
  3431. 'is_main' => ( $isMain ? 1 : 0 ),
  3432. 'sort_field' => $sortField,
  3433. 'sort_order' => $sortOrder ) );
  3434. if ( $remoteID !== false )
  3435. {
  3436. $nodeAssignment->setAttribute( 'remote_id', $remoteID );
  3437. }
  3438. $nodeAssignment->store();
  3439. return $nodeAssignment;
  3440. }
  3441. /**
  3442. * Creates object with nodeAssignment from given parent Node, class ID and language code.
  3443. *
  3444. * @param eZContentObjectTreeNode $parentNode
  3445. * @param int $contentClassID
  3446. * @param string $languageCode
  3447. * @param string|bool $remoteID
  3448. *
  3449. * @return eZContentObject|null
  3450. */
  3451. static function createWithNodeAssignment( $parentNode, $contentClassID, $languageCode, $remoteID = false )
  3452. {
  3453. $class = eZContentClass::fetch( $contentClassID );
  3454. $parentObject = $parentNode->attribute( 'object' );
  3455. // Check if the user has access to create a folder here
  3456. if ( $class instanceof eZContentClass and
  3457. $parentObject->checkAccess( 'create', $contentClassID, false, false, $languageCode ) == '1' )
  3458. {
  3459. // Set section of the newly created object to the section's value of it's parent object
  3460. $sectionID = $parentObject->attribute( 'section_id' );
  3461. $db = eZDB::instance();
  3462. $db->begin();
  3463. $contentObject = $class->instantiateIn( $languageCode, false, $sectionID, false, eZContentObjectVersion::STATUS_INTERNAL_DRAFT );
  3464. $nodeAssignment = $contentObject->createNodeAssignment( $parentNode->attribute( 'node_id' ),
  3465. true, $remoteID,
  3466. $class->attribute( 'sort_field' ),
  3467. $class->attribute( 'sort_order' ) );
  3468. $db->commit();
  3469. return $contentObject;
  3470. }
  3471. return null;
  3472. }
  3473. /**
  3474. * Returns the visible nodes of the current object. The main node is the
  3475. * first element in the returned value (if it is considered visible).
  3476. *
  3477. * The setting site.ini/[SiteAccessSettings]/ShowHiddenNodes is taken into
  3478. * account, ie an hidden (or hidden by superior) node is considered visible
  3479. * if ShowHiddenNodes is true.
  3480. *
  3481. * @see eZContentObject::assignedNodes
  3482. * @return eZContentObjectTreeNode[]
  3483. */
  3484. function visibleNodes()
  3485. {
  3486. $result = array();
  3487. foreach ( $this->assignedNodes( true, true ) as $node )
  3488. {
  3489. if ( $node->attribute( 'node_id' ) == $this->attribute( 'main_node_id' ) )
  3490. {
  3491. array_unshift( $result, $node );
  3492. }
  3493. else
  3494. {
  3495. $result[] = $node;
  3496. }
  3497. }
  3498. return $result;
  3499. }
  3500. /**
  3501. * Returns whether the current object has at least one visible node.
  3502. *
  3503. * The setting site.ini/[SiteAccessSettings]/ShowHiddenNodes is taken into
  3504. * account, ie an hidden (or hidden by superior) node is considered visible
  3505. * if ShowHiddenNodes is true.
  3506. *
  3507. * @return boolean
  3508. */
  3509. function hasVisibleNode()
  3510. {
  3511. $contentobjectID = $this->attribute( 'id' );
  3512. if ( $contentobjectID == null )
  3513. {
  3514. return false;
  3515. }
  3516. $visibilitySQL = '';
  3517. if ( eZINI::instance()->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) !== 'true' )
  3518. {
  3519. $visibilitySQL = "AND ezcontentobject_tree.is_invisible = 0 ";
  3520. }
  3521. $rows = eZDB::instance()->arrayQuery(
  3522. "SELECT COUNT(ezcontentobject_tree.node_id) AS node_count " .
  3523. "FROM ezcontentobject_tree " .
  3524. "INNER JOIN ezcontentobject ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id) " .
  3525. "WHERE contentobject_id = $contentobjectID " . $visibilitySQL
  3526. );
  3527. return ( $rows[0]['node_count'] > 0 );
  3528. }
  3529. /**
  3530. * Returns the node assignments for the current object.
  3531. *
  3532. * @param boolean $asObject
  3533. * @param boolean $checkVisibility if true, the visibility and the setting
  3534. * site.ini/[SiteAccessSettings]/ShowHiddenNodes are taken into account.
  3535. * @return eZContentObjectTreeNode[]|array[]
  3536. */
  3537. function assignedNodes( $asObject = true, $checkVisibility = false )
  3538. {
  3539. $contentobjectID = $this->attribute( 'id' );
  3540. if ( $contentobjectID == null )
  3541. {
  3542. return array();
  3543. }
  3544. $visibilitySQL = '';
  3545. if (
  3546. $checkVisibility === true
  3547. && eZINI::instance()->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) !== 'true'
  3548. )
  3549. {
  3550. $visibilitySQL = "AND ezcontentobject_tree.is_invisible = 0 ";
  3551. }
  3552. $nodesListArray = eZDB::instance()->arrayQuery(
  3553. "SELECT " .
  3554. "ezcontentobject.contentclass_id, ezcontentobject.current_version, ezcontentobject.initial_language_id, ezcontentobject.language_mask, " .
  3555. "ezcontentobject.modified, ezcontentobject.name, ezcontentobject.owner_id, ezcontentobject.published, ezcontentobject.remote_id AS object_remote_id, ezcontentobject.section_id, " .
  3556. "ezcontentobject.status, ezcontentobject_tree.contentobject_is_published, ezcontentobject_tree.contentobject_version, ezcontentobject_tree.depth, " .
  3557. "ezcontentobject_tree.is_hidden, ezcontentobject_tree.is_invisible, ezcontentobject_tree.main_node_id, ezcontentobject_tree.modified_subnode, ezcontentobject_tree.node_id, " .
  3558. "ezcontentobject_tree.parent_node_id, ezcontentobject_tree.path_identification_string, ezcontentobject_tree.path_string, ezcontentobject_tree.priority, ezcontentobject_tree.remote_id, " .
  3559. "ezcontentobject_tree.sort_field, ezcontentobject_tree.sort_order, ezcontentclass.serialized_name_list as class_serialized_name_list, " .
  3560. "ezcontentclass.identifier as class_identifier, " .
  3561. "ezcontentclass.is_container as is_container " .
  3562. "FROM ezcontentobject_tree " .
  3563. "INNER JOIN ezcontentobject ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id) " .
  3564. "INNER JOIN ezcontentclass ON (ezcontentclass.version = 0 AND ezcontentclass.id = ezcontentobject.contentclass_id) " .
  3565. "WHERE contentobject_id = $contentobjectID " .
  3566. $visibilitySQL .
  3567. "ORDER BY path_string"
  3568. );
  3569. if ( $asObject == true )
  3570. {
  3571. return eZContentObjectTreeNode::makeObjectsArray( $nodesListArray, true, array( "id" => $contentobjectID ) );
  3572. }
  3573. else
  3574. return $nodesListArray;
  3575. }
  3576. /**
  3577. * Returns the main node id for the current object and sets the attribute MainNodeID in the current object
  3578. *
  3579. * @return int|null
  3580. */
  3581. function mainNodeID()
  3582. {
  3583. if ( !is_numeric( $this->MainNodeID ) )
  3584. {
  3585. $mainNodeID = eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ) );
  3586. $this->MainNodeID = $mainNodeID;
  3587. }
  3588. return $this->MainNodeID;
  3589. }
  3590. /**
  3591. * Returns the main node id for the current object.
  3592. *
  3593. * @return int|null
  3594. */
  3595. function mainNode()
  3596. {
  3597. return eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ), true );
  3598. }
  3599. /**
  3600. * Sets the permissions for this object.
  3601. *
  3602. * @param array $permissionArray
  3603. */
  3604. function setPermissions( $permissionArray )
  3605. {
  3606. $this->Permissions =& $permissionArray;
  3607. }
  3608. /**
  3609. * Returns the permission for the current object.
  3610. *
  3611. * @return array
  3612. */
  3613. function permissions( )
  3614. {
  3615. return $this->Permissions;
  3616. }
  3617. /**
  3618. * Returns a list of languages of the current object that the current user can edit
  3619. *
  3620. * @return eZContentLanguage[]
  3621. */
  3622. function canEditLanguages()
  3623. {
  3624. $availableLanguages = $this->availableLanguages();
  3625. $languages = array();
  3626. foreach ( eZContentLanguage::prioritizedLanguages() as $language )
  3627. {
  3628. $languageCode = $language->attribute( 'locale' );
  3629. if ( in_array( $languageCode, $availableLanguages ) &&
  3630. $this->canEdit( false, false, false, $languageCode ) )
  3631. {
  3632. $languages[] = $language;
  3633. }
  3634. }
  3635. return $languages;
  3636. }
  3637. /**
  3638. * Returns a list of languages of the current object that the current user can create
  3639. *
  3640. * @return eZContentLanguage[]
  3641. */
  3642. function canCreateLanguages()
  3643. {
  3644. $availableLanguages = $this->availableLanguages();
  3645. $languages = array();
  3646. foreach ( eZContentLanguage::prioritizedLanguages() as $language )
  3647. {
  3648. $languageCode = $language->attribute( 'locale' );
  3649. if ( !in_array( $languageCode, $availableLanguages ) &&
  3650. $this->checkAccess( 'edit', false, false, false, $languageCode ) )
  3651. {
  3652. $languages[] = $language;
  3653. }
  3654. }
  3655. return $languages;
  3656. }
  3657. /**
  3658. * @param array $limitationValueList
  3659. * @param int $userID
  3660. * @param int|bool $contentObjectID
  3661. * @return string Returns "denied" or "allowed"
  3662. */
  3663. function checkGroupLimitationAccess( $limitationValueList, $userID, $contentObjectID = false )
  3664. {
  3665. $access = 'denied';
  3666. if ( is_array( $limitationValueList ) && is_numeric( $userID ) )
  3667. {
  3668. if ( $contentObjectID !== false )
  3669. {
  3670. $contentObject = eZContentObject::fetch( $contentObjectID );
  3671. }
  3672. else
  3673. {
  3674. $contentObject = $this;
  3675. }
  3676. if ( is_object( $contentObject ) )
  3677. {
  3678. // limitation value == 1, means "self group"
  3679. if ( in_array( 1, $limitationValueList ) )
  3680. {
  3681. // no need to check groups if user ownes this object
  3682. $ownerID = $contentObject->attribute( 'owner_id' );
  3683. if ( $ownerID == $userID || $contentObject->attribute( 'id' ) == $userID )
  3684. {
  3685. $access = 'allowed';
  3686. }
  3687. else
  3688. {
  3689. // get parent node ids for 'user' and 'owner'
  3690. $groupList = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( array( $userID, $ownerID ), true );
  3691. // find group(s) which is common for 'user' and 'owner'
  3692. $commonGroup = array_intersect( $groupList[$userID], $groupList[$ownerID] );
  3693. if ( count( $commonGroup ) > 0 )
  3694. {
  3695. // ok, we have at least 1 common group
  3696. $access = 'allowed';
  3697. }
  3698. }
  3699. }
  3700. }
  3701. }
  3702. return $access;
  3703. }
  3704. /**
  3705. * Check access for the current object
  3706. *
  3707. * @param string $functionName Function name ( edit, read, remove, etc. )
  3708. * @param int|bool $originalClassID Used to check access for object creation
  3709. * @param int|bool $parentClassID Used to check access for object creation
  3710. * @param bool $returnAccessList If true, returns access list instead of access result
  3711. * @param string|bool $language
  3712. * @return array|int 1 if has access, 0 if not, array if $returnAccessList is true
  3713. */
  3714. function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
  3715. {
  3716. $classID = $originalClassID;
  3717. $user = eZUser::currentUser();
  3718. $userID = $user->attribute( 'contentobject_id' );
  3719. $origFunctionName = $functionName;
  3720. // Fetch the ID of the language if we get a string with a language code
  3721. // e.g. 'eng-GB'
  3722. $originalLanguage = $language;
  3723. if ( is_string( $language ) && strlen( $language ) > 0 )
  3724. {
  3725. $language = eZContentLanguage::idByLocale( $language );
  3726. }
  3727. else
  3728. {
  3729. $language = false;
  3730. }
  3731. // This will be filled in with the available languages of the object
  3732. // if a Language check is performed.
  3733. $languageList = false;
  3734. // The 'move' function simply reuses 'edit' for generic access
  3735. // but adds another top-level check below
  3736. // The original function is still available in $origFunctionName
  3737. if ( $functionName == 'move' )
  3738. $functionName = 'edit';
  3739. $accessResult = $user->hasAccessTo( 'content' , $functionName );
  3740. $accessWord = $accessResult['accessWord'];
  3741. /*
  3742. // Uncomment this part if 'create' permissions should become implied 'edit'.
  3743. // Merges in 'create' policies with 'edit'
  3744. if ( $functionName == 'edit' &&
  3745. !in_array( $accessWord, array( 'yes', 'no' ) ) )
  3746. {
  3747. // Add in create policies.
  3748. $accessExtraResult = $user->hasAccessTo( 'content', 'create' );
  3749. if ( $accessExtraResult['accessWord'] != 'no' )
  3750. {
  3751. $accessWord = $accessExtraResult['accessWord'];
  3752. if ( isset( $accessExtraResult['policies'] ) )
  3753. {
  3754. $accessResult['policies'] = array_merge( $accessResult['policies'],
  3755. $accessExtraResult['policies'] );
  3756. }
  3757. if ( isset( $accessExtraResult['accessList'] ) )
  3758. {
  3759. $accessResult['accessList'] = array_merge( $accessResult['accessList'],
  3760. $accessExtraResult['accessList'] );
  3761. }
  3762. }
  3763. }
  3764. */
  3765. if ( $origFunctionName == 'remove' or
  3766. $origFunctionName == 'move' )
  3767. {
  3768. $mainNode = $this->attribute( 'main_node' );
  3769. // We do not allow these actions on objects placed at top-level
  3770. // - remove
  3771. // - move
  3772. if ( $mainNode and $mainNode->attribute( 'parent_node_id' ) <= 1 )
  3773. {
  3774. return 0;
  3775. }
  3776. }
  3777. if ( $classID === false )
  3778. {
  3779. $classID = $this->attribute( 'contentclass_id' );
  3780. }
  3781. if ( $accessWord == 'yes' )
  3782. {
  3783. return 1;
  3784. }
  3785. else if ( $accessWord == 'no' )
  3786. {
  3787. if ( $functionName == 'edit' )
  3788. {
  3789. // Check if we have 'create' access under the main parent
  3790. if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) )
  3791. {
  3792. $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) );
  3793. $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
  3794. if ( $parentObj instanceof eZContentObject )
  3795. {
  3796. $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ),
  3797. $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
  3798. return $result;
  3799. }
  3800. else
  3801. {
  3802. eZDebug::writeError( "Error retrieving parent object of main node for object id: " . $this->attribute( 'id' ), __METHOD__ );
  3803. }
  3804. }
  3805. return 0;
  3806. }
  3807. if ( $returnAccessList === false )
  3808. {
  3809. return 0;
  3810. }
  3811. else
  3812. {
  3813. return $accessResult['accessList'];
  3814. }
  3815. }
  3816. else
  3817. {
  3818. $policies =& $accessResult['policies'];
  3819. $access = 'denied';
  3820. foreach ( array_keys( $policies ) as $pkey )
  3821. {
  3822. $limitationArray =& $policies[ $pkey ];
  3823. if ( $access == 'allowed' )
  3824. {
  3825. break;
  3826. }
  3827. $limitationList = array();
  3828. if ( isset( $limitationArray['Subtree' ] ) )
  3829. {
  3830. $checkedSubtree = false;
  3831. }
  3832. else
  3833. {
  3834. $checkedSubtree = true;
  3835. $accessSubtree = false;
  3836. }
  3837. if ( isset( $limitationArray['Node'] ) )
  3838. {
  3839. $checkedNode = false;
  3840. }
  3841. else
  3842. {
  3843. $checkedNode = true;
  3844. $accessNode = false;
  3845. }
  3846. foreach ( array_keys( $limitationArray ) as $key )
  3847. {
  3848. $access = 'denied';
  3849. switch( $key )
  3850. {
  3851. case 'Class':
  3852. {
  3853. if ( $functionName == 'create' and
  3854. !$originalClassID )
  3855. {
  3856. $access = 'allowed';
  3857. }
  3858. else if ( $functionName == 'create' and
  3859. in_array( $classID, $limitationArray[$key] ) )
  3860. {
  3861. $access = 'allowed';
  3862. }
  3863. else if ( $functionName != 'create' and
  3864. in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key] ) )
  3865. {
  3866. $access = 'allowed';
  3867. }
  3868. else
  3869. {
  3870. $access = 'denied';
  3871. $limitationList = array( 'Limitation' => $key,
  3872. 'Required' => $limitationArray[$key] );
  3873. }
  3874. } break;
  3875. case 'ParentClass':
  3876. {
  3877. if ( in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key] ) )
  3878. {
  3879. $access = 'allowed';
  3880. }
  3881. else
  3882. {
  3883. $access = 'denied';
  3884. $limitationList = array( 'Limitation' => $key,
  3885. 'Required' => $limitationArray[$key] );
  3886. }
  3887. } break;
  3888. case 'ParentDepth':
  3889. {
  3890. $assignedNodes = $this->attribute( 'assigned_nodes' );
  3891. if ( count( $assignedNodes ) > 0 )
  3892. {
  3893. foreach ( $assignedNodes as $assignedNode )
  3894. {
  3895. $depth = $assignedNode->attribute( 'depth' );
  3896. if ( in_array( $depth, $limitationArray[$key] ) )
  3897. {
  3898. $access = 'allowed';
  3899. break;
  3900. }
  3901. }
  3902. }
  3903. if ( $access != 'allowed' )
  3904. {
  3905. $access = 'denied';
  3906. $limitationList = array( 'Limitation' => $key,
  3907. 'Required' => $limitationArray[$key] );
  3908. }
  3909. } break;
  3910. case 'Section':
  3911. case 'User_Section':
  3912. {
  3913. if ( in_array( $this->attribute( 'section_id' ), $limitationArray[$key] ) )
  3914. {
  3915. $access = 'allowed';
  3916. }
  3917. else
  3918. {
  3919. $access = 'denied';
  3920. $limitationList = array( 'Limitation' => $key,
  3921. 'Required' => $limitationArray[$key] );
  3922. }
  3923. } break;
  3924. case 'Language':
  3925. {
  3926. $languageMask = 0;
  3927. // If we don't have a language list yet we need to fetch it
  3928. // and optionally filter out based on $language.
  3929. if ( $functionName == 'create' )
  3930. {
  3931. // If the function is 'create' we do not use the language_mask for matching.
  3932. if ( $language !== false )
  3933. {
  3934. $languageMask = $language;
  3935. }
  3936. else
  3937. {
  3938. // If the create is used and no language specified then
  3939. // we need to match against all possible languages (which
  3940. // is all bits set, ie. -1).
  3941. $languageMask = -1;
  3942. }
  3943. }
  3944. else
  3945. {
  3946. if ( $language !== false )
  3947. {
  3948. if ( $languageList === false )
  3949. {
  3950. $languageMask = (int)$this->attribute( 'language_mask' );
  3951. // We are restricting language check to just one language
  3952. $languageMask &= (int)$language;
  3953. // If the resulting mask is 0 it means that the user is trying to
  3954. // edit a language which does not exist, ie. translating.
  3955. // The mask will then become the language trying to edit.
  3956. if ( $languageMask == 0 )
  3957. {
  3958. $languageMask = $language;
  3959. }
  3960. }
  3961. }
  3962. else
  3963. {
  3964. $languageMask = -1;
  3965. }
  3966. }
  3967. // Fetch limit mask for limitation list
  3968. $limitMask = eZContentLanguage::maskByLocale( $limitationArray[$key] );
  3969. if ( ( $languageMask & $limitMask ) != 0 )
  3970. {
  3971. $access = 'allowed';
  3972. }
  3973. else
  3974. {
  3975. $access = 'denied';
  3976. $limitationList = array( 'Limitation' => $key,
  3977. 'Required' => $limitationArray[$key] );
  3978. }
  3979. } break;
  3980. case 'Owner':
  3981. case 'ParentOwner':
  3982. {
  3983. // if limitation value == 2, anonymous limited to current session.
  3984. if ( in_array( 2, $limitationArray[$key] ) &&
  3985. $user->isAnonymous() )
  3986. {
  3987. $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
  3988. if ( $createdObjectIDList &&
  3989. in_array( $this->ID, unserialize( $createdObjectIDList ) ) )
  3990. {
  3991. $access = 'allowed';
  3992. }
  3993. }
  3994. else if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
  3995. {
  3996. $access = 'allowed';
  3997. }
  3998. if ( $access != 'allowed' )
  3999. {
  4000. $access = 'denied';
  4001. $limitationList = array ( 'Limitation' => $key, 'Required' => $limitationArray[$key] );
  4002. }
  4003. } break;
  4004. case 'Group':
  4005. case 'ParentGroup':
  4006. {
  4007. $access = $this->checkGroupLimitationAccess( $limitationArray[$key], $userID );
  4008. if ( $access != 'allowed' )
  4009. {
  4010. $access = 'denied';
  4011. $limitationList = array ( 'Limitation' => $key,
  4012. 'Required' => $limitationArray[$key] );
  4013. }
  4014. } break;
  4015. case 'State':
  4016. {
  4017. if ( count( array_intersect( $limitationArray[$key], $this->attribute( 'state_id_array' ) ) ) == 0 )
  4018. {
  4019. $access = 'denied';
  4020. $limitationList = array ( 'Limitation' => $key,
  4021. 'Required' => $limitationArray[$key] );
  4022. }
  4023. else
  4024. {
  4025. $access = 'allowed';
  4026. }
  4027. } break;
  4028. case 'Node':
  4029. {
  4030. $accessNode = false;
  4031. $mainNodeID = $this->attribute( 'main_node_id' );
  4032. foreach ( $limitationArray[$key] as $nodeID )
  4033. {
  4034. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  4035. $limitationNodeID = $node['main_node_id'];
  4036. if ( $mainNodeID == $limitationNodeID )
  4037. {
  4038. $access = 'allowed';
  4039. $accessNode = true;
  4040. break;
  4041. }
  4042. }
  4043. if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree )
  4044. {
  4045. $access = 'denied';
  4046. // ??? TODO: if there is a limitation on Subtree, return two limitations?
  4047. $limitationList = array( 'Limitation' => $key,
  4048. 'Required' => $limitationArray[$key] );
  4049. }
  4050. else
  4051. {
  4052. $access = 'allowed';
  4053. }
  4054. $checkedNode = true;
  4055. } break;
  4056. case 'Subtree':
  4057. {
  4058. $accessSubtree = false;
  4059. $assignedNodes = $this->attribute( 'assigned_nodes' );
  4060. if ( count( $assignedNodes ) != 0 )
  4061. {
  4062. foreach ( $assignedNodes as $assignedNode )
  4063. {
  4064. $path = $assignedNode->attribute( 'path_string' );
  4065. $subtreeArray = $limitationArray[$key];
  4066. foreach ( $subtreeArray as $subtreeString )
  4067. {
  4068. if ( strstr( $path, $subtreeString ) )
  4069. {
  4070. $access = 'allowed';
  4071. $accessSubtree = true;
  4072. break;
  4073. }
  4074. }
  4075. }
  4076. }
  4077. else
  4078. {
  4079. $parentNodes = $this->attribute( 'parent_nodes' );
  4080. if ( count( $parentNodes ) == 0 )
  4081. {
  4082. if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
  4083. {
  4084. $access = 'allowed';
  4085. $accessSubtree = true;
  4086. }
  4087. }
  4088. else
  4089. {
  4090. foreach ( $parentNodes as $parentNode )
  4091. {
  4092. $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false );
  4093. $path = $parentNode['path_string'];
  4094. $subtreeArray = $limitationArray[$key];
  4095. foreach ( $subtreeArray as $subtreeString )
  4096. {
  4097. if ( strstr( $path, $subtreeString ) )
  4098. {
  4099. $access = 'allowed';
  4100. $accessSubtree = true;
  4101. break;
  4102. }
  4103. }
  4104. }
  4105. }
  4106. }
  4107. if ( $access != 'allowed' && $checkedNode && !$accessNode )
  4108. {
  4109. $access = 'denied';
  4110. // ??? TODO: if there is a limitation on Node, return two limitations?
  4111. $limitationList = array( 'Limitation' => $key,
  4112. 'Required' => $limitationArray[$key] );
  4113. }
  4114. else
  4115. {
  4116. $access = 'allowed';
  4117. }
  4118. $checkedSubtree = true;
  4119. } break;
  4120. case 'User_Subtree':
  4121. {
  4122. $assignedNodes = $this->attribute( 'assigned_nodes' );
  4123. if ( count( $assignedNodes ) != 0 )
  4124. {
  4125. foreach ( $assignedNodes as $assignedNode )
  4126. {
  4127. $path = $assignedNode->attribute( 'path_string' );
  4128. $subtreeArray = $limitationArray[$key];
  4129. foreach ( $subtreeArray as $subtreeString )
  4130. {
  4131. if ( strstr( $path, $subtreeString ) )
  4132. {
  4133. $access = 'allowed';
  4134. }
  4135. }
  4136. }
  4137. }
  4138. else
  4139. {
  4140. $parentNodes = $this->attribute( 'parent_nodes' );
  4141. if ( count( $parentNodes ) == 0 )
  4142. {
  4143. if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID )
  4144. {
  4145. $access = 'allowed';
  4146. }
  4147. }
  4148. else
  4149. {
  4150. foreach ( $parentNodes as $parentNode )
  4151. {
  4152. $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false );
  4153. $path = $parentNode['path_string'];
  4154. $subtreeArray = $limitationArray[$key];
  4155. foreach ( $subtreeArray as $subtreeString )
  4156. {
  4157. if ( strstr( $path, $subtreeString ) )
  4158. {
  4159. $access = 'allowed';
  4160. break;
  4161. }
  4162. }
  4163. }
  4164. }
  4165. }
  4166. if ( $access != 'allowed' )
  4167. {
  4168. $access = 'denied';
  4169. $limitationList = array( 'Limitation' => $key,
  4170. 'Required' => $limitationArray[$key] );
  4171. }
  4172. } break;
  4173. default:
  4174. {
  4175. if ( strncmp( $key, 'StateGroup_', 11 ) === 0 )
  4176. {
  4177. if ( count( array_intersect( $limitationArray[$key],
  4178. $this->attribute( 'state_id_array' ) ) ) == 0 )
  4179. {
  4180. $access = 'denied';
  4181. $limitationList = array ( 'Limitation' => $key,
  4182. 'Required' => $limitationArray[$key] );
  4183. }
  4184. else
  4185. {
  4186. $access = 'allowed';
  4187. }
  4188. }
  4189. }
  4190. }
  4191. if ( $access == 'denied' )
  4192. {
  4193. break;
  4194. }
  4195. }
  4196. $policyList[] = array( 'PolicyID' => $pkey,
  4197. 'LimitationList' => $limitationList );
  4198. }
  4199. if ( $access == 'denied' )
  4200. {
  4201. if ( $functionName == 'edit' )
  4202. {
  4203. // Check if we have 'create' access under the main parent
  4204. if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) )
  4205. {
  4206. $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) );
  4207. $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
  4208. if ( $parentObj instanceof eZContentObject )
  4209. {
  4210. $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ),
  4211. $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
  4212. }
  4213. else
  4214. {
  4215. eZDebug::writeError( "Error retrieving parent object of main node for object id: " . $this->attribute( 'id' ), __METHOD__ );
  4216. $result = 0;
  4217. }
  4218. if ( $result )
  4219. {
  4220. $access = 'allowed';
  4221. }
  4222. return $result;
  4223. }
  4224. }
  4225. }
  4226. if ( $access == 'denied' )
  4227. {
  4228. if ( $returnAccessList === false )
  4229. {
  4230. return 0;
  4231. }
  4232. else
  4233. {
  4234. return array( 'FunctionRequired' => array ( 'Module' => 'content',
  4235. 'Function' => $origFunctionName,
  4236. 'ClassID' => $classID,
  4237. 'MainNodeID' => $this->attribute( 'main_node_id' ) ),
  4238. 'PolicyList' => $policyList );
  4239. }
  4240. }
  4241. else
  4242. {
  4243. return 1;
  4244. }
  4245. }
  4246. }
  4247. // code-template::create-block: class-list-from-policy, is-object
  4248. // code-template::auto-generated:START class-list-from-policy
  4249. // This code is automatically generated from templates/classlistfrompolicy.ctpl
  4250. // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
  4251. /**
  4252. * Returns an array of classes the current user has access to
  4253. *
  4254. * @param array $policy
  4255. * @param array|bool $allowedLanguageCodes
  4256. * @return array
  4257. */
  4258. function classListFromPolicy( $policy, $allowedLanguageCodes = false )
  4259. {
  4260. $canCreateClassIDListPart = array();
  4261. $hasClassIDLimitation = false;
  4262. $user = eZUser::currentUser();
  4263. $userID = $user->attribute( 'contentobject_id' );
  4264. if ( isset( $policy['ParentOwner'] ) )
  4265. {
  4266. // if limitation value == 2, anonymous limited to current session.
  4267. if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() )
  4268. {
  4269. $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
  4270. if ( !$createdObjectIDList ||
  4271. !in_array( $this->ID, unserialize( $createdObjectIDList ) ) )
  4272. {
  4273. return array();
  4274. }
  4275. }
  4276. else if ( $this->attribute( 'owner_id' ) != $userID &&
  4277. $this->ID != $userID )
  4278. {
  4279. return array();
  4280. }
  4281. }
  4282. if ( isset( $policy['ParentGroup'] ) )
  4283. {
  4284. $access = $this->checkGroupLimitationAccess( $policy['ParentGroup'], $userID );
  4285. if ( $access !== 'allowed' )
  4286. {
  4287. return array();
  4288. }
  4289. }
  4290. if ( isset( $policy['Class'] ) )
  4291. {
  4292. $canCreateClassIDListPart = $policy['Class'];
  4293. $hasClassIDLimitation = true;
  4294. }
  4295. if ( isset( $policy['User_Section'] ) )
  4296. {
  4297. if ( !in_array( $this->attribute( 'section_id' ), $policy['User_Section'] ) )
  4298. {
  4299. return array();
  4300. }
  4301. }
  4302. if ( isset( $policy['User_Subtree'] ) )
  4303. {
  4304. $allowed = false;
  4305. $assignedNodes = $this->attribute( 'assigned_nodes' );
  4306. foreach ( $assignedNodes as $assignedNode )
  4307. {
  4308. $path = $assignedNode->attribute( 'path_string' );
  4309. foreach ( $policy['User_Subtree'] as $subtreeString )
  4310. {
  4311. if ( strstr( $path, $subtreeString ) )
  4312. {
  4313. $allowed = true;
  4314. break;
  4315. }
  4316. }
  4317. }
  4318. if( !$allowed )
  4319. {
  4320. return array();
  4321. }
  4322. }
  4323. if ( isset( $policy['Section'] ) )
  4324. {
  4325. if ( !in_array( $this->attribute( 'section_id' ), $policy['Section'] ) )
  4326. {
  4327. return array();
  4328. }
  4329. }
  4330. if ( isset( $policy['ParentClass'] ) )
  4331. {
  4332. if ( !in_array( $this->attribute( 'contentclass_id' ), $policy['ParentClass'] ) )
  4333. {
  4334. return array();
  4335. }
  4336. }
  4337. if ( isset( $policy['Assigned'] ) )
  4338. {
  4339. if ( $this->attribute( 'owner_id' ) != $userID )
  4340. {
  4341. return array();
  4342. }
  4343. }
  4344. $allowedNode = false;
  4345. if ( isset( $policy['Node'] ) )
  4346. {
  4347. $allowed = false;
  4348. foreach( $policy['Node'] as $nodeID )
  4349. {
  4350. $mainNodeID = $this->attribute( 'main_node_id' );
  4351. $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
  4352. if ( $mainNodeID == $node['main_node_id'] )
  4353. {
  4354. $allowed = true;
  4355. $allowedNode = true;
  4356. break;
  4357. }
  4358. }
  4359. if ( !$allowed && !isset( $policy['Subtree'] ) )
  4360. {
  4361. return array();
  4362. }
  4363. }
  4364. if ( isset( $policy['Subtree'] ) )
  4365. {
  4366. $allowed = false;
  4367. $assignedNodes = $this->attribute( 'assigned_nodes' );
  4368. foreach ( $assignedNodes as $assignedNode )
  4369. {
  4370. $path = $assignedNode->attribute( 'path_string' );
  4371. foreach ( $policy['Subtree'] as $subtreeString )
  4372. {
  4373. if ( strstr( $path, $subtreeString ) )
  4374. {
  4375. $allowed = true;
  4376. break;
  4377. }
  4378. }
  4379. }
  4380. if ( !$allowed && !$allowedNode )
  4381. {
  4382. return array();
  4383. }
  4384. }
  4385. if ( isset( $policy['Language'] ) )
  4386. {
  4387. if ( $allowedLanguageCodes )
  4388. {
  4389. $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] );
  4390. }
  4391. else
  4392. {
  4393. $allowedLanguageCodes = $policy['Language'];
  4394. }
  4395. }
  4396. if ( $hasClassIDLimitation )
  4397. {
  4398. return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes );
  4399. }
  4400. return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes );
  4401. }
  4402. // This code is automatically generated from templates/classlistfrompolicy.ctpl
  4403. // code-template::auto-generated:END class-list-from-policy
  4404. // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation
  4405. // code-template::auto-generated:START can-instantiate-class-list
  4406. // This code is automatically generated from templates/classcreatelist.ctpl
  4407. // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
  4408. /**
  4409. * Finds all classes that the current user can create objects from and returns.
  4410. * It is also possible to filter the list event more with $includeFilter and $groupList.
  4411. *
  4412. * @param bool $asObject If true then it return eZContentClass objects, if not it will be an associative array
  4413. * @param bool $includeFilter If true then it will include only from class groups defined in $groupList, if not it will exclude those groups.
  4414. * @param bool $groupList An array with class group IDs that should be used in filtering, use false if you do not wish to filter at all.
  4415. * @param bool $fetchID A unique name for the current fetch, this must be supplied when filtering is used if you want caching to work.
  4416. * @return array|eZPersistentObject[]
  4417. */
  4418. function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false )
  4419. {
  4420. $ini = eZINI::instance();
  4421. $groupArray = array();
  4422. $languageCodeList = eZContentLanguage::fetchLocaleList();
  4423. $allowedLanguages = array( '*' => array() );
  4424. $user = eZUser::currentUser();
  4425. $accessResult = $user->hasAccessTo( 'content' , 'create' );
  4426. $accessWord = $accessResult['accessWord'];
  4427. $classIDArray = array();
  4428. $classList = array();
  4429. $fetchAll = false;
  4430. if ( $accessWord == 'yes' )
  4431. {
  4432. $fetchAll = true;
  4433. $allowedLanguages['*'] = $languageCodeList;
  4434. }
  4435. else if ( $accessWord == 'no' )
  4436. {
  4437. // Cannot create any objects, return empty list.
  4438. return $classList;
  4439. }
  4440. else
  4441. {
  4442. $policies = $accessResult['policies'];
  4443. foreach ( $policies as $policyKey => $policy )
  4444. {
  4445. $policyArray = $this->classListFromPolicy( $policy, $languageCodeList );
  4446. if ( empty( $policyArray ) )
  4447. {
  4448. continue;
  4449. }
  4450. $classIDArrayPart = $policyArray['classes'];
  4451. $languageCodeArrayPart = $policyArray['language_codes'];
  4452. // No class limitation for this policy AND no previous limitation(s)
  4453. if ( $classIDArrayPart == '*' && empty( $classIDArray ) )
  4454. {
  4455. $fetchAll = true;
  4456. $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) );
  4457. }
  4458. else if ( is_array( $classIDArrayPart ) )
  4459. {
  4460. $fetchAll = false;
  4461. foreach( $classIDArrayPart as $class )
  4462. {
  4463. if ( isset( $allowedLanguages[$class] ) )
  4464. {
  4465. $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) );
  4466. }
  4467. else
  4468. {
  4469. $allowedLanguages[$class] = $languageCodeArrayPart;
  4470. }
  4471. }
  4472. $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) );
  4473. }
  4474. }
  4475. }
  4476. $db = eZDB::instance();
  4477. $filterTableSQL = '';
  4478. $filterSQL = '';
  4479. // Create extra SQL statements for the class group filters.
  4480. if ( is_array( $groupList ) )
  4481. {
  4482. if ( count( $groupList ) == 0 )
  4483. {
  4484. return $classList;
  4485. }
  4486. $filterTableSQL = ', ezcontentclass_classgroup ccg';
  4487. $filterSQL = ( " AND" .
  4488. " cc.id = ccg.contentclass_id AND" .
  4489. " " );
  4490. $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' );
  4491. }
  4492. $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
  4493. if ( $fetchAll )
  4494. {
  4495. // If $asObject is true we fetch all fields in class
  4496. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  4497. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  4498. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  4499. "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
  4500. "ORDER BY $classNameFilter[nameField] ASC" );
  4501. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  4502. }
  4503. else
  4504. {
  4505. // If the constrained class list is empty we are not allowed to create any class
  4506. if ( count( $classIDArray ) == 0 )
  4507. {
  4508. return $classList;
  4509. }
  4510. $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' );
  4511. // If $asObject is true we fetch all fields in class
  4512. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  4513. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  4514. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  4515. "WHERE $classIDCondition AND" .
  4516. " cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
  4517. "ORDER BY $classNameFilter[nameField] ASC" );
  4518. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  4519. }
  4520. if ( $asObject )
  4521. {
  4522. foreach ( $classList as $key => $class )
  4523. {
  4524. $id = $class->attribute( 'id' );
  4525. if ( isset( $allowedLanguages[$id] ) )
  4526. {
  4527. $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) );
  4528. }
  4529. else
  4530. {
  4531. $languageCodes = $allowedLanguages['*'];
  4532. }
  4533. $classList[$key]->setCanInstantiateLanguages( $languageCodes );
  4534. }
  4535. }
  4536. eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" );
  4537. return $classList;
  4538. }
  4539. // This code is automatically generated from templates/classcreatelist.ctpl
  4540. // code-template::auto-generated:END can-instantiate-class-list
  4541. /**
  4542. * Get accesslist for specified function
  4543. *
  4544. * @param string $function
  4545. * @return array|int
  4546. */
  4547. function accessList( $function )
  4548. {
  4549. switch( $function )
  4550. {
  4551. case 'read':
  4552. {
  4553. return $this->checkAccess( 'read', false, false, true );
  4554. } break;
  4555. case 'edit':
  4556. {
  4557. return $this->checkAccess( 'edit', false, false, true );
  4558. } break;
  4559. }
  4560. return 0;
  4561. }
  4562. /**
  4563. * Returns true if the current user can read this content object.
  4564. *
  4565. * @return bool
  4566. */
  4567. function canRead( )
  4568. {
  4569. if ( !isset( $this->Permissions["can_read"] ) )
  4570. {
  4571. $this->Permissions["can_read"] = $this->checkAccess( 'read' );
  4572. }
  4573. return ( $this->Permissions["can_read"] == 1 );
  4574. }
  4575. /**
  4576. * Returns true if the current user can create a pdf of this content object.
  4577. *
  4578. * @return bool
  4579. */
  4580. function canPdf( )
  4581. {
  4582. if ( !isset( $this->Permissions["can_pdf"] ) )
  4583. {
  4584. $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' );
  4585. }
  4586. return ( $this->Permissions["can_pdf"] == 1 );
  4587. }
  4588. /**
  4589. * Returns true if the node can be viewed as embeded object by the current user.
  4590. *
  4591. * @return bool
  4592. */
  4593. function canViewEmbed( )
  4594. {
  4595. if ( !isset( $this->Permissions["can_view_embed"] ) )
  4596. {
  4597. $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' );
  4598. }
  4599. return ( $this->Permissions["can_view_embed"] == 1 );
  4600. }
  4601. /**
  4602. * Returns true if the current user can diff this content object.
  4603. *
  4604. * @return bool
  4605. */
  4606. function canDiff( )
  4607. {
  4608. if ( !isset( $this->Permissions["can_diff"] ) )
  4609. {
  4610. $this->Permissions["can_diff"] = $this->checkAccess( 'diff' );
  4611. }
  4612. return ( $this->Permissions["can_diff"] == 1 );
  4613. }
  4614. /**
  4615. * Returns true if the current user can create a content object like this one.
  4616. *
  4617. * @return bool
  4618. */
  4619. function canCreate( )
  4620. {
  4621. if ( !isset( $this->Permissions["can_create"] ) )
  4622. {
  4623. $this->Permissions["can_create"] = $this->checkAccess( 'create' );
  4624. }
  4625. return ( $this->Permissions["can_create"] == 1 );
  4626. }
  4627. /**
  4628. * Returns true if the current user can edit this content object.
  4629. *
  4630. * @param int|bool $originalClassID
  4631. * @param int|bool $parentClassID
  4632. * @param bool $returnAccessList
  4633. * @param string|bool $language
  4634. * @return bool
  4635. */
  4636. function canEdit( $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
  4637. {
  4638. $isCalledClean = ( func_num_args() == 0 );
  4639. if ( isset( $this->Permissions["can_edit"] ) && $isCalledClean )
  4640. {
  4641. $canEdit = $this->Permissions["can_edit"];
  4642. }
  4643. else
  4644. {
  4645. $canEdit = $this->checkAccess( 'edit', $originalClassID, $parentClassID, $returnAccessList, $language );
  4646. if ( $canEdit != 1 )
  4647. {
  4648. $user = eZUser::currentUser();
  4649. if ( $user->attribute( 'contentobject_id' ) === $this->attribute( 'id' ) )
  4650. {
  4651. $access = $user->hasAccessTo( 'user', 'selfedit' );
  4652. if ( $access['accessWord'] == 'yes' )
  4653. {
  4654. $canEdit = 1;
  4655. }
  4656. }
  4657. }
  4658. if ( $isCalledClean )
  4659. {
  4660. $this->Permissions["can_edit"] = $canEdit;
  4661. }
  4662. }
  4663. return ( $canEdit == 1 );
  4664. }
  4665. /**
  4666. * Returns true if the current user can translate this content object.
  4667. *
  4668. * @return bool
  4669. */
  4670. function canTranslate( )
  4671. {
  4672. if ( !isset( $this->Permissions["can_translate"] ) )
  4673. {
  4674. $this->Permissions["can_translate"] = $this->checkAccess( 'translate' );
  4675. if ( $this->Permissions["can_translate"] != 1 )
  4676. {
  4677. $user = eZUser::currentUser();
  4678. if ( $user->id() == $this->attribute( 'id' ) )
  4679. {
  4680. $access = $user->hasAccessTo( 'user', 'selfedit' );
  4681. if ( $access['accessWord'] == 'yes' )
  4682. {
  4683. $this->Permissions["can_translate"] = 1;
  4684. }
  4685. }
  4686. }
  4687. }
  4688. return ( $this->Permissions["can_translate"] == 1 );
  4689. }
  4690. /**
  4691. * Returns true if the current user can remove this content object.
  4692. *
  4693. * @return bool
  4694. */
  4695. function canRemove( )
  4696. {
  4697. if ( !isset( $this->Permissions["can_remove"] ) )
  4698. {
  4699. $this->Permissions["can_remove"] = $this->checkAccess( 'remove' );
  4700. }
  4701. return ( $this->Permissions["can_remove"] == 1 );
  4702. }
  4703. /**
  4704. * Returns true if the current user can move this content object.
  4705. *
  4706. * @return bool
  4707. */
  4708. function canMoveFrom( )
  4709. {
  4710. if ( !isset( $this->Permissions['can_move_from'] ) )
  4711. {
  4712. $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' );
  4713. }
  4714. return ( $this->Permissions['can_move_from'] == 1 );
  4715. }
  4716. /**
  4717. * Returns the name of the class which this object was created from.
  4718. *
  4719. * The object will cache the class name information so multiple calls will be fast.
  4720. *
  4721. * @return string|bool|null
  4722. */
  4723. function className()
  4724. {
  4725. if ( !is_numeric( $this->ClassID ) )
  4726. {
  4727. return null;
  4728. }
  4729. if ( $this->ClassName !== false )
  4730. return $this->ClassName;
  4731. $db = eZDB::instance();
  4732. $id = (int)$this->ClassID;
  4733. $sql = "SELECT serialized_name_list FROM ezcontentclass WHERE id=$id and version=0";
  4734. $rows = $db->arrayQuery( $sql );
  4735. if ( count( $rows ) > 0 )
  4736. {
  4737. $this->ClassName = eZContentClass::nameFromSerializedString( $rows[0]['serialized_name_list'] );
  4738. }
  4739. return $this->ClassName;
  4740. }
  4741. /**
  4742. * Returns an array of the content actions which can be performed on the current object.
  4743. *
  4744. * @return array|bool
  4745. */
  4746. function contentActionList()
  4747. {
  4748. $version = $this->attribute( 'current_version' );
  4749. $language = $this->initialLanguageCode();
  4750. if ( !isset( $this->ContentObjectAttributeArray[$version][$language] ) )
  4751. {
  4752. $attributeList = $this->contentObjectAttributes();
  4753. $this->ContentObjectAttributeArray[$version][$language] =& $attributeList;
  4754. }
  4755. else
  4756. $attributeList = $this->ContentObjectAttributeArray[$version][$language];
  4757. // Fetch content actions if not already fetched
  4758. if ( $this->ContentActionList === false )
  4759. {
  4760. foreach ( $attributeList as $attribute )
  4761. {
  4762. $contentActionList = $attribute->contentActionList();
  4763. if ( is_array( $contentActionList ) && !empty( $contentActionList ) )
  4764. {
  4765. foreach ( $contentActionList as $action )
  4766. {
  4767. if ( !$this->hasContentAction( $action['action'] ) )
  4768. {
  4769. $this->ContentActionList[] = $action;
  4770. }
  4771. }
  4772. }
  4773. }
  4774. }
  4775. return $this->ContentActionList;
  4776. }
  4777. /**
  4778. * Returns true if the given content action is in the content action list
  4779. *
  4780. * @param string $name Name of the content action
  4781. * @return bool
  4782. */
  4783. function hasContentAction( $name )
  4784. {
  4785. $return = false;
  4786. if ( is_array ( $this->ContentActionList ) )
  4787. {
  4788. foreach ( $this->ContentActionList as $action )
  4789. {
  4790. if ( $action['action'] == $name )
  4791. {
  4792. $return = true;
  4793. }
  4794. }
  4795. }
  4796. return $return;
  4797. }
  4798. /**
  4799. * Returns the languages the object has been translated into/exists in.
  4800. *
  4801. * @return array An array with the language codes.
  4802. */
  4803. function availableLanguages()
  4804. {
  4805. $languages = array();
  4806. $languageObjects = $this->languages();
  4807. foreach ( $languageObjects as $languageObject )
  4808. {
  4809. $languages[] = $languageObject->attribute( 'locale' );
  4810. }
  4811. return $languages;
  4812. }
  4813. /**
  4814. * Returns the languages the object has been translated into/exists in as a JSON string
  4815. *
  4816. * @return bool|string
  4817. */
  4818. function availableLanguagesJsArray()
  4819. {
  4820. return eZContentLanguage::jsArrayByMask( $this->LanguageMask );
  4821. }
  4822. /**
  4823. * @return eZContentLanguage[]|array
  4824. */
  4825. function languages()
  4826. {
  4827. return isset( $this->LanguageMask ) ?
  4828. eZContentLanguage::prioritizedLanguagesByMask( $this->LanguageMask ) :
  4829. array();
  4830. }
  4831. /**
  4832. * @return eZContentLanguage[]|array
  4833. */
  4834. function allLanguages()
  4835. {
  4836. $languages = isset( $this->LanguageMask ) ? eZContentLanguage::languagesByMask( $this->LanguageMask ) : array();
  4837. return $languages;
  4838. }
  4839. /**
  4840. * @return string|bool
  4841. */
  4842. static function defaultLanguage()
  4843. {
  4844. if ( ! isset( $GLOBALS['eZContentObjectDefaultLanguage'] ) )
  4845. {
  4846. $defaultLanguage = false;
  4847. $language = eZContentLanguage::topPriorityLanguage();
  4848. if ( $language )
  4849. {
  4850. $defaultLanguage = $language->attribute( 'locale' );
  4851. }
  4852. else
  4853. {
  4854. $ini = eZINI::instance();
  4855. if ( $ini->hasVariable( 'RegionalSettings', 'ContentObjectLocale' ) )
  4856. {
  4857. $defaultLanguage = $ini->variable( 'RegionalSettings', 'ContentObjectLocale' );
  4858. eZContentLanguage::fetchByLocale( $defaultLanguage, true );
  4859. }
  4860. }
  4861. $GLOBALS['eZContentObjectDefaultLanguage'] = $defaultLanguage;
  4862. }
  4863. return $GLOBALS['eZContentObjectDefaultLanguage'];
  4864. }
  4865. /**
  4866. * Set default language. Checks if default language is valid.
  4867. *
  4868. * @deprecated
  4869. * @param string $lang Default language.
  4870. * @return bool
  4871. */
  4872. static function setDefaultLanguage( $lang )
  4873. {
  4874. return false;
  4875. }
  4876. /**
  4877. * @param string $name
  4878. */
  4879. function setClassName( $name )
  4880. {
  4881. $this->ClassName = $name;
  4882. }
  4883. /**
  4884. * Returns an array with locale strings, these strings represents the languages which content objects
  4885. * are allowed to be translated into.
  4886. *
  4887. * The setting ContentSettings/TranslationList in site.ini determines the array.
  4888. *
  4889. * @return string[]
  4890. */
  4891. static function translationStringList()
  4892. {
  4893. $translationList = array();
  4894. $languageList = eZContentLanguage::fetchList();
  4895. foreach ( $languageList as $language )
  4896. {
  4897. $translationList[] = $language->attribute( 'locale' );
  4898. }
  4899. return $translationList;
  4900. }
  4901. /**
  4902. * Returns an array with locale objects, these objects represents the languages the content objects are
  4903. * allowed to be translated into.
  4904. *
  4905. * The setting ContentSettings/TranslationList in site.ini determines the array.
  4906. *
  4907. * @return eZLocale[]
  4908. */
  4909. static function translationList()
  4910. {
  4911. $translationList = array();
  4912. $languageList = eZContentLanguage::fetchList();
  4913. foreach ( $languageList as $language )
  4914. {
  4915. $translationList[] = $language->localeObject();
  4916. }
  4917. return $translationList;
  4918. }
  4919. /**
  4920. * Returns the attributes for the content object version \a $version and content object \a $contentObjectID.
  4921. *
  4922. * @param int $version
  4923. * @param bool $asObject
  4924. * @return eZContentClassAttribute[]|array|null
  4925. */
  4926. function fetchClassAttributes( $version = 0, $asObject = true )
  4927. {
  4928. return eZContentClassAttribute::fetchListByClassID( $this->attribute( 'contentclass_id' ), $version, $asObject );
  4929. }
  4930. /**
  4931. * Maps input lange to another one if defined in $options['language_map'].
  4932. *
  4933. * If it cannot map it returns the original language.
  4934. *
  4935. * @param string $language
  4936. * @param array $options
  4937. * @return string
  4938. */
  4939. static function mapLanguage( $language, $options )
  4940. {
  4941. if ( isset( $options['language_map'][$language] ) )
  4942. {
  4943. return $options['language_map'][$language];
  4944. }
  4945. return $language;
  4946. }
  4947. /**
  4948. * Unserialize xml structure. Creates an object from xml input.
  4949. *
  4950. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  4951. * the calls within a db transaction; thus within db->begin and db->commit.
  4952. *
  4953. * @param mixed $package
  4954. * @param DOMElement $domNode
  4955. * @param array $options
  4956. * @param int|bool $ownerID Override owner ID, null to use XML owner id (optional)
  4957. * @param string $handlerType
  4958. * @return array|bool|eZContentObject|null created object, false if could not create object/xml invalid
  4959. */
  4960. static function unserialize( $package, $domNode, &$options, $ownerID = false, $handlerType = 'ezcontentobject' )
  4961. {
  4962. if ( $domNode->localName != 'object' )
  4963. {
  4964. $retValue = false;
  4965. return $retValue;
  4966. }
  4967. $initialLanguage = eZContentObject::mapLanguage( $domNode->getAttribute( 'initial_language' ), $options );
  4968. if( $initialLanguage === 'skip' )
  4969. {
  4970. $retValue = true;
  4971. return $retValue;
  4972. }
  4973. $sectionID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'section_id' );
  4974. if ( $ownerID === false )
  4975. {
  4976. $ownerID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'owner_id' );
  4977. }
  4978. $remoteID = $domNode->getAttribute( 'remote_id' );
  4979. $name = $domNode->getAttribute( 'name' );
  4980. $classRemoteID = $domNode->getAttribute( 'class_remote_id' );
  4981. $classIdentifier = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'class_identifier' );
  4982. $alwaysAvailable = ( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'always_available' ) == '1' );
  4983. $contentClass = eZContentClass::fetchByRemoteID( $classRemoteID );
  4984. if ( !$contentClass )
  4985. {
  4986. $contentClass = eZContentClass::fetchByIdentifier( $classIdentifier );
  4987. }
  4988. if ( !$contentClass )
  4989. {
  4990. $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_NO_CLASS,
  4991. 'element_id' => $remoteID,
  4992. 'description' => "Can't install object '$name': Unable to fetch class with remoteID: $classRemoteID." );
  4993. $retValue = false;
  4994. return $retValue;
  4995. }
  4996. /** @var DOMElement $versionListNode */
  4997. $versionListNode = $domNode->getElementsByTagName( 'version-list' )->item( 0 );
  4998. $importedLanguages = array();
  4999. foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode )
  5000. {
  5001. /** @var DOMElement $versionDOMNode */
  5002. foreach ( $versionDOMNode->getElementsByTagName( 'object-translation' ) as $versionDOMNodeChild )
  5003. {
  5004. /** @var DOMElement $versionDOMNodeChild */
  5005. $importedLanguage = eZContentObject::mapLanguage( $versionDOMNodeChild->getAttribute( 'language' ), $options );
  5006. $language = eZContentLanguage::fetchByLocale( $importedLanguage );
  5007. // Check if the language is allowed in this setup.
  5008. if ( $language )
  5009. {
  5010. $hasTranslation = true;
  5011. }
  5012. else
  5013. {
  5014. if ( $importedLanguage == 'skip' )
  5015. continue;
  5016. // if there is no needed translation in system then add it
  5017. $locale = eZLocale::instance( $importedLanguage );
  5018. $translationName = $locale->internationalLanguageName();
  5019. $translationLocale = $locale->localeCode();
  5020. if ( $locale->isValid() )
  5021. {
  5022. eZContentLanguage::addLanguage( $locale->localeCode(), $locale->internationalLanguageName() );
  5023. $hasTranslation = true;
  5024. }
  5025. else
  5026. $hasTranslation = false;
  5027. }
  5028. if ( $hasTranslation )
  5029. {
  5030. $importedLanguages[] = $importedLanguage;
  5031. $importedLanguages = array_unique( $importedLanguages );
  5032. }
  5033. }
  5034. }
  5035. // If object exists we return a error.
  5036. // Minimum install element is an object now.
  5037. $contentObject = eZContentObject::fetchByRemoteID( $remoteID );
  5038. // Figure out initial language
  5039. if ( !$initialLanguage ||
  5040. !in_array( $initialLanguage, $importedLanguages ) )
  5041. {
  5042. $initialLanguage = false;
  5043. foreach ( eZContentLanguage::prioritizedLanguages() as $language )
  5044. {
  5045. if ( in_array( $language->attribute( 'locale' ), $importedLanguages ) )
  5046. {
  5047. $initialLanguage = $language->attribute( 'locale' );
  5048. break;
  5049. }
  5050. }
  5051. }
  5052. if ( !$contentObject )
  5053. {
  5054. $firstVersion = true;
  5055. $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID );
  5056. }
  5057. else
  5058. {
  5059. $firstVersion = false;
  5060. $description = "Object '$name' already exists.";
  5061. $choosenAction = eZPackageHandler::errorChoosenAction( self::PACKAGE_ERROR_EXISTS,
  5062. $options, $description, $handlerType, false );
  5063. switch( $choosenAction )
  5064. {
  5065. case eZPackage::NON_INTERACTIVE:
  5066. case self::PACKAGE_UPDATE:
  5067. {
  5068. // Keep existing contentobject.
  5069. } break;
  5070. case self::PACKAGE_REPLACE:
  5071. {
  5072. eZContentObjectOperations::remove( $contentObject->attribute( 'id' ) );
  5073. unset( $contentObject );
  5074. $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID );
  5075. $firstVersion = true;
  5076. } break;
  5077. case self::PACKAGE_SKIP:
  5078. {
  5079. $retValue = true;
  5080. return $retValue;
  5081. } break;
  5082. case self::PACKAGE_NEW:
  5083. {
  5084. $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
  5085. $contentObject->store();
  5086. unset( $contentObject );
  5087. $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID );
  5088. $firstVersion = true;
  5089. } break;
  5090. default:
  5091. {
  5092. $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_EXISTS,
  5093. 'element_id' => $remoteID,
  5094. 'description' => $description,
  5095. 'actions' => array( self::PACKAGE_REPLACE => ezpI18n::tr( 'kernel/classes', 'Replace existing object' ),
  5096. self::PACKAGE_SKIP => ezpI18n::tr( 'kernel/classes', 'Skip object' ),
  5097. self::PACKAGE_NEW => ezpI18n::tr( 'kernel/classes', 'Keep existing and create a new one' ),
  5098. self::PACKAGE_UPDATE => ezpI18n::tr( 'kernel/classes', 'Update existing object' ) ) );
  5099. $retValue = false;
  5100. return $retValue;
  5101. } break;
  5102. }
  5103. }
  5104. $db = eZDB::instance();
  5105. $db->begin();
  5106. if ( $alwaysAvailable )
  5107. {
  5108. // Make sure always available bit is set.
  5109. $contentObject->setAttribute( 'language_mask', (int)$contentObject->attribute( 'language_mask' ) | 1 );
  5110. }
  5111. $contentObject->setAttribute( 'section_id', $sectionID );
  5112. $contentObject->store();
  5113. $activeVersion = false;
  5114. $lastVersion = false;
  5115. $versionListActiveVersion = $versionListNode->getAttribute( 'active_version' );
  5116. $contentObject->setAttribute( 'remote_id', $remoteID );
  5117. $contentObject->setAttribute( 'contentclass_id', $contentClass->attribute( 'id' ) );
  5118. $contentObject->store();
  5119. $sectionObject = eZSection::fetch( $sectionID );
  5120. if ( $sectionObject instanceof eZSection )
  5121. {
  5122. $updateWithParentSection = false;
  5123. }
  5124. else
  5125. {
  5126. $updateWithParentSection = true;
  5127. }
  5128. $options['language_array'] = $importedLanguages;
  5129. $versionList = array();
  5130. foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode )
  5131. {
  5132. unset( $nodeList );
  5133. $nodeList = array();
  5134. $contentObjectVersion = eZContentObjectVersion::unserialize( $versionDOMNode,
  5135. $contentObject,
  5136. $ownerID,
  5137. $sectionID,
  5138. $versionListActiveVersion,
  5139. $firstVersion,
  5140. $nodeList,
  5141. $options,
  5142. $package,
  5143. 'ezcontentobject',
  5144. $initialLanguage );
  5145. if ( !$contentObjectVersion )
  5146. {
  5147. $db->commit();
  5148. $retValue = false;
  5149. return $retValue;
  5150. }
  5151. $versionStatus = $versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'status' );
  5152. $versionList[$versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'version' )] = array( 'node_list' => $nodeList,
  5153. 'status' => $versionStatus );
  5154. unset( $versionStatus );
  5155. $firstVersion = false;
  5156. $lastVersion = $contentObjectVersion->attribute( 'version' );
  5157. if ( $versionDOMNode->getAttribute( 'version' ) == $versionListActiveVersion )
  5158. {
  5159. $activeVersion = $contentObjectVersion->attribute( 'version' );
  5160. }
  5161. eZNodeAssignment::setNewMainAssignment( $contentObject->attribute( 'id' ), $lastVersion );
  5162. eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObject->attribute( 'id' ),
  5163. 'version' => $lastVersion ) );
  5164. $mainNodeInfo = null;
  5165. foreach ( $nodeList as $nodeInfo )
  5166. {
  5167. if ( $nodeInfo['is_main'] )
  5168. {
  5169. $mainNodeInfo =& $nodeInfo;
  5170. break;
  5171. }
  5172. }
  5173. if ( $mainNodeInfo )
  5174. {
  5175. $existingMainNode = eZContentObjectTreeNode::fetchByRemoteID( $mainNodeInfo['parent_remote_id'], false );
  5176. if ( $existingMainNode )
  5177. {
  5178. eZContentObjectTreeNode::updateMainNodeID( $existingMainNode['node_id'],
  5179. $mainNodeInfo['contentobject_id'],
  5180. $mainNodeInfo['contentobject_version'],
  5181. $mainNodeInfo['parent_node'],
  5182. $updateWithParentSection );
  5183. }
  5184. }
  5185. unset( $mainNodeInfo );
  5186. // Refresh $contentObject from DB.
  5187. $contentObject = eZContentObject::fetch( $contentObject->attribute( 'id' ) );
  5188. }
  5189. if ( !$activeVersion )
  5190. {
  5191. $activeVersion = $lastVersion;
  5192. }
  5193. /*
  5194. $contentObject->setAttribute( 'current_version', $activeVersion );
  5195. */
  5196. $contentObject->setAttribute( 'name', $name );
  5197. if ( isset( $options['use_dates_from_package'] ) && $options['use_dates_from_package'] )
  5198. {
  5199. $contentObject->setAttribute( 'published', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'published' ) ) );
  5200. $contentObject->setAttribute( 'modified', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'modified' ) ) );
  5201. }
  5202. $contentObject->store();
  5203. $versions = $contentObject->versions();
  5204. $objectName = $contentObject->name();
  5205. $objectID = $contentObject->attribute( 'id' );
  5206. foreach ( $versions as $version )
  5207. {
  5208. $versionNum = $version->attribute( 'version' );
  5209. $oldVersionStatus = $version->attribute( 'status' );
  5210. $newVersionStatus = isset( $versionList[$versionNum] ) ? $versionList[$versionNum]['status'] : null;
  5211. // set the correct status for non-published versions
  5212. if ( isset( $newVersionStatus ) && $oldVersionStatus != $newVersionStatus && $newVersionStatus != eZContentObjectVersion::STATUS_PUBLISHED )
  5213. {
  5214. $version->setAttribute( 'status', $newVersionStatus );
  5215. $version->store( array( 'status' ) );
  5216. }
  5217. // when translation does not have object name set then we copy object name from the current object version
  5218. $translations = $version->translations( false );
  5219. if ( !$translations )
  5220. continue;
  5221. foreach ( $translations as $translation )
  5222. {
  5223. if ( ! $contentObject->name( $versionNum, $translation ) )
  5224. {
  5225. eZDebug::writeNotice( "Setting name '$objectName' for version ($versionNum) of the content object ($objectID) in language($translation)", __METHOD__ );
  5226. $contentObject->setName( $objectName, $versionNum, $translation );
  5227. }
  5228. }
  5229. }
  5230. foreach ( $versionList[$versionListActiveVersion]['node_list'] as $nodeInfo )
  5231. {
  5232. unset( $parentNode );
  5233. $parentNode = eZContentObjectTreeNode::fetchNode( $contentObject->attribute( 'id' ),
  5234. $nodeInfo['parent_node'] );
  5235. if ( is_object( $parentNode ) )
  5236. {
  5237. $parentNode->setAttribute( 'priority', $nodeInfo['priority'] );
  5238. $parentNode->store( array( 'priority' ) );
  5239. }
  5240. }
  5241. $db->commit();
  5242. return $contentObject;
  5243. }
  5244. /**
  5245. * Performs additional unserialization actions that need to be performed when all
  5246. * objects contained in the package are already installed. (maintain objects' cross-relations)
  5247. *
  5248. * @param mixed $package
  5249. */
  5250. function postUnserialize( $package )
  5251. {
  5252. foreach( $this->versions() as $version )
  5253. {
  5254. $version->postUnserialize( $package );
  5255. }
  5256. }
  5257. /**
  5258. * Returns a DOM structure of the content object and it's attributes.
  5259. *
  5260. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5261. * the calls within a db transaction; thus within db->begin and db->commit.
  5262. *
  5263. * @param mixed $package
  5264. * @param int|bool $specificVersion Content object version, true for current version, false for all, else array containing specific versions.
  5265. * @param array|bool $options Package options or false
  5266. * @param int[]|bool $contentNodeIDArray Array of allowed nodes or false
  5267. * @param int[]|bool $topNodeIDArray Array of top nodes in current package export or false
  5268. * @return bool|DOMElement
  5269. */
  5270. function serialize( $package, $specificVersion = false, $options = false, $contentNodeIDArray = false, $topNodeIDArray = false )
  5271. {
  5272. if ( $options &&
  5273. $options['node_assignment'] == 'main' )
  5274. {
  5275. if ( !isset( $contentNodeIDArray[$this->attribute( 'main_node_id' )] ) )
  5276. {
  5277. return false;
  5278. }
  5279. }
  5280. $dom = new DomDocument();
  5281. $objectNode = $dom->createElementNS( 'http://ez.no/ezobject', 'ezremote:object' );
  5282. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:id', $this->ID );
  5283. $objectNode->setAttribute( 'name', $this->Name );
  5284. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:section_id', $this->SectionID );
  5285. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:owner_id', $this->OwnerID );
  5286. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_id', $this->ClassID );
  5287. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:published', eZDateUtils::rfc1123Date( $this->attribute( 'published' ) ) );
  5288. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:modified', eZDateUtils::rfc1123Date( $this->attribute( 'modified' ) ) );
  5289. if ( !$this->attribute( 'remote_id' ) )
  5290. {
  5291. $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) );
  5292. $this->store();
  5293. }
  5294. $objectNode->setAttribute( 'remote_id', $this->attribute( 'remote_id' ) );
  5295. $contentClass = $this->attribute( 'content_class' );
  5296. $objectNode->setAttribute( 'class_remote_id', $contentClass->attribute( 'remote_id' ) );
  5297. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_identifier', $contentClass->attribute( 'identifier' ) );
  5298. $alwaysAvailableText = '0';
  5299. if ( (int)$this->attribute( 'language_mask' ) & 1 )
  5300. {
  5301. $alwaysAvailableText = '1';
  5302. }
  5303. $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:always_available', $alwaysAvailableText );
  5304. $versions = array();
  5305. $oneLanguagePerVersion = false;
  5306. if ( $specificVersion === false )
  5307. {
  5308. $versions = $this->versions();
  5309. // Since we are exporting all versions it should only contain
  5310. // one language per version
  5311. //$oneLanguagePerVersion = true; // uncomment to get one language per version
  5312. }
  5313. else if ( $specificVersion === true )
  5314. {
  5315. $versions[] = $this->currentVersion();
  5316. }
  5317. else
  5318. {
  5319. $versions[] = $this->version( $specificVersion );
  5320. // Since we are exporting a specific version it should only contain
  5321. // one language per version?
  5322. $oneLanguagePerVersion = true;
  5323. }
  5324. $this->fetchClassAttributes();
  5325. $exportedLanguages = array();
  5326. $versionsNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:version-list' );
  5327. $versionsNode->setAttribute( 'active_version', $this->CurrentVersion );
  5328. foreach ( $versions as $version )
  5329. {
  5330. if ( !$version )
  5331. {
  5332. continue;
  5333. }
  5334. $options['only_initial_language'] = $oneLanguagePerVersion;
  5335. $versionNode = $version->serialize( $package, $options, $contentNodeIDArray, $topNodeIDArray );
  5336. if ( $versionNode )
  5337. {
  5338. $importedVersionNode = $dom->importNode( $versionNode, true );
  5339. $versionsNode->appendChild( $importedVersionNode );
  5340. foreach ( $versionNode->getElementsByTagName( 'object-translation' ) as $versionNodeChild )
  5341. {
  5342. $exportedLanguage = $versionNodeChild->getAttribute( 'language' );
  5343. $exportedLanguages[] = $exportedLanguage;
  5344. $exportedLanguages = array_unique( $exportedLanguages );
  5345. }
  5346. }
  5347. unset( $versionNode );
  5348. unset( $versionNode );
  5349. }
  5350. $initialLanguageCode = $this->attribute( 'initial_language_code' );
  5351. if ( in_array( $initialLanguageCode, $exportedLanguages ) )
  5352. {
  5353. $objectNode->setAttribute( 'initial_language', $initialLanguageCode );
  5354. }
  5355. $objectNode->appendChild( $versionsNode );
  5356. return $objectNode;
  5357. }
  5358. /**
  5359. * Returns a structure with information required for caching.
  5360. *
  5361. * @param $Params array
  5362. * @return array
  5363. */
  5364. function cacheInfo( $Params )
  5365. {
  5366. $contentCacheInfo =& $GLOBALS['eZContentCacheInfo'];
  5367. if ( !isset( $contentCacheInfo ) )
  5368. {
  5369. $user = eZUser::currentUser();
  5370. $language = false;
  5371. if ( isset( $Params['Language'] ) )
  5372. {
  5373. $language = $Params['Language'];
  5374. }
  5375. $roleList = $user->roleIDList();
  5376. $discountList = eZUserDiscountRule::fetchIDListByUserID( $user->attribute( 'contentobject_id' ) );
  5377. $contentCacheInfo = array( 'language' => $language,
  5378. 'role_list' => $roleList,
  5379. 'discount_list' => $discountList );
  5380. }
  5381. return $contentCacheInfo;
  5382. }
  5383. /**
  5384. * Sets all view cache files to be expired
  5385. */
  5386. static function expireAllViewCache()
  5387. {
  5388. $handler = eZExpiryHandler::instance();
  5389. $handler->setTimestamp( 'content-view-cache', time() );
  5390. $handler->store();
  5391. }
  5392. /**
  5393. * Sets all content cache files to be expired. Both view cache and cache blocks are expired.
  5394. *
  5395. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5396. * the calls within a db transaction; thus within db->begin and db->commit.
  5397. *
  5398. * Note: This is considered a internal function, instead use {@see eZContentCacheManager::clearAllContentCache}
  5399. */
  5400. static function expireAllCache()
  5401. {
  5402. $handler = eZExpiryHandler::instance();
  5403. $handler->setTimestamp( 'content-view-cache', time() );
  5404. $handler->setTimestamp( 'template-block-cache', time() );
  5405. $handler->setTimestamp( 'content-tree-menu', time() );
  5406. $handler->store();
  5407. }
  5408. /**
  5409. * Expires all template block cache. This should be expired anytime any content is published/modified or removed.
  5410. *
  5411. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5412. * the calls within a db transaction; thus within db->begin and db->commit.
  5413. */
  5414. static function expireTemplateBlockCache()
  5415. {
  5416. $handler = eZExpiryHandler::instance();
  5417. $handler->setTimestamp( 'template-block-cache', time() );
  5418. $handler->store();
  5419. }
  5420. /**
  5421. * Sets all complex viewmode content cache files to be expired.
  5422. *
  5423. * Transaction unsafe. If you call several transaction unsafe methods you must enclose
  5424. * the calls within a db transaction; thus within db->begin and db->commit.
  5425. */
  5426. static function expireComplexViewModeCache()
  5427. {
  5428. $handler = eZExpiryHandler::instance();
  5429. $handler->setTimestamp( 'content-complex-viewmode-cache', time() );
  5430. $handler->store();
  5431. }
  5432. /**
  5433. * Returns true if the content cache timestamp $timestamp is expired.
  5434. *
  5435. * @param int $timestamp UNIX Timestamp
  5436. * @return bool
  5437. */
  5438. static function isCacheExpired( $timestamp )
  5439. {
  5440. $handler = eZExpiryHandler::instance();
  5441. if ( !$handler->hasTimestamp( 'content-view-cache' ) )
  5442. return false;
  5443. $expiryTime = $handler->timestamp( 'content-view-cache' );
  5444. if ( $expiryTime > $timestamp )
  5445. return true;
  5446. return false;
  5447. }
  5448. /**
  5449. * Returns true if the viewmode is a complex viewmode.
  5450. *
  5451. * @param string $viewMode
  5452. * @return bool
  5453. */
  5454. static function isComplexViewMode( $viewMode )
  5455. {
  5456. $ini = eZINI::instance();
  5457. $viewModes = $ini->variableArray( 'ContentSettings', 'ComplexDisplayViewModes' );
  5458. return in_array( $viewMode, $viewModes );
  5459. }
  5460. /**
  5461. * Returns true if the viewmode is a complex viewmode and the viewmode timestamp is expired.
  5462. *
  5463. * @param string $viewMode
  5464. * @param int $timestamp UNIX Timestamp
  5465. * @return bool
  5466. */
  5467. static function isComplexViewModeCacheExpired( $viewMode, $timestamp )
  5468. {
  5469. if ( !eZContentObject::isComplexViewMode( $viewMode ) )
  5470. return false;
  5471. $handler = eZExpiryHandler::instance();
  5472. if ( !$handler->hasTimestamp( 'content-complex-viewmode-cache' ) )
  5473. return false;
  5474. $expiryTime = $handler->timestamp( 'content-complex-viewmode-cache' );
  5475. if ( $expiryTime > $timestamp )
  5476. return true;
  5477. return false;
  5478. }
  5479. /**
  5480. * Returns a list of all the authors for this object
  5481. *
  5482. * @return eZUser[]
  5483. */
  5484. function authorArray()
  5485. {
  5486. $db = eZDB::instance();
  5487. $userArray = $db->arrayQuery( "SELECT DISTINCT ezuser.contentobject_id, ezuser.login, ezuser.email, ezuser.password_hash, ezuser.password_hash_type
  5488. FROM ezcontentobject_version, ezuser where ezcontentobject_version.contentobject_id='$this->ID'
  5489. AND ezcontentobject_version.creator_id=ezuser.contentobject_id" );
  5490. $return = array();
  5491. foreach ( $userArray as $userRow )
  5492. {
  5493. $return[] = new eZUser( $userRow );
  5494. }
  5495. return $return;
  5496. }
  5497. /**
  5498. * Returns the number of objects of the given class is created by the given user.
  5499. *
  5500. * @param int $classID
  5501. * @param int $userID
  5502. * @param int|bool $status
  5503. * @return int
  5504. */
  5505. static function fetchObjectCountByUserID( $classID, $userID, $status = false )
  5506. {
  5507. $count = 0;
  5508. if ( is_numeric( $classID ) and is_numeric( $userID ) )
  5509. {
  5510. $db = eZDB::instance();
  5511. $classID = (int) $classID;
  5512. $userID = (int) $userID;
  5513. $statusSQL = $status !== false ? ( ' AND status=' . (int) $status ) : '';
  5514. $countArray = $db->arrayQuery( "SELECT count(*) AS count FROM ezcontentobject WHERE contentclass_id=$classID AND owner_id=$userID$statusSQL" );
  5515. $count = $countArray[0]['count'];
  5516. }
  5517. return $count;
  5518. }
  5519. /**
  5520. * @param int|bool $versionStatus
  5521. * @deprecated This method is left here only for backward compatibility. Use eZContentObjectVersion::removeVersions() method instead.
  5522. */
  5523. static function removeVersions( $versionStatus = false )
  5524. {
  5525. eZContentObjectVersion::removeVersions( $versionStatus );
  5526. }
  5527. /**
  5528. * Sets the object's name to $newName: tries to find attributes that are in 'object pattern name' and updates them
  5529. *
  5530. * @param string $newName
  5531. * @return bool true if object's name was changed, otherwise false.
  5532. */
  5533. function rename( $newName )
  5534. {
  5535. // get 'object name pattern'
  5536. $objectNamePattern = '';
  5537. $contentClass = $this->contentClass();
  5538. if ( is_object( $contentClass ) )
  5539. $objectNamePattern = $contentClass->ContentObjectName;
  5540. if ( $objectNamePattern == '' )
  5541. return false;
  5542. // get parts of object's name pattern( like <attr1|attr2>, <attr3> )
  5543. $objectNamePatternPartsPattern = '/<([^>]+)>/U';
  5544. preg_match_all( $objectNamePatternPartsPattern, $objectNamePattern, $objectNamePatternParts );
  5545. if( count( $objectNamePatternParts ) === 0 || count( $objectNamePatternParts[1] ) == 0 )
  5546. return false;
  5547. $objectNamePatternParts = $objectNamePatternParts[1];
  5548. // replace all <...> with (.*)
  5549. $newNamePattern = preg_replace( $objectNamePatternPartsPattern, '(.*)', $objectNamePattern );
  5550. // add terminators
  5551. $newNamePattern = '/' . $newNamePattern . '/';
  5552. // find parts of $newName
  5553. preg_match_all( $newNamePattern, $newName, $newNameParts );
  5554. // looks ok, we can create new version of object
  5555. $contentObjectVersion = $this->createNewVersion();
  5556. // get contentObjectAttributes
  5557. $dataMap = $contentObjectVersion->attribute( 'data_map' );
  5558. if ( count( $dataMap ) === 0 )
  5559. return false;
  5560. // assign parts of $newName to the object's attributes.
  5561. $pos = 0;
  5562. while( $pos < count( $objectNamePatternParts ) )
  5563. {
  5564. $attributes = $objectNamePatternParts[$pos];
  5565. // if we have something like <attr1|attr2> then
  5566. // 'attr1' will be updated only.
  5567. $attributes = explode( '|', $attributes );
  5568. $attribute = $attributes[0];
  5569. $newNamePart = $newNameParts[$pos+1];
  5570. if ( count( $newNamePart ) === 0 )
  5571. {
  5572. if( $pos === 0 )
  5573. {
  5574. // whole $newName goes into the first attribute
  5575. $attributeValue = $newName;
  5576. }
  5577. else
  5578. {
  5579. // all other attibutes will be set to ''
  5580. $attributeValue = '';
  5581. }
  5582. }
  5583. else
  5584. {
  5585. $attributeValue = $newNamePart[0];
  5586. }
  5587. $contentAttribute =& $dataMap[$attribute];
  5588. $dataType = $contentAttribute->dataType();
  5589. if( is_object( $dataType ) && $dataType->isSimpleStringInsertionSupported() )
  5590. {
  5591. $result = '';
  5592. $dataType->insertSimpleString( $this, $contentObjectVersion, false, $contentAttribute, $attributeValue, $result );
  5593. $contentAttribute->store();
  5594. }
  5595. ++$pos;
  5596. }
  5597. $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $this->attribute( 'id' ),
  5598. 'version' => $contentObjectVersion->attribute( 'version') ) );
  5599. return ($operationResult != null ? true : false);
  5600. }
  5601. /**
  5602. * Removes a translation from the current object
  5603. *
  5604. * @param int $languageID
  5605. * @return bool
  5606. */
  5607. function removeTranslation( $languageID )
  5608. {
  5609. $language = eZContentLanguage::fetch( $languageID );
  5610. if ( !$language )
  5611. {
  5612. return false;
  5613. }
  5614. // check permissions for editing
  5615. if ( !$this->checkAccess( 'edit', false, false, false, $languageID ) )
  5616. {
  5617. return false;
  5618. }
  5619. // check if it is not the initial language
  5620. $objectInitialLanguageID = $this->attribute( 'initial_language_id' );
  5621. if ( $objectInitialLanguageID == $languageID )
  5622. {
  5623. return false;
  5624. }
  5625. // change language_mask of the object
  5626. $languageMask = (int) $this->attribute( 'language_mask' );
  5627. $languageMask = (int) $languageMask & ~ (int) $languageID;
  5628. $this->setAttribute( 'language_mask', $languageMask );
  5629. $db = eZDB::instance();
  5630. $db->begin();
  5631. $this->store();
  5632. $objectID = $this->ID;
  5633. // If the current version has initial_language_id $languageID, change it to the initial_language_id of the object.
  5634. $currentVersion = $this->currentVersion();
  5635. if ( $currentVersion->attribute( 'initial_language_id' ) == $languageID )
  5636. {
  5637. $currentVersion->setAttribute( 'initial_language_id', $objectInitialLanguageID );
  5638. $currentVersion->store();
  5639. }
  5640. // Remove all versions which had the language as its initial ID. Because of previous checks, it is sure we will not remove the published version.
  5641. $versionsToRemove = $this->versions( true, array( 'conditions' => array( 'initial_language_id' => $languageID ) ) );
  5642. foreach ( $versionsToRemove as $version )
  5643. {
  5644. $version->removeThis();
  5645. }
  5646. $altLanguageID = $languageID++;
  5647. // Remove all attributes in the language
  5648. $attributes = $db->arrayQuery( "SELECT * FROM ezcontentobject_attribute
  5649. WHERE contentobject_id='$objectID'
  5650. AND ( language_id='$languageID' OR language_id='$altLanguageID' )" );
  5651. foreach ( $attributes as $attribute )
  5652. {
  5653. $attributeObject = new eZContentObjectAttribute( $attribute );
  5654. $attributeObject->remove( $attributeObject->attribute( 'id' ), $attributeObject->attribute( 'version' ) );
  5655. unset( $attributeObject );
  5656. }
  5657. // Remove all names in the language
  5658. $db->query( "DELETE FROM ezcontentobject_name
  5659. WHERE contentobject_id='$objectID'
  5660. AND ( language_id='$languageID' OR language_id='$altLanguageID' )" );
  5661. // Update masks of the objects
  5662. $mask = eZContentLanguage::maskForRealLanguages() - (int) $languageID;
  5663. if ( $db->databaseName() == 'oracle' )
  5664. {
  5665. $db->query( "UPDATE ezcontentobject_version SET language_mask = bitand( language_mask, $mask )
  5666. WHERE contentobject_id='$objectID'" );
  5667. }
  5668. else
  5669. {
  5670. $db->query( "UPDATE ezcontentobject_version SET language_mask = language_mask & $mask
  5671. WHERE contentobject_id='$objectID'" );
  5672. }
  5673. $urlElementfilter = new eZURLAliasQuery();
  5674. $urlElementfilter->type = 'name';
  5675. // We want all languages present here, so we are turning off
  5676. // language filtering
  5677. $urlElementfilter->languages = false;
  5678. $urlElementfilter->limit = false;
  5679. $nodes = $this->assignedNodes();
  5680. foreach ( $nodes as $node )
  5681. {
  5682. $parent = null;
  5683. $textMD5 = null;
  5684. $urlElementfilter->actions = array( 'eznode:' . $node->attribute( 'node_id' ) );
  5685. $urlElementfilter->prepare();
  5686. $urlElements = $urlElementfilter->fetchAll();
  5687. foreach ($urlElements as $url )
  5688. {
  5689. if ( $url->attribute( 'lang_mask' ) === (int)$languageID or
  5690. $url->attribute( 'lang_mask') === (int)$altLanguageID )
  5691. {
  5692. $parent = $url->attribute( 'parent');
  5693. $textMD5 = $url->attribute( 'text_md5' );
  5694. break;
  5695. }
  5696. }
  5697. if ( $parent !== null and $textMD5 !== null )
  5698. eZURLAliasML::removeSingleEntry( $parent, $textMD5, $language );
  5699. }
  5700. $db->commit();
  5701. return true;
  5702. }
  5703. /**
  5704. * @return int
  5705. */
  5706. function isAlwaysAvailable()
  5707. {
  5708. return ( $this->attribute( 'language_mask' ) & 1 );
  5709. }
  5710. /**
  5711. * @param int $languageID
  5712. * @param int|bool $version
  5713. */
  5714. function setAlwaysAvailableLanguageID( $languageID, $version = false )
  5715. {
  5716. $db = eZDB::instance();
  5717. $db->begin();
  5718. if ( $version == false )
  5719. {
  5720. $version = $this->currentVersion();
  5721. if ( $languageID )
  5722. {
  5723. $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) | 1 );
  5724. }
  5725. else
  5726. {
  5727. $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) & ~1 );
  5728. }
  5729. $this->store();
  5730. }
  5731. $objectID = $this->attribute( 'id' );
  5732. $versionID = $version->attribute( 'version' );
  5733. // reset 'always available' flag
  5734. $sql = "UPDATE ezcontentobject_name SET language_id=";
  5735. if ( $db->databaseName() == 'oracle' )
  5736. {
  5737. $sql .= "bitand( language_id, -2 )";
  5738. }
  5739. else
  5740. {
  5741. $sql .= "language_id & ~1";
  5742. }
  5743. $sql .= " WHERE contentobject_id = '$objectID' AND content_version = '$versionID'";
  5744. $db->query( $sql );
  5745. if ( $languageID != false )
  5746. {
  5747. $newLanguageID = $languageID | 1;
  5748. $sql = "UPDATE ezcontentobject_name
  5749. SET language_id='$newLanguageID'
  5750. WHERE language_id='$languageID' AND contentobject_id = '$objectID' AND content_version = '$versionID'";
  5751. $db->query( $sql );
  5752. }
  5753. $version->setAlwaysAvailableLanguageID( $languageID );
  5754. // Update url alias for all locations
  5755. $nodeRows = eZContentObjectTreeNode::fetchByContentObjectID( $objectID, false );
  5756. $actions = array();
  5757. foreach ( $nodeRows as $nodeRow )
  5758. {
  5759. $nodeID = (int)$nodeRow['node_id'];
  5760. $actions[] = array( 'eznode', $nodeID );
  5761. }
  5762. eZURLAliasML::setLangMaskAlwaysAvailable( $languageID, $actions, null );
  5763. $db->commit();
  5764. }
  5765. /**
  5766. * @return eZSection[]|null
  5767. */
  5768. function allowedAssignSectionList()
  5769. {
  5770. $currentUser = eZUser::currentUser();
  5771. $sectionIDList = $currentUser->canAssignToObjectSectionList( $this );
  5772. $sectionList = array();
  5773. if ( in_array( '*', $sectionIDList ) )
  5774. {
  5775. $sectionList = eZSection::fetchList( false );
  5776. }
  5777. else
  5778. {
  5779. $sectionIDList[] = $this->attribute( 'section_id' );
  5780. $sectionList = eZSection::fetchFilteredList( array( 'id' => array( $sectionIDList ) ), false, false, false );
  5781. }
  5782. return $sectionList;
  5783. }
  5784. /**
  5785. * Gets a list of states a user is allowed to put the content object in.
  5786. *
  5787. * @return array the IDs of all states we are allowed to set
  5788. * @param eZUser $user the user to check the policies of, when omitted the currently logged in user will be used
  5789. */
  5790. function allowedAssignStateIDList( eZUser $user = null )
  5791. {
  5792. if ( !$user instanceof eZUser )
  5793. {
  5794. $user = eZUser::currentUser();
  5795. }
  5796. $access = $user->hasAccessTo( 'state', 'assign' );
  5797. $db = eZDB::instance();
  5798. $sql = 'SELECT ezcobj_state.id
  5799. FROM ezcobj_state, ezcobj_state_group
  5800. WHERE ezcobj_state.group_id = ezcobj_state_group.id
  5801. AND ezcobj_state_group.identifier NOT LIKE \'ez%\'';
  5802. if ( $access['accessWord'] == 'yes' )
  5803. {
  5804. $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) );
  5805. }
  5806. else if ( $access['accessWord'] == 'limited' )
  5807. {
  5808. $userID = $user->attribute( 'contentobject_id' );
  5809. $classID = $this->attribute( 'contentclass_id' );
  5810. $ownerID = $this->attribute( 'owner_id' );
  5811. $sectionID = $this->attribute( 'section_id' );
  5812. $stateIDArray = $this->attribute( 'state_id_array' );
  5813. $allowedStateIDList = array();
  5814. foreach ( $access['policies'] as $policy )
  5815. {
  5816. foreach ( $policy as $ident => $values )
  5817. {
  5818. $allowed = true;
  5819. switch ( $ident )
  5820. {
  5821. case 'Class':
  5822. {
  5823. $allowed = in_array( $classID, $values );
  5824. } break;
  5825. case 'Owner':
  5826. {
  5827. $allowed = in_array( 1, $values ) and $userID != $ownerID;
  5828. } break;
  5829. case 'Group':
  5830. {
  5831. $allowed = $this->checkGroupLimitationAccess( $values, $userID ) === 'allowed';
  5832. } break;
  5833. case 'Section':
  5834. case 'User_Section':
  5835. {
  5836. $allowed = in_array( $sectionID, $values );
  5837. } break;
  5838. //This case is based on the similar if statement in the method : classListFromPolicy
  5839. case 'User_Subtree':
  5840. {
  5841. $allowed = false;
  5842. foreach ( $this->attribute( 'assigned_nodes' ) as $assignedNode )
  5843. {
  5844. $path = $assignedNode->attribute( 'path_string' );
  5845. foreach ( $policy['User_Subtree'] as $subtreeString )
  5846. {
  5847. if ( strpos( $path, $subtreeString ) !== false )
  5848. {
  5849. $allowed = true;
  5850. break;
  5851. }
  5852. }
  5853. }
  5854. } break;
  5855. default:
  5856. {
  5857. if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 )
  5858. {
  5859. $allowed = count( array_intersect( $values, $stateIDArray ) ) > 0;
  5860. }
  5861. }
  5862. }
  5863. if ( !$allowed )
  5864. {
  5865. continue 2;
  5866. }
  5867. }
  5868. if ( isset( $policy['NewState'] ) and count( $policy['NewState'] ) > 0 )
  5869. {
  5870. $allowedStateIDList = array_merge( $allowedStateIDList, $policy['NewState'] );
  5871. }
  5872. else
  5873. {
  5874. $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) );
  5875. break;
  5876. }
  5877. }
  5878. $allowedStateIDList = array_merge( $allowedStateIDList, $stateIDArray );
  5879. }
  5880. else
  5881. {
  5882. $stateIDArray = $this->attribute( 'state_id_array' );
  5883. $allowedStateIDList = $stateIDArray;
  5884. }
  5885. $allowedStateIDList = array_unique( $allowedStateIDList );
  5886. return $allowedStateIDList;
  5887. }
  5888. /**
  5889. * @param eZUser|null $user
  5890. * @return array
  5891. */
  5892. function allowedAssignStateList( eZUser $user = null )
  5893. {
  5894. $allowedStateIDList = $this->allowedAssignStateIDList( $user );
  5895. // retrieve state groups, and for each state group the allowed states (including the current state)
  5896. $groups = eZContentObjectStateGroup::fetchByOffset( false, false );
  5897. $allowedAssignList = array();
  5898. foreach ( $groups as $group )
  5899. {
  5900. // we do not return any internal state
  5901. // all internal states are prepended with the string : "ez_"
  5902. if( strpos( $group->attribute( 'identifier' ), 'ez' ) === 0 )
  5903. continue;
  5904. $states = array();
  5905. $groupStates = $group->attribute( 'states' );
  5906. $currentStateIDArray = $this->attribute( 'state_id_array' );
  5907. $current = false;
  5908. foreach ( $groupStates as $groupState )
  5909. {
  5910. $stateID = $groupState->attribute( 'id' );
  5911. if ( in_array( $stateID, $allowedStateIDList ) )
  5912. {
  5913. $states[] = $groupState;
  5914. }
  5915. if ( in_array( $stateID, $currentStateIDArray ) )
  5916. {
  5917. $current = $groupState;
  5918. }
  5919. }
  5920. $allowedAssignList[] = array( 'group' => $group, 'states' => $states, 'current' => $current );
  5921. }
  5922. return $allowedAssignList;
  5923. }
  5924. /**
  5925. * Gets the current states of the content object.
  5926. *
  5927. * Uses a member variable that caches the result.
  5928. *
  5929. * @param bool $refreshCache if the cache in the member variable needs to be refreshed
  5930. * @return array an associative array with state group id => state id pairs
  5931. */
  5932. function stateIDArray( $refreshCache = false )
  5933. {
  5934. if ( !$this->ID )
  5935. return array();
  5936. if ( $refreshCache || !is_array( $this->StateIDArray ) )
  5937. {
  5938. $this->StateIDArray = array();
  5939. $contentObjectID = $this->ID;
  5940. eZDebug::accumulatorStart( 'state_id_array', 'states' );
  5941. $sql = "SELECT contentobject_state_id, group_id FROM ezcobj_state_link, ezcobj_state
  5942. WHERE ezcobj_state.id=ezcobj_state_link.contentobject_state_id AND
  5943. contentobject_id=$contentObjectID";
  5944. $db = eZDB::instance();
  5945. $rows = $db->arrayQuery( $sql );
  5946. foreach ( $rows as $row )
  5947. {
  5948. $this->StateIDArray[$row['group_id']] = $row['contentobject_state_id'];
  5949. }
  5950. eZDebug::accumulatorStop( 'state_id_array' );
  5951. }
  5952. return $this->StateIDArray;
  5953. }
  5954. /**
  5955. * @return array
  5956. */
  5957. function stateIdentifierArray()
  5958. {
  5959. if ( !$this->ID )
  5960. return array();
  5961. eZDebug::accumulatorStart( 'state_identifier_array', 'states' );
  5962. $return = array();
  5963. $sql = "SELECT l.contentobject_state_id, s.identifier AS state_identifier, g.identifier AS state_group_identifier
  5964. FROM ezcobj_state_link l, ezcobj_state s, ezcobj_state_group g
  5965. WHERE l.contentobject_id={$this->ID} AND
  5966. s.id=l.contentobject_state_id AND
  5967. g.id=s.group_id";
  5968. $db = eZDB::instance();
  5969. $rows = $db->arrayQuery( $sql );
  5970. foreach ( $rows as $row )
  5971. {
  5972. $return[] = $row['state_group_identifier'] . '/' . $row['state_identifier'];
  5973. }
  5974. eZDebug::accumulatorStop( 'state_identifier_array' );
  5975. return $return;
  5976. }
  5977. /**
  5978. * Sets the state of a content object.
  5979. *
  5980. * Changes are stored immediately in the database, does not require a store() of the content object.
  5981. * Should only be called on instances of eZContentObject that have a ID (that were stored already before).
  5982. *
  5983. * @param eZContentObjectState $state
  5984. * @return boolean true when the state was set, false if the state equals the current state
  5985. */
  5986. function assignState( eZContentObjectState $state )
  5987. {
  5988. $groupID = $state->attribute( 'group_id' );
  5989. $stateID = $state->attribute( 'id' );
  5990. $contentObjectID = $this->ID;
  5991. $currentStateIDArray = $this->stateIDArray( true );
  5992. $currentStateID = $currentStateIDArray[$groupID];
  5993. if ( $currentStateID == $stateID )
  5994. {
  5995. return false;
  5996. }
  5997. $sql = "UPDATE ezcobj_state_link
  5998. SET contentobject_state_id=$stateID
  5999. WHERE contentobject_state_id=$currentStateID AND
  6000. contentobject_id=$contentObjectID";
  6001. eZDB::instance()->query( $sql );
  6002. $this->StateIDArray[$groupID] = $stateID;
  6003. return true;
  6004. }
  6005. /**
  6006. * Sets the default states of a content object.
  6007. *
  6008. * This function is called upon instantiating a content object with {@link eZContentClass::instantiate()}, so
  6009. * should normally not be called by any other code.
  6010. */
  6011. function assignDefaultStates()
  6012. {
  6013. $db = eZDB::instance();
  6014. $db->begin();
  6015. $defaultStates = eZContentObjectState::defaults();
  6016. $contentObjectID = $this->ID;
  6017. foreach ( $defaultStates as $state )
  6018. {
  6019. $stateID = $state->attribute( 'id' );
  6020. $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id)
  6021. VALUES($stateID, $contentObjectID)" );
  6022. }
  6023. $db->commit();
  6024. }
  6025. /**
  6026. * Restores attributes for current content object when it's being restored from trash
  6027. */
  6028. public function restoreObjectAttributes()
  6029. {
  6030. $db = eZDB::instance();
  6031. $db->begin();
  6032. foreach ( $this->allContentObjectAttributes( $this->attribute( "id" ) ) as $contentObjectAttribute )
  6033. {
  6034. $datatype = $contentObjectAttribute->dataType();
  6035. if ( !$datatype instanceof eZDataType )
  6036. {
  6037. eZDebug::writeError( "Attribute #{$contentObjectAttribute->attribute( "id" )} from contentobject #{$this->attribute( "id" )} isn't valid", __METHOD__ );
  6038. continue;
  6039. }
  6040. $datatype->restoreTrashedObjectAttribute( $contentObjectAttribute );
  6041. }
  6042. $db->commit();
  6043. }
  6044. /**
  6045. * Returns object's section identifier
  6046. *
  6047. * @return string|bool
  6048. */
  6049. public function sectionIdentifier()
  6050. {
  6051. $section = eZSection::fetch( $this->attribute( 'section_id' ) );
  6052. if( $section instanceof eZSection )
  6053. {
  6054. return $section->attribute( 'identifier' );
  6055. }
  6056. return false;
  6057. }
  6058. /**
  6059. * @var int Object ID
  6060. */
  6061. public $ID;
  6062. /**
  6063. * @var string Object name
  6064. */
  6065. public $Name;
  6066. /**
  6067. * @var bool|string Stores the current language
  6068. */
  6069. public $CurrentLanguage;
  6070. /**
  6071. * @var bool|string Stores the current class name
  6072. */
  6073. public $ClassName;
  6074. /**
  6075. * @var string The object's class identifier
  6076. */
  6077. public $ClassIdentifier;
  6078. /**
  6079. * @var eZContentObjectAttribute[] The datamap for content object attributes
  6080. */
  6081. public $DataMap = array();
  6082. /**
  6083. * @var array|bool Contains an array of the content object actions for the current object
  6084. */
  6085. public $ContentActionList = false;
  6086. /**
  6087. * @var eZContentObjectAttribute[] Contains a cached version of the content object attributes for the given version and language
  6088. */
  6089. public $ContentObjectAttributes = array();
  6090. /**
  6091. * @var int|bool Contains the main node id for this object
  6092. */
  6093. public $MainNodeID = false;
  6094. /**
  6095. * @var array Contains the arrays of relatedobject id by fetching input for this object
  6096. */
  6097. public $InputRelationList = array();
  6098. /**
  6099. * Cache for the state ID array
  6100. *
  6101. * @var array|bool
  6102. * @see stateIDArray()
  6103. */
  6104. private $StateIDArray = false;
  6105. }
  6106. ?>