PageRenderTime 67ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 2ms

/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

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

  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ā€¦

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