PageRenderTime 84ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/kernel/classes/ezcontentobject.php

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