PageRenderTime 57ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/kernel/classes/ezcontentclass.php

https://github.com/granitegreg/ezpublish
PHP | 2017 lines | 1429 code | 221 blank | 367 comment | 175 complexity | f66e314d69358035ce6717cc0c434652 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. //
  3. // Definition of eZContentClass class
  4. //
  5. // Created on: <16-Apr-2002 11:08:14 amos>
  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 eZContentClass ezcontentclass.php
  32. \ingroup eZKernel
  33. \brief Handles eZ Publish content classes
  34. \sa eZContentObject
  35. */
  36. class eZContentClass extends eZPersistentObject
  37. {
  38. const VERSION_STATUS_DEFINED = 0;
  39. const VERSION_STATUS_TEMPORARY = 1;
  40. const VERSION_STATUS_MODIFIED = 2;
  41. /**
  42. * Max length of content object name.
  43. * @var int
  44. */
  45. const CONTENT_OBJECT_NAME_MAX_LENGTH = 255;
  46. function eZContentClass( $row )
  47. {
  48. if ( is_array( $row ) )
  49. {
  50. $this->eZPersistentObject( $row );
  51. $this->VersionCount = false;
  52. $this->InGroups = null;
  53. $this->AllGroups = null;
  54. if ( isset( $row["version_count"] ) )
  55. $this->VersionCount = $row["version_count"];
  56. $this->NameList = new eZContentClassNameList();
  57. if ( isset( $row['serialized_name_list'] ) )
  58. $this->NameList->initFromSerializedList( $row['serialized_name_list'] );
  59. else
  60. $this->NameList->initDefault();
  61. $this->DescriptionList = new eZSerializedObjectNameList();
  62. if ( isset( $row['serialized_description_list'] ) )
  63. $this->DescriptionList->initFromSerializedList( $row['serialized_description_list'] );
  64. else
  65. $this->DescriptionList->initDefault();
  66. }
  67. $this->DataMap = false;
  68. }
  69. static function definition()
  70. {
  71. static $definition = array( "fields" => array( "id" => array( 'name' => 'ID',
  72. 'datatype' => 'integer',
  73. 'default' => 0,
  74. 'required' => true ),
  75. "version" => array( 'name' => 'Version',
  76. 'datatype' => 'integer',
  77. 'default' => 0,
  78. 'required' => true ),
  79. "serialized_name_list" => array( 'name' => 'SerializedNameList',
  80. 'datatype' => 'string',
  81. 'default' => '',
  82. 'required' => true ),
  83. 'serialized_description_list' => array( 'name' => 'SerializedDescriptionList',
  84. 'datatype' => 'string',
  85. 'default' => '',
  86. 'required' => true ),
  87. "identifier" => array( 'name' => "Identifier",
  88. 'datatype' => 'string',
  89. 'default' => '',
  90. 'required' => true ),
  91. "contentobject_name" => array( 'name' => "ContentObjectName",
  92. 'datatype' => 'string',
  93. 'default' => '',
  94. 'required' => true ),
  95. "url_alias_name" => array( 'name' => "URLAliasName",
  96. 'datatype' => 'string',
  97. 'default' => '',
  98. 'required' => false ),
  99. "creator_id" => array( 'name' => "CreatorID",
  100. 'datatype' => 'integer',
  101. 'default' => 0,
  102. 'required' => true,
  103. 'foreign_class' => 'eZUser',
  104. 'foreign_attribute' => 'contentobject_id',
  105. 'multiplicity' => '1..*' ),
  106. "modifier_id" => array( 'name' => "ModifierID",
  107. 'datatype' => 'integer',
  108. 'default' => 0,
  109. 'required' => true,
  110. 'foreign_class' => 'eZUser',
  111. 'foreign_attribute' => 'contentobject_id',
  112. 'multiplicity' => '1..*' ),
  113. "created" => array( 'name' => "Created",
  114. 'datatype' => 'integer',
  115. 'default' => 0,
  116. 'required' => true ),
  117. "remote_id" => array( 'name' => "RemoteID",
  118. 'datatype' => 'string',
  119. 'default' => '',
  120. 'required' => true ),
  121. "modified" => array( 'name' => "Modified",
  122. 'datatype' => 'integer',
  123. 'default' => 0,
  124. 'required' => true ),
  125. "is_container" => array( 'name' => "IsContainer",
  126. 'datatype' => 'integer',
  127. 'default' => 0,
  128. 'required' => true ),
  129. 'always_available' => array( 'name' => "AlwaysAvailable",
  130. 'datatype' => 'integer',
  131. 'default' => 0,
  132. 'required' => true ),
  133. 'language_mask' => array( 'name' => "LanguageMask",
  134. 'datatype' => 'integer',
  135. 'default' => 0,
  136. 'required' => true ),
  137. 'initial_language_id' => array( 'name' => "InitialLanguageID",
  138. 'datatype' => 'integer',
  139. 'default' => 0,
  140. 'required' => true,
  141. 'foreign_class' => 'eZContentLanguage',
  142. 'foreign_attribute' => 'id',
  143. 'multiplicity' => '1..*' ),
  144. 'sort_field' => array( 'name' => 'SortField',
  145. 'datatype' => 'integer',
  146. 'default' => 1,
  147. 'required' => true ),
  148. 'sort_order' => array( 'name' => 'SortOrder',
  149. 'datatype' => 'integer',
  150. 'default' => 1,
  151. 'required' => true ) ),
  152. "keys" => array( "id", "version" ),
  153. "function_attributes" => array( "data_map" => "dataMap",
  154. 'object_count' => 'objectCount',
  155. 'object_list' => 'objectList',
  156. 'version_count' => 'versionCount',
  157. 'version_status' => 'versionStatus',
  158. 'remote_id' => 'remoteID', // Note: This overrides remote_id field
  159. 'ingroup_list' => 'fetchGroupList',
  160. 'ingroup_id_list' => 'fetchGroupIDList',
  161. 'match_ingroup_id_list' => 'fetchMatchGroupIDList',
  162. 'group_list' => 'fetchAllGroups',
  163. 'creator' => 'creator',
  164. 'modifier' => 'modifier',
  165. 'can_instantiate_languages' => 'canInstantiateLanguages',
  166. 'name' => 'name',
  167. 'nameList' => 'nameList',
  168. 'description' => 'description',
  169. 'descriptionList' => 'descriptionList',
  170. 'languages' => 'languages',
  171. 'prioritized_languages' => 'prioritizedLanguages',
  172. 'prioritized_languages_js_array' => 'prioritizedLanguagesJsArray',
  173. 'can_create_languages' => 'canCreateLanguages',
  174. 'top_priority_language_locale' => 'topPriorityLanguageLocale',
  175. 'always_available_language' => 'alwaysAvailableLanguage' ),
  176. 'set_functions' => array( 'name' => 'setName' ),
  177. "increment_key" => "id",
  178. "class_name" => "eZContentClass",
  179. "sort" => array( "id" => "asc" ),
  180. "name" => "ezcontentclass" );
  181. return $definition;
  182. }
  183. function __clone()
  184. {
  185. unset( $this->Version );
  186. unset( $this->InGroups );
  187. unset( $this->AllGroups );
  188. unset( $this->CanInstantiateLanguages );
  189. unset( $this->VersionCount );
  190. $this->ID = null;
  191. $this->RemoteID = eZRemoteIdUtility::generate( 'class' );
  192. }
  193. /*!
  194. Creates an 'eZContentClass' object.
  195. To specify contentclass name use either $optionalValues['serialized_name_list'] or
  196. combination of $optionalValues['name'] and/or $languageLocale.
  197. In case of conflict(when both 'serialized_name_list' and 'name' with/without $languageLocale
  198. are specified) 'serialized_name_list' has top priority. This means that 'name' and
  199. $languageLocale will be ingnored because 'serialized_name_list' already has all needed info
  200. about names and languages.
  201. If 'name' is specified then the contentclass will have a name in $languageLocale(if specified) or
  202. in default language.
  203. If neither of 'serialized_name_list' or 'name' isn't specified then the contentclass will have an empty
  204. name in 'languageLocale'(if specified) or in default language.
  205. 'language_mask' and 'initial_language_id' attributes will be set according to specified(either
  206. in 'serialized_name_list' or by $languageLocale) languages.
  207. \return 'eZContentClass' object.
  208. */
  209. static function create( $userID = false, $optionalValues = array(), $languageLocale = false )
  210. {
  211. $dateTime = time();
  212. if ( !$userID )
  213. $userID = eZUser::currentUserID();
  214. $nameList = new eZContentClassNameList();
  215. if ( isset( $optionalValues['serialized_name_list'] ) )
  216. $nameList->initFromSerializedList( $optionalValues['serialized_name_list'] );
  217. else if ( isset( $optionalValues['name'] ) )
  218. $nameList->initFromString( $optionalValues['name'], $languageLocale );
  219. else
  220. $nameList->initFromString( '', $languageLocale );
  221. $descriptionList = new eZSerializedObjectNameList();
  222. if ( isset( $optionalValues['serialized_description_list'] ) )
  223. $descriptionList->initFromSerializedList( $optionalValues['serialized_description_list'] );
  224. else if ( isset( $optionalValues['description'] ) )
  225. $descriptionList->initFromString( $optionalValues['description'], $languageLocale );
  226. else
  227. $descriptionList->initFromString( '', $languageLocale );
  228. $languageMask = $nameList->languageMask();
  229. $initialLanguageID = $nameList->alwaysAvailableLanguageID();
  230. $contentClassDefinition = eZContentClass::definition();
  231. $row = array(
  232. "id" => null,
  233. "version" => 1,
  234. "serialized_name_list" => $nameList->serializeNames(),
  235. 'serialized_description_list' => $descriptionList->serializeNames(),
  236. "identifier" => "",
  237. "contentobject_name" => "",
  238. "creator_id" => $userID,
  239. "modifier_id" => $userID,
  240. "created" => $dateTime,
  241. 'remote_id' => eZRemoteIdUtility::generate( 'class' ),
  242. "modified" => $dateTime,
  243. "is_container" => $contentClassDefinition[ 'fields' ][ 'is_container' ][ 'default' ],
  244. "always_available" => $contentClassDefinition[ 'fields' ][ 'always_available' ][ 'default' ],
  245. 'language_mask' => $languageMask,
  246. 'initial_language_id' => $initialLanguageID,
  247. "sort_field" => $contentClassDefinition[ 'fields' ][ 'sort_field' ][ 'default' ],
  248. "sort_order" => $contentClassDefinition[ 'fields' ][ 'sort_order' ][ 'default' ] );
  249. $row = array_merge( $row, $optionalValues );
  250. $contentClass = new eZContentClass( $row );
  251. // setting 'dirtyData' to make sure the 'NameList' will be stored into db.
  252. $contentClass->NameList->setHasDirtyData( true );
  253. return $contentClass;
  254. }
  255. function instantiateIn( $lang, $userID = false, $sectionID = 1, $versionNumber = false, $versionStatus = eZContentObjectVersion::STATUS_INTERNAL_DRAFT )
  256. {
  257. return eZContentClass::instantiate( $userID, $sectionID, $versionNumber, $lang, $versionStatus );
  258. }
  259. /*!
  260. Creates a new content object instance and stores it.
  261. \param userID user ID (optional), current user if not set (also store object id in session if $userID = false)
  262. \param sectionID section ID (optional), 1 if not set (Standard section)
  263. \param versionNumber version number, create initial version if not set
  264. \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
  265. the calls within a db transaction; thus within db->begin and db->commit.
  266. */
  267. function instantiate( $userID = false, $sectionID = 1, $versionNumber = false, $languageCode = false, $versionStatus = eZContentObjectVersion::STATUS_INTERNAL_DRAFT )
  268. {
  269. $attributes = $this->fetchAttributes();
  270. if ( $userID === false )
  271. {
  272. $user = eZUser::currentUser();
  273. $userID = $user->attribute( 'contentobject_id' );
  274. }
  275. if ( $languageCode == false )
  276. {
  277. $languageCode = eZContentObject::defaultLanguage();
  278. }
  279. $object = eZContentObject::create( ezpI18n::tr( "kernel/contentclass", "New %1", null, array( $this->name( $languageCode ) ) ),
  280. $this->attribute( "id" ),
  281. $userID,
  282. $sectionID,
  283. 1,
  284. $languageCode );
  285. if ( $this->attribute( 'always_available' ) )
  286. {
  287. $object->setAttribute( 'language_mask', (int)$object->attribute( 'language_mask') | 1 );
  288. }
  289. $db = eZDB::instance();
  290. $db->begin();
  291. $object->store();
  292. $object->assignDefaultStates();
  293. $object->setName( ezpI18n::tr( "kernel/contentclass", "New %1", null, array( $this->name( $languageCode ) ) ), false, $languageCode );
  294. if ( !$versionNumber )
  295. {
  296. $version = $object->createInitialVersion( $userID, $languageCode );
  297. }
  298. else
  299. {
  300. $version = eZContentObjectVersion::create( $object->attribute( "id" ), $userID, $versionNumber, $languageCode );
  301. }
  302. if ( $versionStatus !== false )
  303. {
  304. $version->setAttribute( 'status', $versionStatus );
  305. }
  306. $version->store();
  307. foreach ( $attributes as $attribute )
  308. {
  309. $attribute->instantiate( $object->attribute( 'id' ), $languageCode );
  310. }
  311. if ( isset( $user ) && $user instanceof eZUser && $user->isAnonymous() )
  312. {
  313. $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
  314. if ( !$createdObjectIDList )
  315. {
  316. $createdObjectIDList = array( $object->attribute( 'id' ) );
  317. }
  318. else
  319. {
  320. $createdObjectIDList = unserialize( $createdObjectIDList );
  321. $createdObjectIDList[] = $object->attribute( 'id' );
  322. }
  323. eZPreferences::setValue( 'ObjectCreationIDList', serialize( $createdObjectIDList ) );
  324. }
  325. $db->commit();
  326. return $object;
  327. }
  328. function canInstantiateClasses()
  329. {
  330. $accessResult = eZUser::currentUser()->hasAccessTo( 'content' , 'create' );
  331. $canInstantiateClasses = 1;
  332. if ( $accessResult['accessWord'] == 'no' )
  333. {
  334. $canInstantiateClasses = 0;
  335. }
  336. return $canInstantiateClasses;
  337. }
  338. // code-template::create-block: can-instantiate-class-list, group-filter, role-caching, class-policy-list, name-instantiate, object-creation, class-sql-creation, static-method
  339. // code-template::auto-generated:START can-instantiate-class-list
  340. // This code is automatically generated from templates/classcreatelist.ctpl
  341. // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
  342. /*!
  343. \static
  344. Finds all classes that the current user can create objects from and returns.
  345. It is also possible to filter the list event more with \a $includeFilter and \a $groupList.
  346. \param $asObject If \c true then it return eZContentClass objects, if not it will
  347. be an associative array with \c name and \c id keys.
  348. \param $includeFilter If \c true then it will include only from class groups defined in
  349. \a $groupList, if not it will exclude those groups.
  350. \param $groupList An array with class group IDs that should be used in filtering, use
  351. \c false if you do not wish to filter at all.
  352. \param $fetchID A unique name for the current fetch, this must be supplied when filtering is
  353. used if you want caching to work.
  354. */
  355. static function canInstantiateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false )
  356. {
  357. $ini = eZINI::instance();
  358. $groupArray = array();
  359. $enableCaching = ( $ini->variable( 'RoleSettings', 'EnableCaching' ) == 'true' );
  360. if ( is_array( $groupList ) )
  361. {
  362. if ( $fetchID == false )
  363. $enableCaching = false;
  364. }
  365. if ( $enableCaching )
  366. {
  367. $http = eZHTTPTool::instance();
  368. eZExpiryHandler::registerShutdownFunction();
  369. $handler = eZExpiryHandler::instance();
  370. $expiredTimeStamp = 0;
  371. if ( $handler->hasTimestamp( 'user-class-cache' ) )
  372. $expiredTimeStamp = $handler->timestamp( 'user-class-cache' );
  373. $classesCachedForUser = $http->sessionVariable( 'ClassesCachedForUser' );
  374. $classesCachedTimestamp = $http->sessionVariable( 'ClassesCachedTimestamp' );
  375. $cacheVar = 'CanInstantiateClassList';
  376. if ( is_array( $groupList ) and $fetchID !== false )
  377. {
  378. $cacheVar = 'CanInstantiateClassListGroup';
  379. }
  380. $user = eZUser::currentUser();
  381. $userID = $user->id();
  382. if ( ( $classesCachedTimestamp >= $expiredTimeStamp ) && $classesCachedForUser == $userID )
  383. {
  384. if ( $http->hasSessionVariable( $cacheVar ) )
  385. {
  386. if ( $fetchID !== false )
  387. {
  388. // Check if the group contains our ID, if not we need to fetch from DB
  389. $groupArray = $http->sessionVariable( $cacheVar );
  390. if ( isset( $groupArray[$fetchID] ) )
  391. {
  392. return $groupArray[$fetchID];
  393. }
  394. }
  395. else
  396. {
  397. return $http->sessionVariable( $cacheVar );
  398. }
  399. }
  400. }
  401. else
  402. {
  403. $http->setSessionVariable( 'ClassesCachedForUser' , $userID );
  404. $http->setSessionVariable( 'ClassesCachedTimestamp', time() );
  405. }
  406. }
  407. $languageCodeList = eZContentLanguage::fetchLocaleList();
  408. $allowedLanguages = array( '*' => array() );
  409. $user = eZUser::currentUser();
  410. $accessResult = $user->hasAccessTo( 'content' , 'create' );
  411. $accessWord = $accessResult['accessWord'];
  412. $classIDArray = array();
  413. $classList = array();
  414. $fetchAll = false;
  415. if ( $accessWord == 'yes' )
  416. {
  417. $fetchAll = true;
  418. $allowedLanguages['*'] = $languageCodeList;
  419. }
  420. else if ( $accessWord == 'no' )
  421. {
  422. // Cannot create any objects, return empty list.
  423. return $classList;
  424. }
  425. else
  426. {
  427. $policies = $accessResult['policies'];
  428. foreach ( $policies as $policyKey => $policy )
  429. {
  430. $classIDArrayPart = '*';
  431. if ( isset( $policy['Class'] ) )
  432. {
  433. $classIDArrayPart = $policy['Class'];
  434. }
  435. $languageCodeArrayPart = $languageCodeList;
  436. if ( isset( $policy['Language'] ) )
  437. {
  438. $languageCodeArrayPart = array_intersect( $policy['Language'], $languageCodeList );
  439. }
  440. if ( $classIDArrayPart == '*' )
  441. {
  442. $fetchAll = true;
  443. $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) );
  444. }
  445. else
  446. {
  447. foreach( $classIDArrayPart as $class )
  448. {
  449. if ( isset( $allowedLanguages[$class] ) )
  450. {
  451. $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) );
  452. }
  453. else
  454. {
  455. $allowedLanguages[$class] = $languageCodeArrayPart;
  456. }
  457. }
  458. $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) );
  459. }
  460. }
  461. }
  462. $db = eZDB::instance();
  463. $filterTableSQL = '';
  464. $filterSQL = '';
  465. // Create extra SQL statements for the class group filters.
  466. if ( is_array( $groupList ) )
  467. {
  468. if ( count( $groupList ) == 0 )
  469. {
  470. return $classList;
  471. }
  472. $filterTableSQL = ', ezcontentclass_classgroup ccg';
  473. $filterSQL = ( " AND" .
  474. " cc.id = ccg.contentclass_id AND" .
  475. " " );
  476. $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' );
  477. }
  478. $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
  479. $filterSQL .= " AND cc.id=" . $classNameFilter['from'] . ".contentclass_id";
  480. if ( $fetchAll )
  481. {
  482. // If $asObject is true we fetch all fields in class
  483. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  484. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  485. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  486. "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL " .
  487. "ORDER BY $classNameFilter[nameField] ASC" );
  488. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  489. }
  490. else
  491. {
  492. // If the constrained class list is empty we are not allowed to create any class
  493. if ( count( $classIDArray ) == 0 )
  494. {
  495. return $classList;
  496. }
  497. $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' );
  498. // If $asObject is true we fetch all fields in class
  499. $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
  500. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  501. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  502. "WHERE $classIDCondition AND" .
  503. " cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL " .
  504. "ORDER BY $classNameFilter[nameField] ASC" );
  505. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  506. }
  507. if ( $asObject )
  508. {
  509. foreach ( $classList as $key => $class )
  510. {
  511. $id = $class->attribute( 'id' );
  512. if ( isset( $allowedLanguages[$id] ) )
  513. {
  514. $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) );
  515. }
  516. else
  517. {
  518. $languageCodes = $allowedLanguages['*'];
  519. }
  520. $classList[$key]->setCanInstantiateLanguages( $languageCodes );
  521. }
  522. }
  523. eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" );
  524. if ( $enableCaching )
  525. {
  526. if ( $fetchID !== false )
  527. {
  528. $groupArray[$fetchID] = $classList;
  529. $http->setSessionVariable( $cacheVar, $groupArray );
  530. }
  531. else
  532. {
  533. $http->setSessionVariable( $cacheVar, $classList );
  534. }
  535. }
  536. return $classList;
  537. }
  538. // This code is automatically generated from templates/classcreatelist.ctpl
  539. // code-template::auto-generated:END can-instantiate-class-list
  540. /*!
  541. \return The creator of the class as an eZUser object by using the $CreatorID as user ID.
  542. */
  543. function creator()
  544. {
  545. if ( isset( $this->CreatorID ) and $this->CreatorID )
  546. {
  547. return eZUser::fetch( $this->CreatorID );
  548. }
  549. return null;
  550. }
  551. /*!
  552. \return The modifier of the class as an eZUser object by using the $ModifierID as user ID.
  553. */
  554. function modifier()
  555. {
  556. if ( isset( $this->ModifierID ) and $this->ModifierID )
  557. {
  558. return eZUser::fetch( $this->ModifierID );
  559. }
  560. return null;
  561. }
  562. /*!
  563. Find all groups the current class is placed in and returns a list of group objects.
  564. \return An array with eZContentClassGroup objects.
  565. \sa fetchGroupIDList()
  566. */
  567. function fetchGroupList()
  568. {
  569. $this->InGroups = eZContentClassClassGroup::fetchGroupList( $this->attribute( "id" ),
  570. $this->attribute( "version" ),
  571. true );
  572. return $this->InGroups;
  573. }
  574. /*!
  575. Find all groups the current class is placed in and returns a list of group IDs.
  576. \return An array with integers (ids).
  577. \sa fetchGroupList()
  578. */
  579. function fetchGroupIDList()
  580. {
  581. $list = eZContentClassClassGroup::fetchGroupList( $this->attribute( "id" ),
  582. $this->attribute( "version" ),
  583. false );
  584. $this->InGroupIDs = array();
  585. foreach ( $list as $item )
  586. {
  587. $this->InGroupIDs[] = $item['group_id'];
  588. }
  589. return $this->InGroupIDs;
  590. }
  591. /*!
  592. Returns the result from fetchGroupIDList() if class group overrides is
  593. enabled in content.ini.
  594. \return An array with eZContentClassGroup objects or \c false if disabled.
  595. \note \c EnableClassGroupOverride in group \c ContentOverrideSettings from INI file content.ini
  596. controls this behaviour.
  597. */
  598. function fetchMatchGroupIDList()
  599. {
  600. $contentINI = eZINI::instance( 'content.ini' );
  601. if( $contentINI->variable( 'ContentOverrideSettings', 'EnableClassGroupOverride' ) == 'true' )
  602. {
  603. return $this->attribute( 'ingroup_id_list' );
  604. }
  605. return false;
  606. }
  607. /*!
  608. Finds all Classes in the system and returns them.
  609. \return An array with eZContentClass objects.
  610. */
  611. static function fetchAllClasses( $asObject = true, $includeFilter = true, $groupList = false )
  612. {
  613. $filterTableSQL = '';
  614. $filterSQL = '';
  615. if ( is_array( $groupList ) )
  616. {
  617. $filterTableSQL = ', ezcontentclass_classgroup ccg';
  618. $filterSQL = ( " AND" .
  619. " cc.id = ccg.contentclass_id AND" .
  620. " ccg.group_id " );
  621. $groupText = implode( ', ', $groupList );
  622. if ( $includeFilter )
  623. $filterSQL .= "IN ( $groupText )";
  624. else
  625. $filterSQL .= "NOT IN ( $groupText )";
  626. }
  627. $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
  628. $classList = array();
  629. $db = eZDb::instance();
  630. // If $asObject is true we fetch all fields in class
  631. $fields = $asObject ? "cc.*" : "cc.id, $classNameFilter[nameField]";
  632. $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
  633. "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
  634. "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . "$filterSQL AND $classNameFilter[where]" .
  635. "ORDER BY $classNameFilter[nameField] ASC" );
  636. $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
  637. return $classList;
  638. }
  639. /*!
  640. Finds all Class groups in the system and returns them.
  641. \return An array with eZContentClassGroup objects.
  642. \sa fetchGroupList(), fetchGroupIDList()
  643. */
  644. function fetchAllGroups()
  645. {
  646. $this->AllGroups = eZContentClassGroup::fetchList();
  647. return $this->AllGroups;
  648. }
  649. /*!
  650. \return true if the class is part of the group \a $groupID
  651. */
  652. function inGroup( $groupID )
  653. {
  654. return eZContentClassClassGroup::classInGroup( $this->attribute( 'id' ),
  655. $this->attribute( 'version' ),
  656. $groupID );
  657. }
  658. /*!
  659. \static
  660. Will remove all temporary classes from the database.
  661. */
  662. static function removeTemporary()
  663. {
  664. $version = eZContentClass::VERSION_STATUS_TEMPORARY;
  665. $temporaryClasses = eZContentClass::fetchList( $version, true );
  666. $db = eZDB::instance();
  667. $db->begin();
  668. foreach ( $temporaryClasses as $class )
  669. {
  670. $class->remove( true, $version );
  671. }
  672. eZPersistentObject::removeObject( eZContentClassAttribute::definition(),
  673. array( 'version' => $version ) );
  674. $db->commit();
  675. }
  676. /*!
  677. Get remote id of content node
  678. */
  679. function remoteID()
  680. {
  681. $remoteID = eZPersistentObject::attribute( 'remote_id', true );
  682. if ( !$remoteID &&
  683. $this->Version == eZContentClass::VERSION_STATUS_DEFINED )
  684. {
  685. $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'class' ) );
  686. $this->sync( array( 'remote_id' ) );
  687. $remoteID = eZPersistentObject::attribute( 'remote_id', true );
  688. }
  689. return $remoteID;
  690. }
  691. /*!
  692. \note If you want to remove a class with all data associated with it (objects/classMembers)
  693. you should use eZContentClassOperations::remove()
  694. */
  695. function remove( $removeAttributes = false, $version = eZContentClass::VERSION_STATUS_DEFINED )
  696. {
  697. // If we are not allowed to remove just return false
  698. if ( $this->Version == eZContentClass::VERSION_STATUS_DEFINED && !$this->isRemovable() )
  699. return false;
  700. if ( is_array( $removeAttributes ) or $removeAttributes )
  701. $this->removeAttributes( $removeAttributes );
  702. $this->NameList->remove( $this );
  703. eZPersistentObject::remove();
  704. }
  705. /*!
  706. Checks if the class can be removed and returns \c true if it can, \c false otherwise.
  707. \sa removableInformation()
  708. */
  709. function isRemovable()
  710. {
  711. $info = $this->removableInformation( false );
  712. return count( $info['list'] ) == 0;
  713. }
  714. /*!
  715. Returns information on why the class cannot be removed,
  716. it does the same checks as in isRemovable() but generates
  717. some text in the return array.
  718. \return An array which contains:
  719. - text - Plain text description why this cannot be removed
  720. - list - An array with reasons why this failed, each entry contains:
  721. - text - Plain text description of the reason.
  722. - list - A sublist of reason (e.g from an attribute), is optional.
  723. \param $includeAll Controls whether the returned information will contain all
  724. sources for not being to remove or just the first that it finds.
  725. */
  726. function removableInformation( $includeAll = true )
  727. {
  728. $result = array( 'text' => ezpI18n::tr( 'kernel/contentclass', "Cannot remove class '%class_name':",
  729. null, array( '%class_name' => $this->attribute( 'name' ) ) ),
  730. 'list' => array() );
  731. $db = eZDB::instance();
  732. // Check top-level nodes
  733. $rows = $db->arrayQuery( "SELECT ezcot.node_id
  734. FROM ezcontentobject_tree ezcot, ezcontentobject ezco
  735. WHERE ezcot.depth = 1 AND
  736. ezco.contentclass_id = $this->ID AND
  737. ezco.id=ezcot.contentobject_id" );
  738. if ( count( $rows ) > 0 )
  739. {
  740. $result['list'][] = array( 'text' => ezpI18n::tr( 'kernel/contentclass', 'The class is used by a top-level node and cannot be removed.
  741. You will need to change the class of the node by using the swap functionality.' ) );
  742. if ( !$includeAll )
  743. return $result;
  744. }
  745. // Check class attributes
  746. foreach ( $this->fetchAttributes() as $attribute )
  747. {
  748. $dataType = $attribute->dataType();
  749. if ( !$dataType->isClassAttributeRemovable( $attribute ) )
  750. {
  751. $info = $dataType->classAttributeRemovableInformation( $attribute, $includeAll );
  752. $result['list'][] = $info;
  753. if ( !$includeAll )
  754. return $result;
  755. }
  756. }
  757. return $result;
  758. }
  759. /*!
  760. \note Removes class attributes
  761. \param removeAttributes Array of attributes to remove
  762. \param version Version to remove( optional )
  763. */
  764. function removeAttributes( $removeAttributes = false, $version = false )
  765. {
  766. if ( is_array( $removeAttributes ) )
  767. {
  768. $db = eZDB::instance();
  769. $db->begin();
  770. foreach( $removeAttributes as $attribute )
  771. {
  772. $attribute->removeThis();
  773. }
  774. $db->commit();
  775. }
  776. else
  777. {
  778. $contentClassID = $this->ID;
  779. if ( $version === false )
  780. {
  781. $version = $this->Version;
  782. }
  783. $classAttributes = $this->fetchAttributes( );
  784. $db = eZDB::instance();
  785. $db->begin();
  786. foreach ( $classAttributes as $classAttribute )
  787. {
  788. $dataType = $classAttribute->dataType();
  789. $dataType->deleteStoredClassAttribute( $classAttribute, $version );
  790. }
  791. eZPersistentObject::removeObject( eZContentClassAttribute::definition(),
  792. array( 'contentclass_id' => $contentClassID,
  793. 'version' => $version ) );
  794. $db->commit();
  795. }
  796. }
  797. function compareAttributes( $attr1, $attr2 )
  798. {
  799. return ( $attr1->attribute( "placement" ) > $attr2->attribute( "placement" ) ) ? 1 : -1;
  800. }
  801. function adjustAttributePlacements( $attributes )
  802. {
  803. if ( !is_array( $attributes ) )
  804. return;
  805. usort( $attributes, array( $this, "compareAttributes" ) );
  806. $i = 0;
  807. foreach( $attributes as $attribute )
  808. {
  809. $attribute->setAttribute( "placement", ++$i );
  810. }
  811. }
  812. function store( $store_childs = false, $fieldFilters = null )
  813. {
  814. self::expireCache();
  815. $db = eZDB::instance();
  816. $db->begin();
  817. if ( is_array( $store_childs ) ||
  818. $store_childs )
  819. {
  820. if ( is_array( $store_childs ) )
  821. {
  822. $attributes = $store_childs;
  823. }
  824. else
  825. {
  826. $attributes = $this->fetchAttributes();
  827. }
  828. foreach( $attributes as $attribute )
  829. {
  830. if ( is_object ( $attribute ) )
  831. $attribute->store();
  832. }
  833. }
  834. eZExpiryHandler::registerShutdownFunction();
  835. $handler = eZExpiryHandler::instance();
  836. $handler->setTimestamp( 'user-class-cache', time() );
  837. $handler->store();
  838. $this->setAttribute( 'serialized_name_list', $this->NameList->serializeNames() );
  839. $this->setAttribute( 'serialized_description_list', $this->DescriptionList->serializeNames() );
  840. eZPersistentObject::store( $fieldFilters );
  841. $this->NameList->store( $this );
  842. $db->commit();
  843. }
  844. function sync( $fieldFilters = null )
  845. {
  846. if ( $this->hasDirtyData() )
  847. $this->store( false, $fieldFilters );
  848. }
  849. /*!
  850. Initializes this class as a copy of \a $originalClass by
  851. creating new a new name and identifier.
  852. It will check if there are other classes already with this name
  853. in which case it will append a unique number to the name and identifier.
  854. */
  855. function initializeCopy( &$originalClass )
  856. {
  857. $name = ezpI18n::tr( 'kernel/class', 'Copy of %class_name', null,
  858. array( '%class_name' => $originalClass->attribute( 'name' ) ) );
  859. $identifier = 'copy_of_' . $originalClass->attribute( 'identifier' );
  860. $db = eZDB::instance();
  861. $sql = "SELECT count( ezcontentclass_name.name ) AS count FROM ezcontentclass, ezcontentclass_name WHERE ezcontentclass.id = ezcontentclass_name.contentclass_id AND ezcontentclass_name.name like '" . $db->escapeString( $name ) . "%'";
  862. $rows = $db->arrayQuery( $sql );
  863. $count = $rows[0]['count'];
  864. if ( $count > 0 )
  865. {
  866. ++$count;
  867. $name .= $count;
  868. $identifier .= $count;
  869. }
  870. $this->setName( $name );
  871. $this->setAttribute( 'identifier', $identifier );
  872. $this->setAttribute( 'created', time() );
  873. $user = eZUser::currentUser();
  874. $userID = $user->attribute( "contentobject_id" );
  875. $this->setAttribute( 'creator_id', $userID );
  876. }
  877. /**
  878. * Stores the current class as a defined version, updates the contentobject_name
  879. * attribute and recreates the class group entries.
  880. *
  881. * @note It will remove any existing temporary or defined classes before storing.
  882. *
  883. * @param array $attributes array of attributes of the content class
  884. */
  885. public function storeDefined( $attributes )
  886. {
  887. $this->storeVersioned( $attributes, self::VERSION_STATUS_DEFINED );
  888. }
  889. /**
  890. * Stores the current class as a modified version, updates the contentobject_name
  891. * attribute and recreates the class group entries.
  892. *
  893. * @note It will remove classes in the previous and specified version before storing.
  894. *
  895. * @param array $attributes array of attributes
  896. * @param int $version version status
  897. * @since Version 4.3
  898. */
  899. public function storeVersioned( $attributes, $version )
  900. {
  901. $previousVersion = $this->attribute( 'version' );
  902. $db = eZDB::instance();
  903. $db->begin();
  904. $this->removeAttributes( false, $version );
  905. $this->removeAttributes( false, $previousVersion );
  906. $this->remove( false );
  907. $this->setVersion( $version, $attributes );
  908. $this->setAttribute( "modifier_id", eZUser::currentUser()->attribute( "contentobject_id" ) );
  909. $this->setAttribute( "modified", time() );
  910. $this->adjustAttributePlacements( $attributes );
  911. foreach( $attributes as $attribute )
  912. {
  913. $attribute->storeVersioned( $version );
  914. }
  915. // Set contentobject_name to something sensible if it is missing
  916. if ( count( $attributes ) > 0 && trim( $this->attribute( 'contentobject_name' ) ) == '' )
  917. {
  918. $this->setAttribute( 'contentobject_name', '<' . $attributes[0]->attribute( 'identifier' ) . '>' );
  919. }
  920. // Recreate class member entries
  921. eZContentClassClassGroup::removeClassMembers( $this->ID, $version );
  922. foreach( eZContentClassClassGroup::fetchGroupList( $this->ID, $previousVersion ) as $classgroup )
  923. {
  924. $classgroup->setAttribute( 'contentclass_version', $version );
  925. $classgroup->store();
  926. }
  927. eZContentClassClassGroup::removeClassMembers( $this->ID, $previousVersion );
  928. eZExpiryHandler::registerShutdownFunction();
  929. $handler = eZExpiryHandler::instance();
  930. $time = time();
  931. $handler->setTimestamp( 'user-class-cache', $time );
  932. $handler->setTimestamp( 'class-identifier-cache', $time );
  933. $handler->setTimestamp( 'sort-key-cache', $time );
  934. $handler->store();
  935. eZContentCacheManager::clearAllContentCache();
  936. $this->setAttribute( 'serialized_name_list', $this->NameList->serializeNames() );
  937. $this->setAttribute( 'serialized_description_list', $this->DescriptionList->serializeNames() );
  938. eZPersistentObject::store();
  939. $this->NameList->store( $this );
  940. $db->commit();
  941. }
  942. function setVersion( $version, $set_childs = false )
  943. {
  944. if ( is_array( $set_childs ) or $set_childs )
  945. {
  946. if ( is_array( $set_childs ) )
  947. {
  948. $attributes = $set_childs;
  949. }
  950. else
  951. {
  952. $attributes = $this->fetchAttributes();
  953. }
  954. foreach( $attributes as $attribute )
  955. {
  956. $attribute->setAttribute( "version", $version );
  957. }
  958. }
  959. if ( $this->Version != $version )
  960. $this->NameList->setHasDirtyData();
  961. $this->setAttribute( "version", $version );
  962. }
  963. static function exists( $id, $version = eZContentClass::VERSION_STATUS_DEFINED, $userID = false, $useIdentifier = false )
  964. {
  965. $conds = array( "version" => $version );
  966. if ( $useIdentifier )
  967. $conds["identifier"] = $id;
  968. else
  969. $conds["id"] = $id;
  970. if ( $userID !== false and is_numeric( $userID ) )
  971. $conds["creator_id"] = $userID;
  972. $version_sort = "desc";
  973. if ( $version == eZContentClass::VERSION_STATUS_DEFINED )
  974. $conds['version'] = $version;
  975. $rows = eZPersistentObject::fetchObjectList( eZContentClass::definition(),
  976. null,
  977. $conds,
  978. null,
  979. array( "offset" => 0,
  980. "length" => 1 ),
  981. false );
  982. if ( count( $rows ) > 0 )
  983. return $rows[0]['id'];
  984. return false;
  985. }
  986. static function fetch( $id, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED, $user_id = false ,$parent_id = null )
  987. {
  988. global $eZContentClassObjectCache;
  989. // If the object given by its id is not cached or should be returned as array
  990. // then we fetch it from the DB (objects are always cached as arrays).
  991. if ( !isset( $eZContentClassObjectCache[$id] ) or $asObject === false or $version != eZContentClass::VERSION_STATUS_DEFINED )
  992. {
  993. $conds = array( "id" => $id,
  994. "version" => $version );
  995. if ( $user_id !== false and is_numeric( $user_id ) )
  996. $conds["creator_id"] = $user_id;
  997. $version_sort = "desc";
  998. if ( $version == eZContentClass::VERSION_STATUS_DEFINED )
  999. $version_sort = "asc";
  1000. $rows = eZPersistentObject::fetchObjectList( eZContentClass::definition(),
  1001. null,
  1002. $conds,
  1003. array( "version" => $version_sort ),
  1004. array( "offset" => 0,
  1005. "length" => 2 ),
  1006. false );
  1007. if ( count( $rows ) == 0 )
  1008. {
  1009. $contentClass = null;
  1010. return $contentClass;
  1011. }
  1012. $row = $rows[0];
  1013. $row["version_count"] = count( $rows );
  1014. if ( $asObject )
  1015. {
  1016. $contentClass = new eZContentClass( $row );
  1017. if ( $version == eZContentClass::VERSION_STATUS_DEFINED )
  1018. {
  1019. $eZContentClassObjectCache[$id] = $contentClass;
  1020. }
  1021. return $contentClass;
  1022. }
  1023. else
  1024. $contentClass = $row;
  1025. }
  1026. else
  1027. {
  1028. $contentClass = $eZContentClassObjectCache[$id];
  1029. }
  1030. return $contentClass;
  1031. }
  1032. static function fetchByRemoteID( $remoteID, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED, $user_id = false ,$parent_id = null )
  1033. {
  1034. $conds = array( "remote_id" => $remoteID,
  1035. "version" => $version );
  1036. if ( $user_id !== false and is_numeric( $user_id ) )
  1037. $conds["creator_id"] = $user_id;
  1038. $version_sort = "desc";
  1039. if ( $version == eZContentClass::VERSION_STATUS_DEFINED )
  1040. $version_sort = "asc";
  1041. $rows = eZPersistentObject::fetchObjectList( eZContentClass::definition(),
  1042. null,
  1043. $conds,
  1044. array( "version" => $version_sort ),
  1045. array( "offset" => 0,
  1046. "length" => 2 ),
  1047. false );
  1048. if ( count( $rows ) == 0 )
  1049. {
  1050. $contentClass = null;
  1051. return $contentClass;
  1052. }
  1053. $row = $rows[0];
  1054. $row["version_count"] = count( $rows );
  1055. if ( $asObject )
  1056. {
  1057. return new eZContentClass( $row );
  1058. }
  1059. return $row;
  1060. }
  1061. static function fetchByIdentifier( $identifier, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED, $user_id = false ,$parent_id = null )
  1062. {
  1063. $conds = array( "identifier" => $identifier,
  1064. "version" => $version );
  1065. if ( $user_id !== false and is_numeric( $user_id ) )
  1066. $conds["creator_id"] = $user_id;
  1067. $version_sort = "desc";
  1068. if ( $version == eZContentClass::VERSION_STATUS_DEFINED )
  1069. $version_sort = "asc";
  1070. $rows = eZPersistentObject::fetchObjectList( eZContentClass::definition(),
  1071. null,
  1072. $conds,
  1073. array( "version" => $version_sort ),
  1074. array( "offset" => 0,
  1075. "length" => 2 ),
  1076. false );
  1077. if ( count( $rows ) == 0 )
  1078. {
  1079. $contentClass = null;
  1080. return $contentClass;
  1081. }
  1082. $row = $rows[0];
  1083. $row["version_count"] = count( $rows );
  1084. if ( $asObject )
  1085. {
  1086. return new eZContentClass( $row );
  1087. }
  1088. return $row;
  1089. }
  1090. /*!
  1091. \static
  1092. */
  1093. static function fetchList( $version = eZContentClass::VERSION_STATUS_DEFINED, $asObject = true, $user_id = false,
  1094. $sorts = null, $fields = null, $classFilter = false, $limit = null )
  1095. {
  1096. $conds = array();
  1097. $custom_fields = null;
  1098. $custom_tables = null;
  1099. $custom_conds = null;
  1100. if ( is_numeric( $version ) )
  1101. $conds["version"] = $version;
  1102. if ( $user_id !== false and is_numeric( $user_id ) )
  1103. $conds["creator_id"] = $user_id;
  1104. if ( $classFilter )
  1105. {
  1106. $classIDCount = 0;
  1107. $classIdentifierCount = 0;
  1108. $classIDFilter = array();
  1109. $classIdentifierFilter = array();
  1110. foreach ( $classFilter as $classType )
  1111. {
  1112. if ( is_numeric( $classType ) )
  1113. {
  1114. $classIDFilter[] = $classType;
  1115. $classIDCount++;
  1116. }
  1117. else
  1118. {
  1119. $classIdentifierFilter[] = $classType;
  1120. $classIdentifierCount++;
  1121. }
  1122. }
  1123. if ( $classIDCount > 1 )
  1124. $conds['id'] = array( $classIDFilter );
  1125. else if ( $classIDCount == 1 )
  1126. $conds['id'] = $classIDFilter[0];
  1127. if ( $classIdentifierCount > 1 )
  1128. $conds['identifier'] = array( $classIdentifierFilter );
  1129. else if ( $classIdentifierCount == 1 )
  1130. $conds['identifier'] = $classIdentifierFilter[0];
  1131. }
  1132. if ( $sorts && isset( $sorts['name'] ) )
  1133. {
  1134. $nameFiler = eZContentClassName::sqlFilter( 'ezcontentclass' );
  1135. $custom_tables = array( $nameFiler['from'] );
  1136. $custom_conds = "AND " . $nameFiler['where'];
  1137. $custom_fields = array( $nameFiler['nameField'] );
  1138. $sorts[$nameFiler['orderBy']] = $sorts['name'];
  1139. unset( $sorts['name'] );
  1140. }
  1141. return eZPersistentObject::fetchObjectList( eZContentClass::definition(),
  1142. $fields,
  1143. $conds,
  1144. $sorts,
  1145. $limit,
  1146. $asObject,
  1147. false,
  1148. $custom_fields,
  1149. $custom_tables,
  1150. $custom_conds );
  1151. }
  1152. /*!
  1153. Returns all attributes as an associative array with the key taken from the attribute identifier.
  1154. */
  1155. function dataMap()
  1156. {
  1157. if ( !isset( $this->DataMap[$this->Version] ) )
  1158. {
  1159. $attributes = $this->fetchAttributes( false, true, $this->Version );
  1160. foreach ( $attributes as $attribute )
  1161. {
  1162. $this->DataMap[$this->Version][$attribute->attribute( 'identifier' )] = $attribute;
  1163. }
  1164. }
  1165. return $this->DataMap[$this->Version];
  1166. }
  1167. function fetchAttributes( $id = false, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED )
  1168. {
  1169. if ( $id === false )
  1170. {
  1171. $id = $this->ID;
  1172. $version = $this->Version;
  1173. }
  1174. return eZContentClassAttribute::fetchFilteredList( array( "contentclass_id" => $id,
  1175. "version" => $version ),
  1176. $asObject );
  1177. }
  1178. /*!
  1179. Fetch class attribute by identifier, return null if none exist.
  1180. \param attribute identifier.
  1181. \return Class Attribute, null if none matched
  1182. */
  1183. function fetchAttributeByIdentifier( $identifier, $asObject = true )
  1184. {
  1185. $attributeArray = eZContentClassAttribute::fetchFilteredList( array( 'contentclass_id' => $this->ID,
  1186. 'version' => $this->Version,
  1187. 'identifier' => $identifier ), $asObject );
  1188. if ( count( $attributeArray ) > 0 )
  1189. {
  1190. return $attributeArray[0];
  1191. }
  1192. return null;
  1193. }
  1194. function fetchSearchableAttributes( $id = false, $asObject = true, $version = eZContentClass::VERSION_STATUS_DEFINED )
  1195. {
  1196. if ( $id === false )
  1197. {
  1198. $id = $this->ID;
  1199. $version = $this->Version;
  1200. }
  1201. return eZContentClassAttribute::fetchFilteredList( array( "contentclass_id" => $id,
  1202. "is_searchable" => 1,
  1203. "version" => $version ), $asObject );
  1204. }
  1205. function versionStatus()
  1206. {
  1207. if ( $this->VersionCount == 1 )
  1208. {
  1209. if ( $this->Version == eZContentClass::VERSION_STATUS_TEMPORARY )
  1210. {
  1211. return eZContentClass::VERSION_STATUS_TEMPORARY;
  1212. }
  1213. return eZContentClass::VERSION_STATUS_DEFINED;
  1214. }
  1215. return eZContentClass::VERSION_STATUS_MODIFIED;
  1216. }
  1217. /*!
  1218. \deprecated
  1219. \return The version count for the class if has been determined.
  1220. */
  1221. function versionCount()
  1222. {
  1223. return $this->VersionCount;
  1224. }
  1225. /**
  1226. * Will generate a name for the content object based on the class
  1227. * settings for content object limited by self::CONTENT_OBJECT_NAME_MAX_LENGTH.
  1228. *
  1229. * @param eZContentObject $contentObject
  1230. * @param int|false $version
  1231. * @param string|false $translation
  1232. * @return string
  1233. */
  1234. function contentObjectName( eZContentObject $contentObject, $version = false, $translation = false )
  1235. {
  1236. $contentObjectNamePattern = $this->ContentObjectName;
  1237. $ini = eZINI::instance();
  1238. $length = (int) $ini->variable('ContentSettings', 'ContentObjectNameLimit');
  1239. $sequence = $ini->variable('ContentSettings', 'ContentObjectNameLimitSequence');
  1240. if ( $length < 1 || $length > self::CONTENT_OBJECT_NAME_MAX_LENGTH )
  1241. {
  1242. $length = self::CONTENT_OBJECT_NAME_MAX_LENGTH;
  1243. }
  1244. $nameResolver = new eZNamePatternResolver( $contentObjectNamePattern, $contentObject, $version, $translation );
  1245. $contentObjectName = $nameResolver->resolveNamePattern( $length, $sequence );
  1246. return $contentObjectName;
  1247. }
  1248. /**
  1249. * Will generate a name for the url alias based on the class
  1250. * settings for content object limited by site.ini\[URLTranslator]\UrlAliasNameLimit
  1251. *
  1252. * @param eZContentObject $contentObject
  1253. * @param int|false $version
  1254. * @param string|false $translation
  1255. * @return string
  1256. */
  1257. function urlAliasName( eZContentObject $contentObject, $version = false, $translation = false )
  1258. {
  1259. if ( $this->URLAliasName )
  1260. {
  1261. $urlAliasNamePattern = $this->URLAliasName;
  1262. }
  1263. else
  1264. {
  1265. $urlAliasNamePattern = $this->ContentObjectName;
  1266. }
  1267. $length = (int) eZINI::instance()->variable('URLTranslator', 'UrlAliasNameLimit');
  1268. $nameResolver = new eZNamePatternResolver( $urlAliasNamePattern, $contentObject, $version, $translation );
  1269. $urlAliasName = $nameResolver->resolveNamePattern( $length );
  1270. return $urlAliasName;
  1271. }
  1272. /*!
  1273. Generates a name for the content object based on the content object name pattern
  1274. and data map of an object.
  1275. */
  1276. function buildContentObjectName( $contentObjectName, $dataMap, $tmpTags = false )
  1277. {
  1278. preg_match_all( "|<[^>]+>|U",
  1279. $contentObjectName,
  1280. $tagMatchArray );
  1281. foreach ( $tagMatchArray[0] as $tag )
  1282. {
  1283. $tagName = str_replace( "<", "", $tag );
  1284. $tagName = str_replace( ">", "", $tagName );
  1285. $tagParts = explode( '|', $tagName );
  1286. $namePart = "";
  1287. foreach ( $tagParts as $name )
  1288. {
  1289. // get the value of the attribute to use in name
  1290. if ( isset( $dataMap[$name] ) )
  1291. {
  1292. $namePart = $dataMap[$name]->title();
  1293. if ( $namePart != "" )
  1294. break;
  1295. }
  1296. elseif ( $tmpTags && isset( $tmpTags[$name] ) && $tmpTags[$name] != '' )
  1297. {
  1298. $namePart = $tmpTags[$name];
  1299. break;
  1300. }
  1301. }
  1302. // replace tag with object name part
  1303. $contentObjectName = str_replace( $tag, $namePart, $contentObjectName );
  1304. }
  1305. return $contentObjectName;
  1306. }
  1307. /*!
  1308. \return will return the number of objects published by this class.
  1309. */
  1310. function objectCount()
  1311. {
  1312. $db = eZDB::instance();
  1313. $countRow = $db->arrayQuery( 'SELECT count(*) AS count FROM ezcontentobject '.
  1314. 'WHERE contentclass_id='.$this->ID ." and status = " . eZContentObject::STATUS_PUBLISHED );
  1315. return $countRow[0]['count'];
  1316. }
  1317. /*!
  1318. \return will return the list of objects published by this class.
  1319. */
  1320. function objectList()
  1321. {
  1322. return eZContentObject::fetchSameClassList( $this->ID );
  1323. }
  1324. /*!
  1325. \return Sets the languages which are allowed to be instantiated for the class.
  1326. Used only for the content/ fetch function.
  1327. */
  1328. function setCanInstantiateLanguages( $languageCodes )
  1329. {
  1330. $this->CanInstantiateLanguages = $languageCodes;
  1331. }
  1332. function canInstantiateLanguages()
  1333. {
  1334. if ( is_array( $this->CanInstantiateLanguages ) )
  1335. {
  1336. return array_intersect( eZContentLanguage::prioritizedLanguageCodes(), $this->CanInstantiateLanguages );
  1337. }
  1338. return array();
  1339. }
  1340. /*!
  1341. \static
  1342. Returns a contentclass name from serialized array \a $serializedNameList using
  1343. top language from siteaccess language list or 'always available' name
  1344. from \a $serializedNameList.
  1345. \return string with contentclass name.
  1346. */
  1347. static function nameFromSerializedString( $serializedNameList )
  1348. {
  1349. return eZContentClassNameList::nameFromSerializedString( $serializedNameList );
  1350. }
  1351. /*!
  1352. \static
  1353. Returns a contentclass description from serialized array \a $serializedNameList using
  1354. top language from siteaccess language list or 'always available' name
  1355. from \a $serializedNameList.
  1356. \return string with contentclass description.
  1357. */
  1358. static function descriptionFromSerializedString( $serializedDescriptionList )
  1359. {
  1360. return eZSerializedObjectNameList::nameFromSerializedString( $serializedNameList );
  1361. }
  1362. function hasNameInLanguage( $languageLocale )
  1363. {
  1364. $hasName = $this->NameList->hasNameInLocale( $languageLocale );
  1365. return $hasName;
  1366. }
  1367. /*!
  1368. Returns a contentclass name in \a $languageLocale language.
  1369. Uses siteaccess language list or 'always available' language if \a $languageLocale is 'false'.
  1370. \return string with contentclass name.
  1371. */
  1372. function name( $languageLocale = false )
  1373. {
  1374. return $this->NameList->name( $languageLocale );
  1375. }
  1376. function setName( $name, $languageLocale = false )
  1377. {
  1378. if ( !$languageLocale )
  1379. $languageLocale = $this->topPriorityLanguageLocale();
  1380. $this->NameList->setNameByLanguageLocale( $name, $languageLocale );
  1381. $languageID = eZContentLanguage::idByLocale( $languageLocale );
  1382. $languageMask = $this->attribute( 'language_mask' );
  1383. $this->setAttribute( 'language_mask', $languageMask | $languageID );
  1384. }
  1385. function nameList()
  1386. {
  1387. return $this->NameList->nameList();
  1388. }
  1389. /*!
  1390. Returns a contentclass description in \a $languageLocale language.
  1391. Uses siteaccess language list or 'always available' language if \a $languageLocale is 'false'.
  1392. \return string with contentclass name.
  1393. */
  1394. function description( $languageLocale = false )
  1395. {
  1396. return $this->DescriptionList->name( $languageLocale );
  1397. }
  1398. function setDescription( $description, $languageLocale = false )
  1399. {
  1400. $this->DescriptionList->setName( $description, $languageLocale );
  1401. }
  1402. function descriptionList()
  1403. {
  1404. return $this->DescriptionList->nameList();
  1405. }
  1406. function setAlwaysAvailableLanguageID( $languageID, $updateChilds = true )
  1407. {
  1408. $db = eZDB::instance();
  1409. $db->begin();
  1410. $languageLocale = false;
  1411. if ( $languageID )
  1412. {
  1413. $language = eZContentLanguage::fetch( $languageID );
  1414. $languageLocale = $language->attribute( 'locale' );
  1415. }
  1416. if ( $languageID )
  1417. {
  1418. $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) | 1 );
  1419. $this->NameList->setAlwaysAvailableLanguage( $languageLocale );
  1420. $this->DescriptionList->setAlwaysAvailableLanguage( $languageLocale );
  1421. }
  1422. else
  1423. {
  1424. $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) & ~1 );
  1425. $this->NameList->setAlwaysAvailableLanguage( false );
  1426. $this->DescriptionList->setAlwaysAvailableLanguage( false );
  1427. }
  1428. $this->store();
  1429. $classID = $this->attribute( 'id' );
  1430. $version = $this->attribute( 'version' );
  1431. $attributes = $this->fetchAttributes();
  1432. foreach( $attributes as $attribute )
  1433. {
  1434. $attribute->setAlwaysAvailableLanguage( $languageLocale );
  1435. $attribute->store();
  1436. }
  1437. // reset 'always available' flag
  1438. $sql = "UPDATE ezcontentclass_name SET language_id=";
  1439. if ( $db->databaseName() == 'oracle' )
  1440. {
  1441. $sql .= "bitand( language_id, -2 )";
  1442. }
  1443. else
  1444. {
  1445. $sql .= "language_id & ~1";
  1446. }
  1447. $sql .= " WHERE contentclass_id = '$classID' AND contentclass_version = '$version'";
  1448. $db->query( $sql );
  1449. if ( $languageID != false )
  1450. {
  1451. $newLanguageID = $languageID | 1;
  1452. $sql = "UPDATE ezcontentclass_name
  1453. SET language_id='$newLanguageID'
  1454. WHERE language_id='$languageID' AND contentclass_id = '$classID' AND contentclass_version = '$version'";
  1455. $db->query( $sql );
  1456. }
  1457. $db->commit();
  1458. }
  1459. function clearAlwaysAvailableLanguageID()
  1460. {
  1461. $this->setAlwaysAvailableLanguageID( false );
  1462. }
  1463. /*!
  1464. Wrapper for eZContentClassNameList::languages.
  1465. */
  1466. function languages()
  1467. {
  1468. return $this->NameList->languages();
  1469. }
  1470. /*!
  1471. Wrapper for eZContentClassNameList::prioritizedLanguages.
  1472. */
  1473. function prioritizedLanguages()
  1474. {
  1475. return $this->NameList->prioritizedLanguages();
  1476. }
  1477. function prioritizedLanguagesJsArray()
  1478. {
  1479. return $this->NameList->prioritizedLanguagesJsArray();
  1480. }
  1481. /*!
  1482. Wrapper for eZContentClassNameList::untranslatedLanguages.
  1483. */
  1484. function canCreateLanguages()
  1485. {
  1486. return $this->NameList->untranslatedLanguages();
  1487. }
  1488. /*!
  1489. Wrapper for eZContentClassNameList::topPriorityLanguageLocale.
  1490. */
  1491. function topPriorityLanguageLocale()
  1492. {
  1493. return $this->NameList->topPriorityLanguageLocale();
  1494. }
  1495. /*!
  1496. Wrapper for eZContentClassNameList::alwaysAvailableLanguage.
  1497. \return 'language' object.
  1498. */
  1499. function alwaysAvailableLanguage()
  1500. {
  1501. return $this->NameList->alwaysAvailableLanguage();
  1502. }
  1503. /*!
  1504. Wrapper for eZContentClassNameList::alwaysAvailableLanguageLocale.
  1505. \return 'language' object.
  1506. */
  1507. function alwaysAvailableLanguageLocale()
  1508. {
  1509. $language = $this->NameList->alwaysAvailableLanguageLocale();
  1510. return $language;
  1511. }
  1512. /*!
  1513. Removes translated name for specified by \a $languageID language.
  1514. */
  1515. function removeTranslation( $languageID )
  1516. {
  1517. $language = eZContentLanguage::fetch( $languageID );
  1518. if ( !$language )
  1519. {
  1520. return false;
  1521. }
  1522. // check if it is not the initial language
  1523. $classInitialLanguageID = $this->attribute( 'initial_language_id' );
  1524. if ( $classInitialLanguageID == $languageID )
  1525. {
  1526. return false;
  1527. }
  1528. $db = eZDB::instance();
  1529. $db->begin();
  1530. $classID = $this->attribute( 'id' );
  1531. $languageID = $language->attribute( 'id' );
  1532. // change language_mask of the object
  1533. $languageMask = (int) $this->attribute( 'language_mask' );
  1534. $languageMask = (int) $languageMask & ~ (int) $languageID;
  1535. $this->setAttribute( 'language_mask', $languageMask );
  1536. // Remove all names in the language
  1537. $db->query( "DELETE FROM ezcontentclass_name
  1538. WHERE contentclass_id='$classID'
  1539. AND language_id='$languageID'" );
  1540. $languageLocale = $language->attribute( 'locale' );
  1541. $this->NameList->removeName( $languageLocale );
  1542. $this->DescriptionList->removeName( $languageLocale );
  1543. $this->store();
  1544. // Remove names for attributes in the language
  1545. $attributes = $this->fetchAttributes();
  1546. foreach ( $attributes as $attribute )
  1547. {
  1548. $attribute->removeTranslation( $languageLocale );
  1549. $attribute->store();
  1550. }
  1551. $db->commit();
  1552. return true;
  1553. }
  1554. /**
  1555. * Resolves the string class identifier $identifier to its numeric value
  1556. * Use {@link eZContentObjectTreeNode::classIDByIdentifier()} for < 4.1.
  1557. * If multiple classes have the same identifier, the first found is returned.
  1558. *
  1559. * @static
  1560. * @since Version 4.1
  1561. * @param string|array $identifier identifier string or array of identifiers (array support added in 4.1.1)
  1562. * @return int|false Returns classid or false
  1563. */
  1564. public static function classIDByIdentifier( $identifier )
  1565. {
  1566. $identifierHash = self::classIdentifiersHash();
  1567. if ( is_array( $identifier ) )
  1568. {
  1569. $idList = array();
  1570. foreach( $identifier as $identifierItem )
  1571. {
  1572. if ( isset( $identifierHash[$identifierItem] ) )
  1573. $idList[] = $identifierHash[$identifierItem];
  1574. else if ( is_numeric( $identifierItem ) ) // to be able to pass mixed arrays
  1575. $idList[] = $identifierItem;
  1576. }
  1577. return $idList;
  1578. }
  1579. else if ( isset( $identifierHash[$identifier] ) )
  1580. return $identifierHash[$identifier];
  1581. else
  1582. return false;
  1583. }
  1584. /**
  1585. * Resolves the numeric class identifier $id to its string value
  1586. *
  1587. * @static
  1588. * @since Version 4.1
  1589. * @return string|false Returns classidentifier or false
  1590. */
  1591. public static function classIdentifierByID( $id )
  1592. {
  1593. $identifierHash = array_flip( self::classIdentifiersHash() );
  1594. if ( isset( $identifierHash[$id] ) )
  1595. return $identifierHash[$id];
  1596. else
  1597. return false;
  1598. }
  1599. /**
  1600. * Returns the class identifier hash for the current database.
  1601. * If it is outdated or non-existent, the method updates/generates the file
  1602. *
  1603. * @static
  1604. * @since Version 4.1
  1605. * @access protected
  1606. * @return array Returns hash of classidentifier => classid
  1607. */
  1608. protected static function classIdentifiersHash()
  1609. {
  1610. if ( self::$identifierHash === null )
  1611. {
  1612. $db = eZDB::instance();
  1613. $dbName = md5( $db->DB );
  1614. $cacheDir = eZSys::cacheDirectory();
  1615. $phpCache = new eZPHPCreator( $cacheDir,
  1616. 'classidentifiers_' . $dbName . '.php',
  1617. '',
  1618. array( 'clustering' => 'classidentifiers' ) );
  1619. eZExpiryHandler::registerShutdownFunction();
  1620. $handler = eZExpiryHandler::instance();
  1621. $expiryTime = 0;
  1622. if ( $handler->hasTimestamp( 'class-identifier-cache' ) )
  1623. {
  1624. $expiryTime = $handler->timestamp( 'class-identifier-cache' );
  1625. }
  1626. if ( $phpCache->canRestore( $expiryTime ) )
  1627. {
  1628. $var = $phpCache->restore( array( 'identifierHash' => 'identifier_hash' ) );
  1629. self::$identifierHash = $var['identifierHash'];
  1630. }
  1631. else
  1632. {
  1633. // Fetch identifier/id pair from db
  1634. $query = "SELECT id, identifier FROM ezcontentclass where version=0";
  1635. $identifierArray = $db->arrayQuery( $query );
  1636. self::$identifierHash = array();
  1637. foreach ( $identifierArray as $identifierRow )
  1638. {
  1639. self::$identifierHash[$identifierRow['identifier']] = $identifierRow['id'];
  1640. }
  1641. // Store identifier list to cache file
  1642. $phpCache->addVariable( 'identifier_hash', self::$identifierHash );
  1643. $phpCache->store();
  1644. }
  1645. }
  1646. return self::$identifierHash;
  1647. }
  1648. /*!
  1649. Returns an array of IDs of classes containing a specified datatype
  1650. \param $dataTypeString a datatype identification string
  1651. */
  1652. static function fetchIDListContainingDatatype( $dataTypeString )
  1653. {
  1654. $db = eZDB::instance();
  1655. $version = self::VERSION_STATUS_DEFINED;
  1656. $escapedDataTypeString = $db->escapeString( $dataTypeString );
  1657. $sql = "SELECT DISTINCT contentclass_id
  1658. FROM ezcontentclass_attribute
  1659. WHERE version=$version
  1660. AND data_type_string='$escapedDataTypeString'";
  1661. $classIDArray = $db->arrayQuery( $sql, array( 'column' => 'contentclass_id' ) );
  1662. return $classIDArray;
  1663. }
  1664. /**
  1665. * Expires in-memory cache for eZContentClass.
  1666. *
  1667. * Clears cache for fetched eZContentClass objects,
  1668. * class identifiers and class attributes.
  1669. *
  1670. * @since 4.2
  1671. */
  1672. public static function expireCache()
  1673. {
  1674. unset( $GLOBALS['eZContentClassObjectCache'] );
  1675. self::$identifierHash = null;
  1676. eZContentClassAttribute::expireCache();
  1677. }
  1678. /**
  1679. * Computes the version history limit for a content class
  1680. *
  1681. * @param mixed $class
  1682. * Content class ID, content class identifier or content class object
  1683. * @return int
  1684. * @since 4.2
  1685. */
  1686. public static function versionHistoryLimit( $class )
  1687. {
  1688. // default version limit
  1689. $contentINI = eZINI::instance( 'content.ini' );
  1690. $versionLimit = $contentINI->variable( 'VersionManagement', 'DefaultVersionHistoryLimit' );
  1691. // version limit can't be < 2
  1692. if ( $versionLimit < 2 )
  1693. {
  1694. eZDebug::writeWarning( "Global version history limit must be equal to or higher than 2", __METHOD__ );
  1695. $versionLimit = 2;
  1696. }
  1697. // we need to take $class down to a class ID
  1698. if ( is_numeric( $class ) )
  1699. {
  1700. if (!eZContentClass::classIdentifierByID( $class ) )
  1701. {
  1702. eZDebug::writeWarning( "class integer parameter doesn't match any content class ID", __METHOD__ );
  1703. return $versionLimit;
  1704. }
  1705. $classID = (int)$class;
  1706. }
  1707. // literal identifier
  1708. elseif ( is_string( $class ) )
  1709. {
  1710. $classID = eZContentClass::classIDByIdentifier( $class );
  1711. if ( !$classID )
  1712. {
  1713. eZDebug::writeWarning( "class string parameter doesn't match any content class identifier", __METHOD__ );
  1714. return $versionLimit;
  1715. }
  1716. }
  1717. // eZContentClass object
  1718. elseif ( is_object( $class ) )
  1719. {
  1720. if ( !$class instanceof eZContentClass )
  1721. {
  1722. eZDebug::writeWarning( "class object parameter is not an eZContentClass", __METHOD__ );
  1723. return $versionLimit;
  1724. }
  1725. else
  1726. {
  1727. $classID = $class->attribute( 'id' );
  1728. }
  1729. }
  1730. $classLimitSetting = $contentINI->variable( 'VersionManagement', 'VersionHistoryClass' );
  1731. $classArray = array_keys( $classLimitSetting );
  1732. $limitsArray = array_values( $classLimitSetting );
  1733. $classArray = eZContentClass::classIDByIdentifier( $classArray );
  1734. foreach ( $classArray as $index => $id )
  1735. {
  1736. if ( $id == $classID )
  1737. {
  1738. $limit = $limitsArray[$index];
  1739. // version limit can't be < 2
  1740. if ( $limit < 2 )
  1741. {
  1742. $classIdentifier = eZContentClass::classIdentifierByID( $classID );
  1743. eZDebug::writeWarning( "Version history limit for class {$classIdentifier} must be equal to or higher than 2", __METHOD__ );
  1744. $limit = 2;
  1745. }
  1746. $versionLimit = $limit;
  1747. }
  1748. }
  1749. return $versionLimit;
  1750. }
  1751. /// \privatesection
  1752. public $ID;
  1753. // serialized array of translated class names
  1754. public $SerializedNameList;
  1755. // unserialized class names
  1756. public $NameList;
  1757. // unserialized class description
  1758. public $DescriptionList;
  1759. public $Identifier;
  1760. public $ContentObjectName;
  1761. public $Version;
  1762. public $VersionCount;
  1763. public $CreatorID;
  1764. public $ModifierID;
  1765. public $Created;
  1766. public $Modified;
  1767. public $InGroups;
  1768. public $AllGroups;
  1769. public $IsContainer;
  1770. public $CanInstantiateLanguages;
  1771. public $LanguageMask;
  1772. /**
  1773. * In-memory cache for class identifiers / id matching
  1774. */
  1775. private static $identifierHash = null;
  1776. }
  1777. ?>