|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZContentObject class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package kernel 00009 */ 00010 00011 /*! 00012 \class eZContentObject ezcontentobject.php 00013 \ingroup eZKernel 00014 \brief Handles eZ Publish content objects 00015 00016 It encapsulates the data for an object and provides lots of functions 00017 for dealing with versions, translations and attributes. 00018 00019 \sa eZContentClass 00020 */ 00021 00022 class eZContentObject extends eZPersistentObject 00023 { 00024 const STATUS_DRAFT = 0; 00025 const STATUS_PUBLISHED = 1; 00026 const STATUS_ARCHIVED = 2; 00027 00028 const PACKAGE_ERROR_NO_CLASS = 1; 00029 const PACKAGE_ERROR_EXISTS = 2; 00030 const PACKAGE_ERROR_NODE_EXISTS = 3; 00031 const PACKAGE_ERROR_MODIFIED = 101; 00032 const PACKAGE_ERROR_HAS_CHILDREN = 102; 00033 00034 const PACKAGE_REPLACE = 1; 00035 const PACKAGE_SKIP = 2; 00036 const PACKAGE_NEW = 3; 00037 const PACKAGE_UPDATE = 6; 00038 00039 const PACKAGE_DELETE = 4; 00040 const PACKAGE_KEEP = 5; 00041 00042 const RELATION_COMMON = 1; 00043 const RELATION_EMBED = 2; 00044 const RELATION_LINK = 4; 00045 const RELATION_ATTRIBUTE = 8; 00046 00047 function eZContentObject( $row ) 00048 { 00049 $this->eZPersistentObject( $row ); 00050 $this->ClassIdentifier = false; 00051 if ( isset( $row['contentclass_identifier'] ) ) 00052 $this->ClassIdentifier = $row['contentclass_identifier']; 00053 $this->ClassName = false; 00054 if ( isset( $row['contentclass_name'] ) ) 00055 $this->ClassName = $row['contentclass_name']; 00056 if ( isset( $row['serialized_name_list'] ) ) 00057 $this->ClassName = eZContentClass::nameFromSerializedString( $row['serialized_name_list'] ); 00058 00059 $this->CurrentLanguage = false; 00060 if ( isset( $row['content_translation'] ) ) 00061 { 00062 $this->CurrentLanguage = $row['content_translation']; 00063 } 00064 else if ( isset( $row['real_translation'] ) ) 00065 { 00066 $this->CurrentLanguage = $row['real_translation']; 00067 } 00068 else if ( isset( $row['language_mask'] ) ) 00069 { 00070 $topPriorityLanguage = eZContentLanguage::topPriorityLanguageByMask( $row['language_mask'] ); 00071 if ( $topPriorityLanguage ) 00072 { 00073 $this->CurrentLanguage = $topPriorityLanguage->attribute( 'locale' ); 00074 } 00075 } 00076 } 00077 00078 static function definition() 00079 { 00080 static $definition = array( "fields" => array( "id" => array( 'name' => 'ID', 00081 'datatype' => 'integer', 00082 'default' => 0, 00083 'required' => true ), 00084 "section_id" => array( 'name' => "SectionID", 00085 'datatype' => 'integer', 00086 'default' => 0, 00087 'required' => true, 00088 'foreign_class' => 'eZSection', 00089 'foreign_attribute' => 'id', 00090 'multiplicity' => '1..*' ), 00091 "owner_id" => array( 'name' => "OwnerID", 00092 'datatype' => 'integer', 00093 'default' => 0, 00094 'required' => true, 00095 'foreign_class' => 'eZUser', 00096 'foreign_attribute' => 'contentobject_id', 00097 'multiplicity' => '1..*'), 00098 "contentclass_id" => array( 'name' => "ClassID", 00099 'datatype' => 'integer', 00100 'default' => 0, 00101 'required' => true, 00102 'foreign_class' => 'eZContentClass', 00103 'foreign_attribute' => 'id', 00104 'multiplicity' => '1..*' ), 00105 "name" => array( 'name' => "Name", 00106 'datatype' => 'string', 00107 'default' => '', 00108 'required' => true ), 00109 "published" => array( 'name' => "Published", 00110 'datatype' => 'integer', 00111 'default' => 0, 00112 'required' => true ), 00113 "modified" => array( 'name' => "Modified", 00114 'datatype' => 'integer', 00115 'default' => 0, 00116 'required' => true ), 00117 "current_version" => array( 'name' => "CurrentVersion", 00118 'datatype' => 'integer', 00119 'default' => 0, 00120 'required' => true ), 00121 "status" => array( 'name' => "Status", 00122 'datatype' => 'integer', 00123 'default' => 0, 00124 'required' => true ), 00125 'remote_id' => array( 'name' => "RemoteID", 00126 'datatype' => 'string', 00127 'default' => '', 00128 'required' => true ), 00129 'language_mask' => array( 'name' => 'LanguageMask', 00130 'datatype' => 'integer', 00131 'default' => 0, 00132 'required' => true ), 00133 'initial_language_id' => array( 'name' => 'InitialLanguageID', 00134 'datatype' => 'integer', 00135 'default' => 0, 00136 'required' => true, 00137 'foreign_class' => 'eZContentLanguage', 00138 'foreign_attribute' => 'id', 00139 'multiplicity' => '1..*' ) ), 00140 "keys" => array( "id" ), 00141 "function_attributes" => array( "current" => "currentVersion", 00142 'versions' => 'versions', 00143 'author_array' => 'authorArray', 00144 "class_name" => "className", 00145 "content_class" => "contentClass", 00146 "contentobject_attributes" => "contentObjectAttributes", 00147 "owner" => "owner", 00148 "related_contentobject_array" => "relatedContentObjectList", 00149 "related_contentobject_count" => "relatedContentObjectCount", 00150 'reverse_related_contentobject_array' => 'reverseRelatedObjectList', 00151 'reverse_related_contentobject_count' => 'reverseRelatedObjectCount', 00152 "linked_contentobject_array" => "linkedContentObjectList", 00153 "linked_contentobject_count" => "linkedContentObjectCount", 00154 'reverse_linked_contentobject_array' => 'reverseLinkedObjectList', 00155 'reverse_linked_contentobject_count' => 'reverseLinkedObjectCount', 00156 "embedded_contentobject_array" => "embeddedContentObjectList", 00157 "embedded_contentobject_count" => "embeddedContentObjectCount", 00158 'reverse_embedded_contentobject_array' => 'reverseEmbeddedObjectList', 00159 'reverse_embedded_contentobject_count' => 'reverseEmbeddedObjectCount', 00160 "can_read" => "canRead", 00161 "can_pdf" => "canPdf", 00162 "can_diff" => "canDiff", 00163 "can_create" => "canCreate", 00164 "can_create_class_list" => "canCreateClassList", 00165 "can_edit" => "canEdit", 00166 "can_translate" => "canTranslate", 00167 "can_remove" => "canRemove", 00168 "can_move" => "canMoveFrom", 00169 "can_move_from" => "canMoveFrom", 00170 'can_view_embed' => 'canViewEmbed', 00171 "data_map" => "dataMap", 00172 "grouped_data_map" => "groupedDataMap", 00173 "main_parent_node_id" => "mainParentNodeID", 00174 "assigned_nodes" => "assignedNodes", 00175 "parent_nodes" => "parentNodeIDArray", 00176 "main_node_id" => "mainNodeID", 00177 "main_node" => "mainNode", 00178 "default_language" => "defaultLanguage", 00179 "content_action_list" => "contentActionList", 00180 "class_identifier" => "contentClassIdentifier", 00181 'class_group_id_list' => 'contentClassGroupIDList', 00182 'name' => 'name', 00183 'match_ingroup_id_list' => 'matchIngroupIDList', 00184 'remote_id' => 'remoteID', 00185 'current_language' => 'currentLanguage', 00186 'current_language_object' => 'currentLanguageObject', 00187 'initial_language' => 'initialLanguage', 00188 'initial_language_code' => 'initialLanguageCode', 00189 'available_languages' => 'availableLanguages', 00190 'language_codes' => 'availableLanguages', 00191 'language_js_array' => 'availableLanguagesJsArray', 00192 'languages' => 'languages', 00193 'all_languages' => 'allLanguages', 00194 'can_edit_languages' => 'canEditLanguages', 00195 'can_create_languages' => 'canCreateLanguages', 00196 'always_available' => 'isAlwaysAvailable', 00197 'allowed_assign_section_list' => 'allowedAssignSectionList', 00198 'allowed_assign_state_id_list' => 'allowedAssignStateIDList', 00199 'allowed_assign_state_list' => 'allowedAssignStateList', 00200 'state_id_array' => 'stateIDArray', 00201 'state_identifier_array' => 'stateIdentifierArray' ), 00202 "increment_key" => "id", 00203 "class_name" => "eZContentObject", 00204 "sort" => array( "id" => "asc" ), 00205 "name" => "ezcontentobject" ); 00206 return $definition; 00207 } 00208 00209 /*! 00210 Get class groups this object's class belongs to if match for class groups is enabled. 00211 00212 \return array of class group ids. False if match is disabled. 00213 */ 00214 function matchIngroupIDList() 00215 { 00216 $contentINI = eZINI::instance( 'content.ini' ); 00217 $inList = false; 00218 if( $contentINI->variable( 'ContentOverrideSettings', 'EnableClassGroupOverride' ) == 'true' ) 00219 { 00220 $contentClass = $this->contentClass(); 00221 $inList = $contentClass->attribute( 'ingroup_id_list' ); 00222 } 00223 return $inList; 00224 } 00225 00226 /*! 00227 Store the object 00228 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 00229 the calls within a db transaction; thus within db->begin and db->commit. 00230 */ 00231 function store( $fieldFilters = null ) 00232 { 00233 // Unset the cache 00234 global $eZContentObjectContentObjectCache; 00235 unset( $eZContentObjectContentObjectCache[$this->ID] ); 00236 global $eZContentObjectDataMapCache; 00237 unset( $eZContentObjectDataMapCache[$this->ID] ); 00238 global $eZContentObjectVersionCache; 00239 unset( $eZContentObjectVersionCache[$this->ID] ); 00240 00241 $db = eZDB::instance(); 00242 $db->begin(); 00243 $this->storeNodeModified(); 00244 eZPersistentObject::store( $fieldFilters ); 00245 $db->commit(); 00246 } 00247 00248 /*! 00249 Clear in-memory caches. 00250 \param $idArray objects to clear caches for. 00251 00252 If the parameter is ommitted the caches are cleared for all objects. 00253 */ 00254 00255 static function clearCache( $idArray = array() ) 00256 { 00257 if ( is_numeric( $idArray ) ) 00258 $idArray = array( $idArray ); 00259 00260 // clear in-memory cache for all objects 00261 if ( count( $idArray ) == 0 ) 00262 { 00263 unset( $GLOBALS['eZContentObjectContentObjectCache'] ); 00264 unset( $GLOBALS['eZContentObjectDataMapCache'] ); 00265 unset( $GLOBALS['eZContentObjectVersionCache'] ); 00266 00267 return; 00268 } 00269 00270 // clear in-memory cache for specified object(s) 00271 global $eZContentObjectContentObjectCache; 00272 global $eZContentObjectDataMapCache; 00273 global $eZContentObjectVersionCache; 00274 foreach ( $idArray as $objectID ) 00275 { 00276 unset( $eZContentObjectContentObjectCache[$objectID] ); 00277 unset( $eZContentObjectDataMapCache[$objectID] ); 00278 unset( $eZContentObjectVersionCache[$objectID] ); 00279 } 00280 } 00281 00282 /*! 00283 Update all nodes to set modified_subnode value 00284 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 00285 the calls within a db transaction; thus within db->begin and db->commit. 00286 */ 00287 function storeNodeModified() 00288 { 00289 if ( is_numeric( $this->ID ) ) 00290 { 00291 $nodeArray = $this->assignedNodes(); 00292 00293 $db = eZDB::instance(); 00294 $db->begin(); 00295 foreach ( array_keys( $nodeArray ) as $key ) 00296 { 00297 $nodeArray[$key]->updateAndStoreModified(); 00298 } 00299 $db->commit(); 00300 } 00301 } 00302 00303 function name( $version = false , $lang = false ) 00304 { 00305 // if the object id is null, we can't read data from the database 00306 // and return the locally known name 00307 if ( $this->attribute( 'id' ) === null ) 00308 { 00309 return $this->Name; 00310 } 00311 if ( !$version ) 00312 { 00313 $version = $this->attribute( 'current_version' ); 00314 } 00315 if ( !$lang && $this->CurrentLanguage ) 00316 { 00317 $lang = $this->CurrentLanguage; 00318 } 00319 00320 return $this->versionLanguageName( $version, $lang ); 00321 } 00322 00323 function names() 00324 { 00325 $version = $this->attribute( 'current_version' ); 00326 $id = $this->attribute( 'id' ); 00327 00328 $db = eZDB::instance(); 00329 $rows = $db->arrayQuery( "SELECT name, real_translation FROM ezcontentobject_name WHERE contentobject_id = '$id' AND content_version='$version'" ); 00330 $names = array(); 00331 foreach ( $rows as $row ) 00332 { 00333 $names[$row['real_translation']] = $row['name']; 00334 } 00335 00336 return $names; 00337 } 00338 00339 function versionLanguageName( $version, $lang = false ) 00340 { 00341 $name = false; 00342 if ( !$version > 0 ) 00343 { 00344 eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ ); 00345 return $name; 00346 } 00347 $db = eZDb::instance(); 00348 $contentObjectID = $this->attribute( 'id' ); 00349 if ( !$lang ) 00350 { 00351 // If $lang not given we will use the initial language of the object 00352 $query = "SELECT initial_language_id FROM ezcontentobject WHERE id='$contentObjectID'"; 00353 $rows = $db->arrayQuery( $query ); 00354 if ( $rows ) 00355 { 00356 $languageID = $rows[0]['initial_language_id']; 00357 $language = eZContentLanguage::fetch( $languageID ); 00358 if ( $language ) 00359 { 00360 $lang = $language->attribute( 'locale' ); 00361 } 00362 else 00363 { 00364 return $name; 00365 } 00366 } 00367 else 00368 { 00369 return $name; 00370 } 00371 } 00372 $lang = $db->escapeString( $lang ); 00373 $version = (int) $version; 00374 00375 $languageID = $this->attribute( 'initial_language_id' ); 00376 if ( $this->attribute( 'always_available' ) ) 00377 { 00378 $languageID = (int) $languageID | 1; 00379 } 00380 00381 $query= "SELECT name, content_translation 00382 FROM ezcontentobject_name 00383 WHERE contentobject_id = '$contentObjectID' 00384 AND content_version = '$version' 00385 AND ( content_translation = '$lang' OR language_id = '$languageID' )"; 00386 $result = $db->arrayQuery( $query ); 00387 00388 $resCount = count( $result ); 00389 if( $resCount < 1 ) 00390 { 00391 eZDebug::writeNotice( "There is no object name for version($version) of the content object ($contentObjectID) in language($lang)", __METHOD__ ); 00392 } 00393 else if( $resCount > 1 ) 00394 { 00395 // we have name in requested language => find and return it 00396 foreach( $result as $row ) 00397 { 00398 if( $row['content_translation'] == $lang ) 00399 { 00400 $name = $row['name']; 00401 break; 00402 } 00403 } 00404 } 00405 else 00406 { 00407 // we don't have name in requested language(or requested language is the same as initial language) => use name in initial language 00408 $name = $result[0]['name']; 00409 } 00410 00411 return $name; 00412 } 00413 00414 /*! 00415 Sets the name of the object, in memory only. Use setName() to change it. 00416 */ 00417 function setCachedName( $name ) 00418 { 00419 $this->Name = $name; 00420 } 00421 00422 /*! 00423 Sets the name of the object in all translations. 00424 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 00425 the calls within a db transaction; thus within db->begin and db->commit. 00426 */ 00427 function setName( $objectName, $versionNum = false, $languageCode = false ) 00428 { 00429 $initialLanguageCode = false; 00430 if ( $initialLanguage = $this->initialLanguage() ) 00431 { 00432 $initialLanguageCode = $initialLanguage->attribute( 'locale' ); 00433 } 00434 $db = eZDB::instance(); 00435 00436 if ( $languageCode == false ) 00437 { 00438 $languageCode = $initialLanguageCode; 00439 } 00440 $languageCode = $db->escapeString( $languageCode ); 00441 if ( $languageCode == $initialLanguageCode ) 00442 { 00443 $this->Name = $objectName; 00444 } 00445 00446 if ( !$versionNum ) 00447 { 00448 $versionNum = $this->attribute( 'current_version' ); 00449 } 00450 $objectID =(int) $this->attribute( 'id' ); 00451 $versionNum =(int) $versionNum; 00452 00453 $languageID =(int) eZContentLanguage::idByLocale( $languageCode ); 00454 00455 $objectName = $db->escapeString( $objectName ); 00456 00457 $db->begin(); 00458 00459 // Check if name is already set before setting/changing it. 00460 // This helps to avoid deadlocks in mysql: a pair of DELETE/INSERT might cause deadlock here 00461 // in case of concurrent execution. 00462 $rows = $db->arrayQuery( "SELECT COUNT(*) AS count FROM ezcontentobject_name WHERE contentobject_id = '$objectID' 00463 AND content_version = '$versionNum' AND content_translation ='$languageCode'" ); 00464 if ( $rows[0]['count'] ) 00465 { 00466 $db->query( "UPDATE ezcontentobject_name SET name='$objectName' 00467 WHERE 00468 contentobject_id = '$objectID' AND 00469 content_version = '$versionNum' AND 00470 content_translation ='$languageCode'" ); 00471 } 00472 else 00473 { 00474 $db->query( "INSERT INTO ezcontentobject_name( contentobject_id, 00475 name, 00476 content_version, 00477 content_translation, 00478 real_translation, 00479 language_id ) 00480 VALUES( '$objectID', 00481 '$objectName', 00482 '$versionNum', 00483 '$languageCode', 00484 '$languageCode', 00485 '$languageID' )" ); 00486 } 00487 00488 $db->commit(); 00489 } 00490 00491 /*! 00492 \return a map with all the content object attributes where the keys are the 00493 attribute identifiers. 00494 */ 00495 function dataMap() 00496 { 00497 return $this->fetchDataMap(); 00498 } 00499 00500 /** 00501 * Generates a map with all the content object attributes where the keys are 00502 * the attribute identifiers grouped by class attribute category. 00503 * 00504 * @note Result is not cached, so make sure you don't call this over and over. 00505 * 00506 * @return array 00507 */ 00508 public function groupedDataMap() 00509 { 00510 return self::createGroupedDataMap( $this->fetchDataMap() ); 00511 } 00512 00513 /** 00514 * Generates a map with all the content object attributes where the keys are 00515 * the attribute identifiers grouped by class attribute category. 00516 * 00517 * @note Result is not cached, so make sure you don't call this over and over. 00518 * 00519 * @param array $contentObjectAttributes Array of eZContentObjectAttribute objects 00520 * @return array 00521 */ 00522 public static function createGroupedDataMap( $contentObjectAttributes ) 00523 { 00524 $groupedDataMap = array(); 00525 $contentINI = eZINI::instance( 'content.ini' ); 00526 $categorys = $contentINI->variable( 'ClassAttributeSettings', 'CategoryList' ); 00527 $defaultCategory = $contentINI->variable( 'ClassAttributeSettings', 'DefaultCategory' ); 00528 foreach( $contentObjectAttributes as $attribute ) 00529 { 00530 $classAttribute = $attribute->contentClassAttribute(); 00531 $attributeCategory = $classAttribute->attribute('category'); 00532 $attributeIdentifier = $classAttribute->attribute( 'identifier' ); 00533 if ( !isset( $categorys[ $attributeCategory ] ) || !$attributeCategory ) 00534 $attributeCategory = $defaultCategory; 00535 00536 if ( !isset( $groupedDataMap[ $attributeCategory ] ) ) 00537 $groupedDataMap[ $attributeCategory ] = array(); 00538 00539 $groupedDataMap[ $attributeCategory ][$attributeIdentifier] = $attribute; 00540 00541 } 00542 return $groupedDataMap; 00543 } 00544 00545 /*! 00546 \return a map with all the content object attributes where the keys are the 00547 attribute identifiers. 00548 \sa eZContentObjectTreeNode::dataMap 00549 */ 00550 function fetchDataMap( $version = false, $language = false ) 00551 { 00552 // Global variable to cache datamaps 00553 global $eZContentObjectDataMapCache; 00554 00555 if ( $version == false ) 00556 $version = $this->attribute( 'current_version' ); 00557 00558 if ( $language == false ) 00559 { 00560 $language = $this->CurrentLanguage; 00561 } 00562 00563 if ( !$language || !isset( $eZContentObjectDataMapCache[$this->ID][$version][$language] ) ) 00564 { 00565 $data = $this->contentObjectAttributes( true, $version, $language ); 00566 00567 if ( !$language ) 00568 { 00569 $language = $this->CurrentLanguage; 00570 } 00571 00572 // Store the attributes for later use 00573 $this->ContentObjectAttributeArray[$version][$language] = $data; 00574 $eZContentObjectDataMapCache[$this->ID][$version][$language] = $data; 00575 } 00576 else 00577 { 00578 $data = $eZContentObjectDataMapCache[$this->ID][$version][$language]; 00579 } 00580 00581 if ( !isset( $this->DataMap[$version][$language] ) ) 00582 { 00583 $ret = array(); 00584 foreach( $data as $key => $item ) 00585 { 00586 $identifier = $item->contentClassAttributeIdentifier(); 00587 $ret[$identifier] = $data[$key]; 00588 } 00589 $this->DataMap[$version][$language] = $ret; 00590 } 00591 else 00592 { 00593 $ret = $this->DataMap[$version][$language]; 00594 } 00595 return $ret; 00596 } 00597 00598 function resetDataMap() 00599 { 00600 $this->ContentObjectAttributeArray = array(); 00601 $this->ContentObjectAttributes = array(); 00602 $this->DataMap = array(); 00603 return $this->DataMap; 00604 } 00605 00606 /*! 00607 Fetch a set of content object attributes by their class identifiers. 00608 */ 00609 function fetchAttributesByIdentifier( $identifierArray, $version = false, $languageArray = false, $asObject = true ) 00610 { 00611 if ( count( $identifierArray ) === 0 ) 00612 { 00613 return null; 00614 } 00615 00616 $db = eZDB::instance(); 00617 00618 $identifierQuotedString = array(); 00619 foreach ( $identifierArray as $identifier ) 00620 { 00621 $identifierQuotedString[] = "'$identifier'"; 00622 } 00623 00624 if ( !$version or !is_numeric( $version ) ) 00625 { 00626 $version = $this->CurrentVersion; 00627 } 00628 00629 if ( is_array( $languageArray ) ) 00630 { 00631 $langCodeQuotedString = array(); 00632 foreach ( $languageArray as $langCode ) 00633 { 00634 if ( is_string( $langCode ) ) 00635 $langCodeQuotedString[] = "'$langCode'"; 00636 } 00637 00638 if ( !empty( $langCodeQuotedString ) ) 00639 { 00640 $languageText = "AND "; 00641 $languageText .= $db->generateSQLINStatement( $langCodeQuotedString, 'ezcontentobject_attribute.language_code' ); 00642 } 00643 } 00644 00645 if ( !isset( $languageText ) ) 00646 { 00647 $languageText = "AND " . eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' ); 00648 } 00649 00650 $versionText = "AND ezcontentobject_attribute.version = '$version'"; 00651 00652 $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier 00653 FROM ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version 00654 WHERE 00655 ezcontentclass_attribute.version = ". eZContentClass::VERSION_STATUS_DEFINED . " AND 00656 ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND 00657 ezcontentobject_version.contentobject_id = {$this->ID} AND 00658 ezcontentobject_version.version = {$version} AND 00659 ezcontentobject_attribute.contentobject_id = {$this->ID} 00660 00661 {$languageText} 00662 00663 {$versionText} 00664 00665 AND 00666 "; 00667 00668 $query .= $db->generateSQLINStatement( $identifierQuotedString, 'identifier' ); 00669 00670 $rows = $db->arrayQuery( $query ); 00671 00672 if ( count( $rows ) > 0 ) 00673 { 00674 if ( $asObject ) 00675 { 00676 $returnArray = array(); 00677 foreach( $rows as $row ) 00678 { 00679 $returnArray[$row['id']] = new eZContentObjectAttribute( $row ); 00680 } 00681 return $returnArray; 00682 } 00683 else 00684 { 00685 return $rows; 00686 } 00687 } 00688 return null; 00689 } 00690 00691 /*! 00692 Returns the owner of the object as a content object. 00693 */ 00694 function owner() 00695 { 00696 if ( $this->OwnerID != 0 ) 00697 { 00698 return eZContentObject::fetch( $this->OwnerID ); 00699 } 00700 return null; 00701 } 00702 00703 /*! 00704 \return the content class group identifiers for the current content object 00705 */ 00706 function contentClassGroupIDList() 00707 { 00708 $contentClass = $this->contentClass(); 00709 return $contentClass->attribute( 'ingroup_id_list' ); 00710 } 00711 00712 /*! 00713 \return the content class identifer for the current content object 00714 00715 \note The object will cache the class name information so multiple calls will be fast. 00716 */ 00717 function contentClassIdentifier() 00718 { 00719 if ( !is_numeric( $this->ClassID ) ) 00720 { 00721 $retValue = null; 00722 return $retValue; 00723 } 00724 00725 if ( $this->ClassIdentifier !== false ) 00726 return $this->ClassIdentifier; 00727 00728 $this->ClassIdentifier = eZContentClass::classIdentifierByID( $this->ClassID ); 00729 00730 return $this->ClassIdentifier; 00731 } 00732 00733 /*! 00734 \return the content class for the current content object 00735 */ 00736 function contentClass() 00737 { 00738 if ( !is_numeric( $this->ClassID ) ) 00739 { 00740 $retValue = null; 00741 return $retValue; 00742 } 00743 00744 return eZContentClass::fetch( $this->ClassID ); 00745 } 00746 00747 /*! 00748 Get remote id of content object 00749 */ 00750 function remoteID() 00751 { 00752 $remoteID = eZPersistentObject::attribute( 'remote_id', true ); 00753 00754 // Ensures that we provide the correct remote_id if we have one in the database 00755 if ( $remoteID === null and $this->attribute( 'id' ) ) 00756 { 00757 $db = eZDB::instance(); 00758 $resultArray = $db->arrayQuery( "SELECT remote_id FROM ezcontentobject WHERE id = '" . $this->attribute( 'id' ) . "'" ); 00759 if ( count( $resultArray ) == 1 ) 00760 { 00761 $remoteID = $resultArray[0]['remote_id']; 00762 $this->setAttribute( 'remote_id', $remoteID ); 00763 } 00764 } 00765 00766 if ( !$remoteID ) 00767 { 00768 $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) ); 00769 if ( $this->attribute( 'id' ) !== null ) 00770 $this->sync( array( 'remote_id' ) ); 00771 $remoteID = eZPersistentObject::attribute( 'remote_id', true ); 00772 } 00773 00774 return $remoteID; 00775 } 00776 00777 function mainParentNodeID() 00778 { 00779 $list = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID, false, true ); 00780 return isset( $list[0] ) ? $list[0] : null; 00781 } 00782 00783 /*! 00784 Fetches contentobject by remote ID, returns null if none exist 00785 */ 00786 static function fetchByRemoteID( $remoteID, $asObject = true ) 00787 { 00788 $db = eZDB::instance(); 00789 $remoteID =$db->escapeString( $remoteID ); 00790 $resultArray = $db->arrayQuery( 'SELECT id FROM ezcontentobject WHERE remote_id=\'' . $remoteID . '\'' ); 00791 if ( count( $resultArray ) != 1 ) 00792 $object = null; 00793 else 00794 $object = eZContentObject::fetch( $resultArray[0]['id'], $asObject ); 00795 return $object; 00796 } 00797 00798 /** 00799 * Fetches a content object by ID 00800 * @param int $id ID of the content object to fetch 00801 * @param bool $asObject 00802 * Return the result as an object (true) or an assoc. array (false) 00803 * 00804 * @return eZContentObject 00805 */ 00806 static function fetch( $id, $asObject = true ) 00807 { 00808 global $eZContentObjectContentObjectCache; 00809 00810 // If the object given by its id is not cached or should be returned as array 00811 // then we fetch it from the DB (objects are always cached as arrays). 00812 if ( !isset( $eZContentObjectContentObjectCache[$id] ) or $asObject === false ) 00813 { 00814 $db = eZDB::instance(); 00815 00816 $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) ); 00817 00818 $objectArray = array(); 00819 if ( count( $resArray ) == 1 && $resArray !== false ) 00820 { 00821 $objectArray = $resArray[0]; 00822 } 00823 else 00824 { 00825 eZDebug::writeError( "Object not found ($id)", __METHOD__ ); 00826 $retValue = null; 00827 return $retValue; 00828 } 00829 00830 if ( $asObject ) 00831 { 00832 $obj = new eZContentObject( $objectArray ); 00833 $eZContentObjectContentObjectCache[$id] = $obj; 00834 } 00835 else 00836 { 00837 return $objectArray; 00838 } 00839 00840 return $obj; 00841 } 00842 else 00843 { 00844 return $eZContentObjectContentObjectCache[$id]; 00845 } 00846 } 00847 00848 /*! 00849 \static 00850 Tests for the existance of a content object by using the ID \a $id. 00851 \return \c true if the object exists, \c false otherwise. 00852 \note Uses the static function createFetchSQLString() to generate the SQL 00853 */ 00854 static function exists( $id ) 00855 { 00856 global $eZContentObjectContentObjectCache; 00857 00858 // Check the global cache 00859 if ( isset( $eZContentObjectContentObjectCache[$id] ) ) 00860 return true; 00861 00862 // If the object is not cached we need to check the DB 00863 $db = eZDB::instance(); 00864 00865 $resArray = $db->arrayQuery( eZContentObject::createFetchSQLString( $id ) ); 00866 00867 if ( $resArray !== false and count( $resArray ) == 1 ) 00868 { 00869 return true; 00870 } 00871 00872 return false; 00873 00874 } 00875 00876 /*! 00877 \static 00878 Creates the SQL for fetching the object with ID \a $id and returns the string. 00879 */ 00880 static function createFetchSQLString( $id ) 00881 { 00882 $id = (int) $id; 00883 00884 $fetchSQLString = "SELECT ezcontentobject.*, 00885 ezcontentclass.serialized_name_list as serialized_name_list, 00886 ezcontentclass.identifier as contentclass_identifier, 00887 ezcontentclass.is_container as is_container 00888 FROM 00889 ezcontentobject, 00890 ezcontentclass 00891 WHERE 00892 ezcontentobject.id='$id' AND 00893 ezcontentclass.id = ezcontentobject.contentclass_id AND 00894 ezcontentclass.version=0"; 00895 00896 return $fetchSQLString; 00897 } 00898 00899 /** 00900 * Creates the SQL for filtering objects by visibility, used by IgnoreVisibility on some fetches. 00901 * The object is visible if 1 or more assigned nodes are visible. 00902 * 00903 * @static 00904 * @since Version 4.1 00905 * @param bool $IgnoreVisibility ignores visibility if true 00906 * @param string $ezcontentobjectTable name of ezcontentobject table used in sql 00907 * @return string with sql condition for node filtering by visibility 00908 */ 00909 static function createFilterByVisibilitySQLString( $IgnoreVisibility = false, $ezcontentobjectTable = 'ezcontentobject' ) 00910 { 00911 if ( $IgnoreVisibility ) 00912 return ''; 00913 return " AND ( SELECT MIN( ezct.is_invisible ) FROM ezcontentobject_tree ezct WHERE ezct.contentobject_id = $ezcontentobjectTable.id ) = 0 "; 00914 } 00915 00916 /** 00917 * Fetches the contentobject which has a node with ID $nodeID 00918 * $nodeID can also be an array of NodeIDs. In this case, an array of content objects will be returned 00919 * @param int|array $nodeID Single nodeID or array of NodeIDs 00920 * @param bool $asObject If results have to be returned as eZContentObject instances or not 00921 * @return mixed Content object or array of content objects. 00922 * Content objects can be eZContentObject instances or array result sets 00923 */ 00924 static function fetchByNodeID( $nodeID, $asObject = true ) 00925 { 00926 global $eZContentObjectContentObjectCache; 00927 $resultAsArray = is_array( $nodeID ); 00928 $nodeID = (array)$nodeID; 00929 00930 $db = eZDB::instance(); 00931 00932 $resArray = $db->arrayQuery( 00933 "SELECT co.*, con.name as name, con.real_translation, cot.node_id 00934 FROM ezcontentobject co 00935 JOIN ezcontentobject_tree cot ON co.id = cot.contentobject_id AND co.current_version = cot.contentobject_version 00936 JOIN ezcontentobject_name con ON co.id = con.contentobject_id AND co.current_version = con.content_version 00937 WHERE " . 00938 $db->generateSQLINStatement( $nodeID, 'cot.node_id', false, true, 'int' ) . " AND " . 00939 eZContentLanguage::sqlFilter( 'con', 'co' ) 00940 ); 00941 00942 if ( $resArray === false || empty( $resArray ) ) 00943 { 00944 eZDebug::writeError( 'A problem occured while fetching objects with following NodeIDs : ' . implode( ', ', $nodeID ), __METHOD__ ); 00945 return $resultAsArray ? array() : null; 00946 } 00947 00948 $objectArray = array(); 00949 if ( $asObject ) 00950 { 00951 foreach ( $resArray as $res ) 00952 { 00953 $objectArray[$res['node_id']] = $eZContentObjectContentObjectCache[$res['id']] = new self( $res ); 00954 } 00955 } 00956 else 00957 { 00958 foreach ( $resArray as $res ) 00959 { 00960 $objectArray[$res['node_id']] = $res; 00961 } 00962 } 00963 00964 if ( !$resultAsArray ) 00965 return $objectArray[$res['node_id']]; 00966 00967 return $objectArray; 00968 } 00969 00970 /** 00971 * Fetches a content object list based on an array of content object ids 00972 * 00973 * @param array $idArray array of content object ids 00974 * @param bool $asObject 00975 * Wether to get the result as an array of eZContentObject or an 00976 * array of associative arrays 00977 * 00978 * @return array(contentObjectID => eZContentObject|array) 00979 * array of eZContentObject (if $asObject = true) or array of 00980 * associative arrays (if $asObject = false) 00981 */ 00982 static function fetchIDArray( $idArray, $asObject = true ) 00983 { 00984 global $eZContentObjectContentObjectCache; 00985 00986 $db = eZDB::instance(); 00987 00988 $resRowArray = $db->arrayQuery( 00989 "SELECT ezcontentclass.serialized_name_list as class_serialized_name_list, ezcontentobject.*, ezcontentobject_name.name as name, ezcontentobject_name.real_translation 00990 FROM 00991 ezcontentclass, 00992 ezcontentobject, 00993 ezcontentobject_name 00994 WHERE 00995 ezcontentclass.id=ezcontentobject.contentclass_id AND " . 00996 // All elements from $idArray should be casted to (int) 00997 $db->generateSQLINStatement( $idArray, 'ezcontentobject.id', false, true, 'int' ) . " AND 00998 ezcontentobject.id = ezcontentobject_name.contentobject_id AND 00999 ezcontentobject.current_version = ezcontentobject_name.content_version AND " . 01000 eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ) 01001 ); 01002 01003 $objectRetArray = array(); 01004 foreach ( $resRowArray as $resRow ) 01005 { 01006 $objectID = $resRow['id']; 01007 $resRow['class_name'] = eZContentClass::nameFromSerializedString( $resRow['class_serialized_name_list'] ); 01008 if ( $asObject ) 01009 { 01010 $obj = new eZContentObject( $resRow ); 01011 $obj->ClassName = $resRow['class_name']; 01012 $eZContentObjectContentObjectCache[$objectID] = $obj; 01013 $objectRetArray[$objectID] = $obj; 01014 } 01015 else 01016 { 01017 $objectRetArray[$objectID] = $resRow; 01018 } 01019 } 01020 return $objectRetArray; 01021 } 01022 01023 /*! 01024 \return An array with content objects. 01025 \param $asObject Whether to return objects or not 01026 \param $conditions Optional conditions to limit the fetch, set to \c null to skip it. 01027 \param $offset Where to start fetch from, set to \c false to skip it. 01028 \param $limit Maximum number of objects to fetch, set \c false to skip it. 01029 \sa fetchListCount 01030 */ 01031 static function fetchList( $asObject = true, $conditions = null, $offset = false, $limit = false ) 01032 { 01033 $limitation = null; 01034 if ( $offset !== false or 01035 $limit !== false ) 01036 $limitation = array( 'offset' => $offset, 01037 'length' => $limit ); 01038 return eZPersistentObject::fetchObjectList( eZContentObject::definition(), 01039 null, 01040 $conditions, null, $limitation, 01041 $asObject ); 01042 } 01043 01044 static function fetchFilteredList( $conditions = null, $offset = false, $limit = false, $asObject = true ) 01045 { 01046 $limits = null; 01047 if ( $offset or $limit ) 01048 $limits = array( 'offset' => $offset, 01049 'length' => $limit ); 01050 return eZPersistentObject::fetchObjectList( eZContentObject::definition(), 01051 null, 01052 $conditions, null, $limits, 01053 $asObject ); 01054 } 01055 01056 /*! 01057 \return The number of objects in the database. Optionally \a $conditions can be used to limit the list count. 01058 \sa fetchList 01059 */ 01060 static function fetchListCount( $conditions = null ) 01061 { 01062 $rows = eZPersistentObject::fetchObjectList( eZContentObject::definition(), 01063 array(), 01064 $conditions, 01065 false/* we don't want any sorting when counting. Sorting leads to error on postgresql 8.x */, 01066 null, 01067 false, false, 01068 array( array( 'operation' => 'count( * )', 01069 'name' => 'count' ) ) ); 01070 return $rows[0]['count']; 01071 } 01072 01073 static function fetchSameClassList( $contentClassID, $asObject = true, $offset = false, $limit = false ) 01074 { 01075 $conditions = array( 'contentclass_id' => $contentClassID ); 01076 return eZContentObject::fetchFilteredList( $conditions, $offset, $limit, $asObject ); 01077 } 01078 01079 static function fetchSameClassListCount( $contentClassID ) 01080 { 01081 $result = eZPersistentObject::fetchObjectList( eZContentObject::definition(), 01082 array(), 01083 array( "contentclass_id" => $contentClassID ), 01084 false, null, 01085 false, false, 01086 array( array( 'operation' => 'count( * )', 01087 'name' => 'count' ) ) ); 01088 return $result[0]['count']; 01089 } 01090 01091 /*! 01092 Returns the current version of this document. 01093 */ 01094 function currentVersion( $asObject = true ) 01095 { 01096 return eZContentObjectVersion::fetchVersion( $this->attribute( "current_version" ), $this->ID, $asObject ); 01097 } 01098 01099 /*! 01100 Returns the given object version. False is returned if the versions does not exist. 01101 */ 01102 function version( $version, $asObject = true ) 01103 { 01104 if ( $asObject ) 01105 { 01106 global $eZContentObjectVersionCache; 01107 01108 if ( !isset( $eZContentObjectVersionCache ) ) // prevent PHP warning below 01109 $eZContentObjectVersionCache = array(); 01110 01111 if ( isset( $eZContentObjectVersionCache[$this->ID][$version] ) ) 01112 { 01113 return $eZContentObjectVersionCache[$this->ID][$version]; 01114 } 01115 else 01116 { 01117 $eZContentObjectVersionCache[$this->ID][$version] = eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject ); 01118 return $eZContentObjectVersionCache[$this->ID][$version]; 01119 } 01120 } 01121 else 01122 { 01123 return eZContentObjectVersion::fetchVersion( $version, $this->ID, $asObject ); 01124 } 01125 } 01126 01127 /** 01128 * Returns an array of eZContentObjectVersion for the current object 01129 * according to the conditions in $parameters. 01130 * 01131 * @param boolean $asObject 01132 * @param array $parameters 01133 * @return array 01134 */ 01135 function versions( $asObject = true, $parameters = array() ) 01136 { 01137 $conditions = array( "contentobject_id" => $this->ID ); 01138 if ( isset( $parameters['conditions'] ) ) 01139 { 01140 if ( isset( $parameters['conditions']['status'] ) ) 01141 $conditions['status'] = $parameters['conditions']['status']; 01142 if ( isset( $parameters['conditions']['creator_id'] ) ) 01143 $conditions['creator_id'] = $parameters['conditions']['creator_id']; 01144 if ( isset( $parameters['conditions']['language_code'] ) ) 01145 { 01146 $conditions['initial_language_id'] = eZContentLanguage::idByLocale( $parameters['conditions']['language_code'] ); 01147 } 01148 if ( isset( $parameters['conditions']['initial_language_id'] ) ) 01149 { 01150 $conditions['initial_language_id'] = $parameters['conditions']['initial_language_id']; 01151 } 01152 } 01153 $sort = isset( $parameters['sort'] ) ? $parameters['sort'] : null; 01154 $limit = isset( $parameters['limit'] ) ? $parameters['limit'] : null; 01155 01156 return eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(), 01157 null, $conditions, 01158 $sort, $limit, 01159 $asObject ); 01160 } 01161 01162 /*! 01163 \return \c true if the object has any versions remaining. 01164 */ 01165 function hasRemainingVersions() 01166 { 01167 $remainingVersions = $this->versions( false ); 01168 if ( !is_array( $remainingVersions ) or 01169 count( $remainingVersions ) == 0 ) 01170 { 01171 return false; 01172 } 01173 return true; 01174 } 01175 01176 function createInitialVersion( $userID, $initialLanguageCode = false ) 01177 { 01178 return eZContentObjectVersion::create( $this->attribute( "id" ), $userID, 1, $initialLanguageCode ); 01179 } 01180 01181 function createNewVersionIn( $languageCode, $copyFromLanguageCode = false, $copyFromVersion = false, $versionCheck = true, $status = eZContentObjectVersion::STATUS_DRAFT ) 01182 { 01183 return $this->createNewVersion( $copyFromVersion, $versionCheck, $languageCode, $copyFromLanguageCode, $status ); 01184 } 01185 01186 /*! 01187 Creates a new version and returns it as an eZContentObjectVersion object. 01188 If version number is given as argument that version is used to create a copy. 01189 \param $versionCheck If \c true it will check if there are too many version and 01190 remove some of them to make room for a new. 01191 01192 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01193 the calls within a db transaction; thus within db->begin and db->commit. 01194 */ 01195 function createNewVersion( $copyFromVersion = false, $versionCheck = true, $languageCode = false, $copyFromLanguageCode = false, $status = eZContentObjectVersion::STATUS_DRAFT ) 01196 { 01197 $db = eZDB::instance(); 01198 $db->begin(); 01199 // Check if we have enough space in version list 01200 if ( $versionCheck ) 01201 { 01202 $versionlimit = eZContentClass::versionHistoryLimit( $this->attribute( 'contentclass_id' ) ); 01203 $versionCount = $this->getVersionCount(); 01204 if ( $versionCount >= $versionlimit ) 01205 { 01206 // Remove oldest archived version 01207 $params = array( 'conditions'=> array( 'status' => eZContentObjectVersion::STATUS_ARCHIVED ) ); 01208 $versions = $this->versions( true, $params ); 01209 if ( count( $versions ) > 0 ) 01210 { 01211 $modified = $versions[0]->attribute( 'modified' ); 01212 $removeVersion = $versions[0]; 01213 foreach ( $versions as $version ) 01214 { 01215 $currentModified = $version->attribute( 'modified' ); 01216 if ( $currentModified < $modified ) 01217 { 01218 $modified = $currentModified; 01219 $removeVersion = $version; 01220 } 01221 } 01222 $removeVersion->removeThis(); 01223 } 01224 } 01225 } 01226 01227 // get the next available version number 01228 $nextVersionNumber = $this->nextVersion(); 01229 01230 if ( $copyFromVersion == false ) 01231 { 01232 $version = $this->currentVersion(); 01233 } 01234 else 01235 { 01236 $version = $this->version( $copyFromVersion ); 01237 } 01238 01239 if ( !$languageCode ) 01240 { 01241 $initialLanguage = $version->initialLanguage(); 01242 if ( !$initialLanguage ) 01243 { 01244 $initialLanguage = $this->initialLanguage(); 01245 } 01246 01247 if ( $initialLanguage ) 01248 { 01249 $languageCode = $initialLanguage->attribute( 'locale' ); 01250 } 01251 } 01252 01253 $copiedVersion = $this->copyVersion( $this, $version, $nextVersionNumber, false, $status, $languageCode, $copyFromLanguageCode ); 01254 01255 // We need to make sure the copied version contains node-assignment for the existing nodes. 01256 // This is required for BC since scripts might traverse the node-assignments and mark 01257 // some of them for removal. 01258 $parentMap = array(); 01259 $copiedNodeAssignmentList = $copiedVersion->attribute( 'node_assignments' ); 01260 foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment ) 01261 { 01262 $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment; 01263 } 01264 $nodes = $this->assignedNodes(); 01265 foreach ( $nodes as $node ) 01266 { 01267 $remoteID = 0; 01268 // Remove assignments which conflicts with existing nodes, but keep remote_id 01269 if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) ) 01270 { 01271 $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )]; 01272 $remoteID = $copiedNodeAssignment->attribute( 'remote_id' ); 01273 $copiedNodeAssignment->purge(); 01274 } 01275 $newNodeAssignment = $copiedVersion->assignToNode( $node->attribute( 'parent_node_id' ), $node->attribute( 'is_main' ), 0, 01276 $node->attribute( 'sort_field' ), $node->attribute( 'sort_order' ), 01277 $remoteID ); 01278 // Reset execution bit 01279 $newNodeAssignment->setAttribute( 'op_code', $newNodeAssignment->attribute( 'op_code' ) & ~1 ); 01280 $newNodeAssignment->store(); 01281 } 01282 01283 $db->commit(); 01284 return $copiedVersion; 01285 } 01286 01287 /*! 01288 Creates a new version and returns it as an eZContentObjectVersion object. 01289 If version number is given as argument that version is used to create a copy. 01290 \param $languageCode If \c false all languages will be copied, otherwise 01291 only specified by the locale code string or an array 01292 of the locale code strings. 01293 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01294 the calls within a db transaction; thus within db->begin and db->commit. 01295 */ 01296 function copyVersion( &$newObject, &$version, $newVersionNumber, $contentObjectID = false, $status = eZContentObjectVersion::STATUS_DRAFT, $languageCode = false, $copyFromLanguageCode = false ) 01297 { 01298 $user = eZUser::currentUser(); 01299 $userID = $user->attribute( 'contentobject_id' ); 01300 01301 $nodeAssignmentList = $version->attribute( 'node_assignments' ); 01302 01303 $db = eZDB::instance(); 01304 $db->begin(); 01305 01306 // This is part of the new 3.8 code. 01307 foreach ( array_keys( $nodeAssignmentList ) as $key ) 01308 { 01309 $nodeAssignment = $nodeAssignmentList[$key]; 01310 // Only copy assignments which has a remote_id since it will be used in template code. 01311 if ( $nodeAssignment->attribute( 'remote_id' ) == 0 ) 01312 { 01313 continue; 01314 } 01315 $clonedAssignment = $nodeAssignment->cloneNodeAssignment( $newVersionNumber, $contentObjectID ); 01316 $clonedAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_SET ); // Make sure op_code is marked to 'set' the data. 01317 $clonedAssignment->store(); 01318 } 01319 01320 $currentVersionNumber = $version->attribute( "version" ); 01321 $contentObjectTranslations = $version->translations(); 01322 01323 $clonedVersion = $version->cloneVersion( $newVersionNumber, $userID, $contentObjectID, $status ); 01324 01325 if ( $contentObjectID != false ) 01326 { 01327 if ( $clonedVersion->attribute( 'status' ) == eZContentObjectVersion::STATUS_PUBLISHED ) 01328 $clonedVersion->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT ); 01329 } 01330 01331 $clonedVersion->store(); 01332 01333 // We copy related objects before the attributes, this means that the related objects 01334 // are available once the datatype code is run. 01335 $this->copyContentObjectRelations( $currentVersionNumber, $newVersionNumber, $contentObjectID ); 01336 01337 $languageCodeToCopy = false; 01338 if ( $languageCode && in_array( $languageCode, $this->availableLanguages() ) ) 01339 { 01340 $languageCodeToCopy = $languageCode; 01341 } 01342 if ( $copyFromLanguageCode && in_array( $copyFromLanguageCode, $this->availableLanguages() ) ) 01343 { 01344 $languageCodeToCopy = $copyFromLanguageCode; 01345 } 01346 01347 $haveCopied = false; 01348 if ( !$languageCode || $languageCodeToCopy ) 01349 { 01350 foreach ( $contentObjectTranslations as $contentObjectTranslation ) 01351 { 01352 if ( $languageCode != false && $contentObjectTranslation->attribute( 'language_code' ) != $languageCodeToCopy ) 01353 { 01354 continue; 01355 } 01356 01357 $contentObjectAttributes = $contentObjectTranslation->objectAttributes(); 01358 01359 foreach ( $contentObjectAttributes as $attribute ) 01360 { 01361 $clonedAttribute = $attribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode ); 01362 $clonedAttribute->sync(); 01363 eZDebugSetting::writeDebug( 'kernel-content-object-copy', $clonedAttribute, 'copyVersion:cloned attribute' ); 01364 } 01365 01366 $haveCopied = true; 01367 } 01368 } 01369 01370 if ( !$haveCopied && $languageCode ) 01371 { 01372 $class = $this->contentClass(); 01373 $classAttributes = $class->fetchAttributes(); 01374 foreach ( $classAttributes as $classAttribute ) 01375 { 01376 if ( $classAttribute->attribute( 'can_translate' ) == 1 ) 01377 { 01378 $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber ); 01379 } 01380 else 01381 { 01382 // If attribute is NOT Translatable we should check isAlwaysAvailable(), 01383 // For example, 01384 // if initial_language_id is 4 and the attribute is always available 01385 // language_id will be 5 in ezcontentobject_version/ezcontentobject_attribute, 01386 // this means it uses language ID 4 but also has the bit 0 set to 1 (a reservered bit), 01387 // You can read about this in the document in doc/features/3.8/. 01388 $initialLangID = !$this->isAlwaysAvailable() ? $this->attribute( 'initial_language_id' ) : $this->attribute( 'initial_language_id' ) | 1; 01389 $contentAttribute = eZContentObjectAttribute::fetchByClassAttributeID( $classAttribute->attribute( 'id' ), 01390 $this->attribute( 'id' ), 01391 $this->attribute( 'current_version' ), 01392 $initialLangID ); 01393 if ( $contentAttribute ) 01394 { 01395 $newAttribute = $contentAttribute->cloneContentObjectAttribute( $newVersionNumber, $currentVersionNumber, $contentObjectID, $languageCode ); 01396 $newAttribute->sync(); 01397 } 01398 else 01399 { 01400 $classAttribute->instantiate( $contentObjectID? $contentObjectID: $this->attribute( 'id' ), $languageCode, $newVersionNumber ); 01401 } 01402 } 01403 } 01404 } 01405 01406 if ( $languageCode ) 01407 { 01408 $clonedVersion->setAttribute( 'initial_language_id', eZContentLanguage::idByLocale( $languageCode ) ); 01409 $clonedVersion->updateLanguageMask(); 01410 } 01411 01412 $db->commit(); 01413 01414 return $clonedVersion; 01415 } 01416 01417 /*! 01418 Creates a new content object instance and stores it. 01419 */ 01420 static function create( $name, $contentclassID, $userID, $sectionID = 1, $version = 1, $languageCode = false ) 01421 { 01422 if ( $languageCode == false ) 01423 { 01424 $languageCode = eZContentObject::defaultLanguage(); 01425 } 01426 01427 $languageID = eZContentLanguage::idByLocale( $languageCode ); 01428 01429 $row = array( 01430 "name" => $name, 01431 "current_version" => $version, 01432 'initial_language_id' => $languageID, 01433 'language_mask' => $languageID, 01434 "contentclass_id" => $contentclassID, 01435 "permission_id" => 1, 01436 "parent_id" => 0, 01437 "main_node_id" => 0, 01438 "owner_id" => $userID, 01439 "section_id" => $sectionID, 01440 'remote_id' => eZRemoteIdUtility::generate( 'object' ) ); 01441 01442 return new eZContentObject( $row ); 01443 } 01444 01445 function __clone() 01446 { 01447 $this->setAttribute( 'id', null ); 01448 $this->setAttribute( 'published', time() ); 01449 $this->setAttribute( 'modified', time() ); 01450 $this->resetDataMap(); 01451 } 01452 01453 /*! 01454 Makes a copy of the object which is stored and then returns it. 01455 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01456 the calls within a db transaction; thus within db->begin and db->commit. 01457 */ 01458 function copy( $allVersions = true ) 01459 { 01460 eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy start, all versions=' . ( $allVersions ? 'true' : 'false' ), 'copy' ); 01461 $user = eZUser::currentUser(); 01462 $userID = $user->attribute( 'contentobject_id' ); 01463 01464 $contentObject = clone $this; 01465 $contentObject->setAttribute( 'current_version', 1 ); 01466 $contentObject->setAttribute( 'owner_id', $userID ); 01467 01468 $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) ); 01469 01470 $db = eZDB::instance(); 01471 $db->begin(); 01472 $contentObject->store(); 01473 01474 $originalObjectID = $this->attribute( 'id' ); 01475 $contentObjectID = $contentObject->attribute( 'id' ); 01476 01477 $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id) 01478 SELECT contentobject_state_id, $contentObjectID FROM ezcobj_state_link WHERE contentobject_id = $originalObjectID" ); 01479 01480 $contentObject->setName( $this->attribute('name') ); 01481 eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObject, 'contentObject' ); 01482 01483 01484 $versionList = array(); 01485 if ( $allVersions ) 01486 { 01487 $versions = $this->versions(); 01488 foreach( $versions as $version ) 01489 { 01490 $versionList[$version->attribute( 'version' )] = $version; 01491 } 01492 } 01493 else 01494 { 01495 $versionList[1] = $this->currentVersion(); 01496 } 01497 01498 foreach ( $versionList as $versionNumber => $currentContentObjectVersion ) 01499 { 01500 $currentVersionNumber = $currentContentObjectVersion->attribute( 'version' ); 01501 $contentObject->setName( $currentContentObjectVersion->name(), $versionNumber ); 01502 foreach( $contentObject->translationStringList() as $languageCode ) 01503 { 01504 $contentObject->setName( $currentContentObjectVersion->name( false, $languageCode ), $versionNumber, $languageCode ); 01505 } 01506 01507 $contentObjectVersion = $this->copyVersion( $contentObject, $currentContentObjectVersion, 01508 $versionNumber, $contentObject->attribute( 'id' ), 01509 false ); 01510 01511 if ( $currentVersionNumber == $this->attribute( 'current_version' ) ) 01512 { 01513 $parentMap = array(); 01514 $copiedNodeAssignmentList = $contentObjectVersion->attribute( 'node_assignments' ); 01515 foreach ( $copiedNodeAssignmentList as $copiedNodeAssignment ) 01516 { 01517 $parentMap[$copiedNodeAssignment->attribute( 'parent_node' )] = $copiedNodeAssignment; 01518 } 01519 // Create node-assignment from all current published nodes 01520 $nodes = $this->assignedNodes(); 01521 foreach( $nodes as $node ) 01522 { 01523 $remoteID = 0; 01524 // Remove assignments which conflicts with existing nodes, but keep remote_id 01525 if ( isset( $parentMap[$node->attribute( 'parent_node_id' )] ) ) 01526 { 01527 $copiedNodeAssignment = $parentMap[$node->attribute( 'parent_node_id' )]; 01528 unset( $parentMap[$node->attribute( 'parent_node_id' )] ); 01529 $remoteID = $copiedNodeAssignment->attribute( 'remote_id' ); 01530 $copiedNodeAssignment->purge(); 01531 } 01532 $newNodeAssignment = $contentObjectVersion->assignToNode( $node->attribute( 'parent_node_id' ), $node->attribute( 'is_main' ), 0, 01533 $node->attribute( 'sort_field' ), $node->attribute( 'sort_order' ), 01534 $remoteID ); 01535 } 01536 } 01537 01538 eZDebugSetting::writeDebug( 'kernel-content-object-copy', $contentObjectVersion, 'Copied version' ); 01539 } 01540 01541 // Set version number 01542 if ( $allVersions ) 01543 $contentObject->setAttribute( 'current_version', $this->attribute( 'current_version' ) ); 01544 01545 $contentObject->setAttribute( 'status', eZContentObject::STATUS_DRAFT ); 01546 01547 $contentObject->store(); 01548 01549 $db->commit(); 01550 01551 eZDebugSetting::writeDebug( 'kernel-content-object-copy', 'Copy done', 'copy' ); 01552 return $contentObject; 01553 } 01554 01555 /*! 01556 Reverts the object to the given version. All versions newer then the given version will 01557 be deleted. 01558 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01559 the calls within a db transaction; thus within db->begin and db->commit. 01560 */ 01561 function revertTo( $version ) 01562 { 01563 $db = eZDB::instance(); 01564 $db->begin(); 01565 01566 // Delete stored attribute from other tables 01567 $contentobjectAttributes = $this->allContentObjectAttributes( $this->ID ); 01568 foreach ( $contentobjectAttributes as $contentobjectAttribute ) 01569 { 01570 $contentobjectAttributeVersion = $contentobjectAttribute->attribute("version"); 01571 if( $contentobjectAttributeVersion > $version ) 01572 { 01573 $classAttribute = $contentobjectAttribute->contentClassAttribute(); 01574 $dataType = $classAttribute->dataType(); 01575 $dataType->deleteStoredObjectAttribute( $contentobjectAttribute, $contentobjectAttributeVersion ); 01576 } 01577 } 01578 $version =(int) $version; 01579 $db->query( "DELETE FROM ezcontentobject_attribute 01580 WHERE contentobject_id='$this->ID' AND version>'$version'" ); 01581 01582 $db->query( "DELETE FROM ezcontentobject_version 01583 WHERE contentobject_id='$this->ID' AND version>'$version'" ); 01584 01585 $db->query( "DELETE FROM eznode_assignment 01586 WHERE contentobject_id='$this->ID' AND contentobject_version > '$version'" ); 01587 01588 $this->CurrentVersion = $version; 01589 $this->store(); 01590 $db->commit(); 01591 } 01592 01593 /*! 01594 Copies the given version of the object and creates a new current version. 01595 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01596 the calls within a db transaction; thus within db->begin and db->commit. 01597 */ 01598 function copyRevertTo( $version, $language = false ) 01599 { 01600 $versionObject = $this->createNewVersionIn( $language, false, $version ); 01601 01602 return $versionObject->attribute( 'version' ); 01603 } 01604 01605 static function fixReverseRelations( $objectID, $mode = false ) 01606 { 01607 $db = eZDB::instance(); 01608 $objectID = (int) $objectID; 01609 01610 // Finds all the attributes that store relations to the given object. 01611 $result = $db->arrayQuery( "SELECT attr.* 01612 FROM ezcontentobject_link link, 01613 ezcontentobject_attribute attr 01614 WHERE link.from_contentobject_id=attr.contentobject_id AND 01615 link.from_contentobject_version=attr.version AND 01616 link.contentclassattribute_id=attr.contentclassattribute_id AND 01617 link.to_contentobject_id=$objectID" ); 01618 if ( count( $result ) > 0 ) 01619 { 01620 foreach( $result as $row ) 01621 { 01622 $attr = new eZContentObjectAttribute( $row ); 01623 $dataType = $attr->dataType(); 01624 $dataType->fixRelatedObjectItem( $attr, $objectID, $mode ); 01625 eZContentCacheManager::clearObjectViewCache( $attr->attribute( 'contentobject_id' ), true ); 01626 } 01627 } 01628 } 01629 01630 function removeReverseRelations( $objectID ) 01631 { 01632 $db = eZDB::instance(); 01633 $objectID = (int) $objectID; 01634 // Get list of objects referring to this one. 01635 $relatingObjects = $this->reverseRelatedObjectList( false, 0, false, array( 'AllRelations' => true ) ); 01636 01637 // Finds all the attributes that store relations to the given object. 01638 01639 $result = $db->arrayQuery( "SELECT attr.* 01640 FROM ezcontentobject_link link, 01641 ezcontentobject_attribute attr 01642 WHERE link.from_contentobject_id=attr.contentobject_id AND 01643 link.from_contentobject_version=attr.version AND 01644 link.contentclassattribute_id=attr.contentclassattribute_id AND 01645 link.to_contentobject_id=$objectID" ); 01646 01647 // Remove references from XML. 01648 if ( count( $result ) > 0 ) 01649 { 01650 foreach( $result as $row ) 01651 { 01652 $attr = new eZContentObjectAttribute( $row ); 01653 $dataType = $attr->dataType(); 01654 $dataType->removeRelatedObjectItem( $attr, $objectID ); 01655 eZContentCacheManager::clearObjectViewCache( $attr->attribute( 'contentobject_id' ), true ); 01656 $attr->storeData(); 01657 } 01658 } 01659 01660 // Remove references in ezcontentobject_link. 01661 foreach ( $relatingObjects as $fromObject ) 01662 { 01663 $fromObject->removeContentObjectRelation( $this->attribute( 'id' ), false, false ); 01664 } 01665 } 01666 01667 /*! 01668 If nodeID is not given, this function will remove object from database. All versions and translations of this object will be lost. 01669 Otherwise, it will check node assignment and only delete the object from this node if it was assigned to other nodes as well. 01670 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01671 the calls within a db transaction; thus within db->begin and db->commit. 01672 */ 01673 function purge() 01674 { 01675 $delID = $this->ID; 01676 // Who deletes which content should be logged. 01677 eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ), 01678 'Comment' => 'Purged the current object: eZContentObject::purge()' ) ); 01679 01680 $db = eZDB::instance(); 01681 01682 $db->begin(); 01683 01684 $contentobjectAttributes = $this->allContentObjectAttributes( $delID ); 01685 01686 foreach ( $contentobjectAttributes as $contentobjectAttribute ) 01687 { 01688 $dataType = $contentobjectAttribute->dataType(); 01689 if ( !$dataType ) 01690 continue; 01691 $dataType->deleteStoredObjectAttribute( $contentobjectAttribute ); 01692 } 01693 01694 eZInformationCollection::removeContentObject( $delID ); 01695 01696 eZContentObjectTrashNode::purgeForObject( $delID ); 01697 01698 $db->query( "DELETE FROM ezcontentobject_tree 01699 WHERE contentobject_id='$delID'" ); 01700 01701 $db->query( "DELETE FROM ezcontentobject_attribute 01702 WHERE contentobject_id='$delID'" ); 01703 01704 $db->query( "DELETE FROM ezcontentobject_version 01705 WHERE contentobject_id='$delID'" ); 01706 01707 $db->query( "DELETE FROM ezcontentobject_name 01708 WHERE contentobject_id='$delID'" ); 01709 01710 $db->query( "DELETE FROM ezcobj_state_link WHERE contentobject_id=$delID" ); 01711 01712 $db->query( "DELETE FROM ezcontentobject 01713 WHERE id='$delID'" ); 01714 01715 $db->query( "DELETE FROM eznode_assignment 01716 WHERE contentobject_id = '$delID'" ); 01717 01718 $db->query( "DELETE FROM ezuser_role 01719 WHERE contentobject_id = '$delID'" ); 01720 01721 $db->query( "DELETE FROM ezuser_discountrule 01722 WHERE contentobject_id = '$delID'" ); 01723 01724 eZContentObject::fixReverseRelations( $delID, 'remove' ); 01725 01726 eZSearch::removeObject( $this ); 01727 01728 // Check if deleted object is in basket/wishlist 01729 $sql = 'SELECT DISTINCT ezproductcollection_item.productcollection_id 01730 FROM ezbasket, ezwishlist, ezproductcollection_item 01731 WHERE ( ezproductcollection_item.productcollection_id=ezbasket.productcollection_id OR 01732 ezproductcollection_item.productcollection_id=ezwishlist.productcollection_id ) AND 01733 ezproductcollection_item.contentobject_id=' . $delID; 01734 $rows = $db->arrayQuery( $sql ); 01735 if ( count( $rows ) > 0 ) 01736 { 01737 $countElements = 50; 01738 $deletedArray = array(); 01739 // Create array of productCollectionID will be removed from ezwishlist and ezproductcollection_item 01740 foreach ( $rows as $row ) 01741 { 01742 $deletedArray[] = $row['productcollection_id']; 01743 } 01744 // Split $deletedArray into several arrays with $countElements values 01745 $splitted = array_chunk( $deletedArray, $countElements ); 01746 // Remove eZProductCollectionItem and eZWishList 01747 foreach ( $splitted as $value ) 01748 { 01749 eZPersistentObject::removeObject( eZProductCollectionItem::definition(), array( 'productcollection_id' => array( $value, '' ) ) ); 01750 eZPersistentObject::removeObject( eZWishList::definition(), array( 'productcollection_id' => array( $value, '' ) ) ); 01751 } 01752 } 01753 $db->query( 'UPDATE ezproductcollection_item 01754 SET contentobject_id = 0 01755 WHERE contentobject_id = ' . $delID ); 01756 01757 // Cleanup relations in two steps to avoid locking table for to long 01758 $db->query( "DELETE FROM ezcontentobject_link 01759 WHERE from_contentobject_id = '$delID'" ); 01760 01761 $db->query( "DELETE FROM ezcontentobject_link 01762 WHERE to_contentobject_id = '$delID'" ); 01763 01764 // Cleanup properties: LastVisit, Creator, Owner 01765 $db->query( "DELETE FROM ezuservisit 01766 WHERE user_id = '$delID'" ); 01767 01768 $db->query( "UPDATE ezcontentobject_version 01769 SET creator_id = 0 01770 WHERE creator_id = '$delID'" ); 01771 01772 $db->query( "UPDATE ezcontentobject 01773 SET owner_id = 0 01774 WHERE owner_id = '$delID'" ); 01775 01776 if ( isset( $GLOBALS["eZWorkflowTypeObjects"] ) and is_array( $GLOBALS["eZWorkflowTypeObjects"] ) ) 01777 { 01778 $registeredTypes =& $GLOBALS["eZWorkflowTypeObjects"]; 01779 } 01780 else 01781 { 01782 $registeredTypes = eZWorkflowType::fetchRegisteredTypes(); 01783 } 01784 01785 // Cleanup ezworkflow_event etc... 01786 foreach ( array_keys( $registeredTypes ) as $registeredTypeKey ) 01787 { 01788 $registeredType = $registeredTypes[$registeredTypeKey]; 01789 $registeredType->cleanupAfterRemoving( array( 'DeleteContentObject' => $delID ) ); 01790 } 01791 01792 $db->commit(); 01793 } 01794 01795 /*! 01796 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 01797 the calls within a db transaction; thus within db->begin and db->commit. 01798 */ 01799 function removeThis( $nodeID = null ) 01800 { 01801 $delID = $this->ID; 01802 01803 // Who deletes which content should be logged. 01804 eZAudit::writeAudit( 'content-delete', array( 'Object ID' => $delID, 'Content Name' => $this->attribute( 'name' ), 01805 'Comment' => 'Setted archived status for the current object: eZContentObject::remove()' ) ); 01806 01807 $nodes = $this->attribute( 'assigned_nodes' ); 01808 01809 if ( $nodeID === null or count( $nodes ) <= 1 ) 01810 { 01811 $db = eZDB::instance(); 01812 $db->begin(); 01813 $mainNodeKey = false; 01814 foreach ( $nodes as $key => $node ) 01815 { 01816 if ( $node->attribute( 'main_node_id' ) == $node->attribute( 'node_id' ) ) 01817 { 01818 $mainNodeKey = $key; 01819 } 01820 else 01821 { 01822 $node->removeThis(); 01823 } 01824 } 01825 01826 if ( $mainNodeKey !== false ) 01827 { 01828 $nodes[$mainNodeKey]->removeNodeFromTree( true ); 01829 } 01830 01831 01832 $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED ); 01833 eZSearch::removeObject( $this ); 01834 $this->store(); 01835 eZContentObject::fixReverseRelations( $delID, 'trash' ); 01836 // Delete stored attribute from other tables 01837 $db->commit(); 01838 01839 } 01840 else if ( $nodeID !== null ) 01841 { 01842 $node = eZContentObjectTreeNode::fetch( $nodeID , false ); 01843 if ( is_object( $node ) ) 01844 { 01845 if ( $node->attribute( 'main_node_id' ) == $nodeID ) 01846 { 01847 $db = eZDB::instance(); 01848 $db->begin(); 01849 foreach ( $additionalNodes as $additionalNode ) 01850 { 01851 if ( $additionalNode->attribute( 'node_id' ) != $node->attribute( 'main_node_id' ) ) 01852 { 01853 $additionalNode->remove(); 01854 } 01855 } 01856 01857 $node->removeNodeFromTree( true ); 01858 $this->setAttribute( 'status', eZContentObject::STATUS_ARCHIVED ); 01859 eZSearch::removeObject( $this ); 01860 $this->store(); 01861 eZContentObject::fixReverseRelations( $delID, 'trash' ); 01862 $db->commit(); 01863 } 01864 else 01865 { 01866 eZContentObjectTreeNode::removeNode( $nodeID ); 01867 } 01868 } 01869 } 01870 else 01871 { 01872 eZContentObjectTreeNode::removeNode( $nodeID ); 01873 } 01874 } 01875 01876 /*! 01877 Removes old internal drafts by the specified user associated with this content object. 01878 Only internal drafts older than 1 day will be considered. 01879 \param $userID The ID of the user to cleanup for, if \c false it will use the current user. 01880 */ 01881 function cleanupInternalDrafts( $userID = false, $timeDuration = 86400 ) // default time duration for internal drafts 60*60*24 seconds (1 day) 01882 { 01883 if ( !is_numeric( $timeDuration ) || 01884 $timeDuration < 0 ) 01885 { 01886 eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ ); 01887 return; 01888 } 01889 01890 if ( $userID === false ) 01891 { 01892 $userID = eZUser::currentUserID(); 01893 } 01894 // Fetch all draft/temporary versions by specified user 01895 $parameters = array( 'conditions' => array( 'status' => eZContentObjectVersion::STATUS_INTERNAL_DRAFT, 01896 'creator_id' => $userID ) ); 01897 // Remove temporary drafts which are old. 01898 $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day) 01899 foreach ( $this->versions( true, $parameters ) as $possibleVersion ) 01900 { 01901 if ( $possibleVersion->attribute( 'modified' ) < $expiryTime ) 01902 { 01903 $possibleVersion->removeThis(); 01904 } 01905 } 01906 } 01907 01908 /*! 01909 \static 01910 Removes all old internal drafts by the specified user. 01911 Only internal drafts older than 1 day will be considered. 01912 \param $userID The ID of the user to cleanup for, if \c false it will use the current user. 01913 */ 01914 static function cleanupAllInternalDrafts( $userID = false, $timeDuration = 86400 ) // default time duration for internal drafts 60*60*24 seconds (1 day) 01915 { 01916 if ( !is_numeric( $timeDuration ) || 01917 $timeDuration < 0 ) 01918 { 01919 eZDebug::writeError( "The time duration must be a positive numeric value (timeDuration = $timeDuration)", __METHOD__ ); 01920 return; 01921 } 01922 01923 01924 if ( $userID === false ) 01925 { 01926 $userID = eZUser::currentUserID(); 01927 } 01928 // Remove all internal drafts 01929 $untouchedDrafts = eZContentObjectVersion::fetchForUser( $userID, eZContentObjectVersion::STATUS_INTERNAL_DRAFT ); 01930 01931 $expiryTime = time() - $timeDuration; // only remove drafts older than time duration (default is 1 day) 01932 foreach ( $untouchedDrafts as $untouchedDraft ) 01933 { 01934 if ( $untouchedDraft->attribute( 'modified' ) < $expiryTime ) 01935 { 01936 $untouchedDraft->removeThis(); 01937 } 01938 } 01939 } 01940 01941 /* 01942 Fetch all attributes of all versions belongs to a contentObject. 01943 */ 01944 function allContentObjectAttributes( $contentObjectID, $asObject = true ) 01945 { 01946 return eZPersistentObject::fetchObjectList( eZContentObjectAttribute::definition(), 01947 null, 01948 array("contentobject_id" => $contentObjectID ), 01949 null, 01950 null, 01951 $asObject ); 01952 } 01953 01954 /*! 01955 Fetches the attributes for the current published version of the object. 01956 TODO: fix using of $asObject parameter, 01957 fix condition for getting attribute from cache, 01958 probably need to move method to eZContentObjectVersion class 01959 */ 01960 function contentObjectAttributes( $asObject = true, $version = false, $language = false, $contentObjectAttributeID = false, $distinctItemsOnly = false ) 01961 { 01962 $db = eZDB::instance(); 01963 if ( $version == false ) 01964 { 01965 $version = $this->CurrentVersion; 01966 } 01967 else 01968 { 01969 $version = (int) $version; 01970 } 01971 01972 if ( $language === false ) 01973 { 01974 $language = $this->CurrentLanguage; 01975 } 01976 01977 if ( is_string( $language ) ) 01978 $language = $db->escapeString( $language ); 01979 01980 if ( $contentObjectAttributeID !== false ) 01981 $contentObjectAttributeID =(int) $contentObjectAttributeID; 01982 01983 if ( !$language || !isset( $this->ContentObjectAttributes[$version][$language] ) ) 01984 { 01985 $versionText = "AND ezcontentobject_attribute.version = '$version'"; 01986 if ( $language ) 01987 { 01988 $languageText = "AND ezcontentobject_attribute.language_code = '$language'"; 01989 } 01990 else 01991 { 01992 $languageText = "AND ".eZContentLanguage::sqlFilter( 'ezcontentobject_attribute', 'ezcontentobject_version' ); 01993 } 01994 $attributeIDText = false; 01995 if ( $contentObjectAttributeID ) 01996 $attributeIDText = "AND ezcontentobject_attribute.id = '$contentObjectAttributeID'"; 01997 $distinctText = false; 01998 if ( $distinctItemsOnly ) 01999 $distinctText = "GROUP BY ezcontentobject_attribute.id"; 02000 $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM 02001 ezcontentobject_attribute, ezcontentclass_attribute, ezcontentobject_version 02002 WHERE 02003 ezcontentclass_attribute.version = '0' AND 02004 ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND 02005 ezcontentobject_version.contentobject_id = '$this->ID' AND 02006 ezcontentobject_version.version = '$version' AND 02007 ezcontentobject_attribute.contentobject_id = '$this->ID' $versionText $languageText $attributeIDText 02008 $distinctText 02009 ORDER BY 02010 ezcontentclass_attribute.placement ASC, 02011 ezcontentobject_attribute.language_code ASC"; 02012 02013 $attributeArray = $db->arrayQuery( $query ); 02014 02015 if ( !$language && $attributeArray ) 02016 { 02017 $language = $attributeArray[0]['language_code']; 02018 $this->CurrentLanguage = $language; 02019 } 02020 02021 $returnAttributeArray = array(); 02022 foreach ( $attributeArray as $attribute ) 02023 { 02024 $attr = new eZContentObjectAttribute( $attribute ); 02025 $attr->setContentClassAttributeIdentifier( $attribute['identifier'] ); 02026 $returnAttributeArray[] = $attr; 02027 } 02028 02029 if ( $language !== null and $version !== null ) 02030 { 02031 $this->ContentObjectAttributes[$version][$language] = $returnAttributeArray; 02032 } 02033 } 02034 else 02035 { 02036 $returnAttributeArray = $this->ContentObjectAttributes[$version][$language]; 02037 } 02038 02039 return $returnAttributeArray; 02040 } 02041 02042 /*! 02043 Initializes the cached copy of the content object attributes for the given version and language 02044 */ 02045 function setContentObjectAttributes( &$attributes, $version, $language ) 02046 { 02047 $this->ContentObjectAttributes[$version][$language] = $attributes; 02048 } 02049 02050 /*! 02051 \static 02052 Fetches the attributes for an array of objects. The objList parameter 02053 contains an array of objects ( instanceOf eZContentObject or a object that is or 02054 extends eZContentObjectTreeNode ) to fetch attributes from. 02055 */ 02056 static function fillNodeListAttributes( $objList, $asObject = true ) 02057 { 02058 $db = eZDB::instance(); 02059 02060 if ( count( $objList ) > 0 ) 02061 { 02062 $objectArray = array(); 02063 $tmpLanguageObjectList = array(); 02064 $whereSQL = ''; 02065 $count = count( $objList ); 02066 $i = 0; 02067 foreach ( $objList as $obj ) 02068 { 02069 if ( $obj instanceOf eZContentObject ) 02070 $object = $obj; 02071 else 02072 $object = $obj->attribute( 'object' ); 02073 02074 $language = $object->currentLanguage(); 02075 $tmpLanguageObjectList[$object->attribute( 'id' )] = $language; 02076 $objectArray = array( 'id' => $object->attribute( 'id' ), 02077 'language' => $language, 02078 'version' => $object->attribute( 'current_version' ) ); 02079 02080 $whereSQL .= "( ezcontentobject_attribute.version = '" . $object->attribute( 'current_version' ) . "' AND 02081 ezcontentobject_attribute.contentobject_id = '" . $object->attribute( 'id' ) . "' AND 02082 ezcontentobject_attribute.language_code = '" . $language . "' ) "; 02083 02084 $i++; 02085 if ( $i < $count ) 02086 $whereSQL .= ' OR '; 02087 } 02088 02089 $query = "SELECT ezcontentobject_attribute.*, ezcontentclass_attribute.identifier as identifier FROM 02090 ezcontentobject_attribute, ezcontentclass_attribute 02091 WHERE 02092 ezcontentclass_attribute.version = '0' AND 02093 ezcontentclass_attribute.id = ezcontentobject_attribute.contentclassattribute_id AND 02094 ( $whereSQL ) 02095 ORDER BY 02096 ezcontentobject_attribute.contentobject_id, ezcontentclass_attribute.placement ASC"; 02097 02098 $attributeArray = $db->arrayQuery( $query ); 02099 02100 $tmpAttributeObjectList = array(); 02101 $returnAttributeArray = array(); 02102 foreach ( $attributeArray as $attribute ) 02103 { 02104 $attr = new eZContentObjectAttribute( $attribute ); 02105 $attr->setContentClassAttributeIdentifier( $attribute['identifier'] ); 02106 02107 $tmpAttributeObjectList[$attr->attribute( 'contentobject_id' )][] = $attr; 02108 } 02109 02110 foreach ( $objList as $obj ) 02111 { 02112 if ( $obj instanceOf eZContentObject ) 02113 { 02114 $obj->setContentObjectAttributes( $tmpAttributeObjectList[$obj->attribute( 'id' )], 02115 $obj->attribute( 'current_version' ), 02116 $tmpLanguageObjectList[$obj->attribute( 'id' )] ); 02117 } 02118 else 02119 { 02120 $object = $obj->attribute( 'object' ); 02121 $object->setContentObjectAttributes( $tmpAttributeObjectList[$object->attribute( 'id' )], 02122 $object->attribute( 'current_version' ), 02123 $tmpLanguageObjectList[$object->attribute( 'id' )] ); 02124 $obj->setContentObject( $object ); 02125 } 02126 } 02127 } 02128 } 02129 02130 function resetInputRelationList() 02131 { 02132 $this->InputRelationList = array( eZContentObject::RELATION_EMBED => array(), 02133 eZContentObject::RELATION_LINK => array() ); 02134 } 02135 02136 function appendInputRelationList( $addingIDList, $relationType ) 02137 { 02138 if ( !is_array( $addingIDList ) ) 02139 { 02140 $addingIDList = array( ( int ) $addingIDList ); 02141 } 02142 elseif ( !count( $addingIDList ) ) 02143 { 02144 return; 02145 } 02146 $relationType = ( int ) $relationType; 02147 if ( !$this->InputRelationList ) 02148 { 02149 $this->resetInputRelationList(); 02150 } 02151 02152 foreach ( array_keys( $this->InputRelationList ) as $inputRelationType ) 02153 { 02154 if ( $inputRelationType & $relationType ) 02155 { 02156 $this->InputRelationList[$inputRelationType] = array_merge( $this->InputRelationList[$inputRelationType], $addingIDList ); 02157 } 02158 } 02159 } 02160 02161 function commitInputRelations( $editVersion ) 02162 { 02163 foreach ( $this->InputRelationList as $relationType => $relatedObjectIDArray ) 02164 { 02165 $oldRelatedObjectArray = $this->relatedObjects( $editVersion, false, 0, false, array( 'AllRelations' => $relationType ) ); 02166 02167 foreach ( $oldRelatedObjectArray as $oldRelatedObject ) 02168 { 02169 $oldRelatedObjectID = $oldRelatedObject->ID; 02170 if ( !in_array( $oldRelatedObjectID, $relatedObjectIDArray ) ) 02171 { 02172 $this->removeContentObjectRelation( $oldRelatedObjectID, $editVersion, 0, $relationType ); 02173 } 02174 $relatedObjectIDArray = array_diff( $relatedObjectIDArray, array( $oldRelatedObjectID ) ); 02175 } 02176 02177 foreach ( $relatedObjectIDArray as $relatedObjectID ) 02178 { 02179 $this->addContentObjectRelation( $relatedObjectID, $editVersion, 0, $relationType ); 02180 } 02181 } 02182 return true; 02183 } 02184 02185 function validateInput( $contentObjectAttributes, $attributeDataBaseName, 02186 $inputParameters = false, $parameters = array() ) 02187 { 02188 $result = array( 'unvalidated-attributes' => array(), 02189 'validated-attributes' => array(), 02190 'status-map' => array(), 02191 'require-fixup' => false, 02192 'input-validated' => true ); 02193 $parameters = array_merge( array( 'prefix-name' => false ), 02194 $parameters ); 02195 if ( $inputParameters ) 02196 { 02197 $result['unvalidated-attributes'] =& $inputParameters['unvalidated-attributes']; 02198 $result['validated-attributes'] =& $inputParameters['validated-attributes']; 02199 } 02200 $unvalidatedAttributes =& $result['unvalidated-attributes']; 02201 $validatedAttributes =& $result['validated-attributes']; 02202 $statusMap =& $result['status-map']; 02203 if ( !$inputParameters ) 02204 $inputParameters = array( 'unvalidated-attributes' => &$unvalidatedAttributes, 02205 'validated-attributes' => &$validatedAttributes ); 02206 $requireFixup =& $result['require-fixup']; 02207 $inputValidated =& $result['input-validated']; 02208 $http = eZHTTPTool::instance(); 02209 02210 $this->resetInputRelationList(); 02211 02212 $editVersion = null; 02213 $defaultLanguage = $this->initialLanguageCode(); 02214 foreach( $contentObjectAttributes as $contentObjectAttribute ) 02215 { 02216 $contentClassAttribute = $contentObjectAttribute->contentClassAttribute(); 02217 $editVersion = $contentObjectAttribute->attribute('version'); 02218 02219 // Check if this is a translation 02220 $currentLanguage = $contentObjectAttribute->attribute( 'language_code' ); 02221 02222 $isTranslation = false; 02223 if ( $currentLanguage != $defaultLanguage ) 02224 $isTranslation = true; 02225 02226 // If current attribute is a translation 02227 // Check if this attribute can be translated 02228 // If not do not validate, since the input will be copyed from the original 02229 $doNotValidate = false; 02230 if ( $isTranslation ) 02231 { 02232 if ( !$contentClassAttribute->attribute( 'can_translate' ) ) 02233 $doNotValidate = true; 02234 } 02235 02236 if ( $doNotValidate == true ) 02237 { 02238 $status = eZInputValidator::STATE_ACCEPTED; 02239 } 02240 else 02241 { 02242 $status = $contentObjectAttribute->validateInput( $http, $attributeDataBaseName, 02243 $inputParameters, $parameters ); 02244 } 02245 $statusMap[$contentObjectAttribute->attribute( 'id' )] = array( 'value' => $status, 02246 'attribute' => $contentObjectAttribute ); 02247 02248 if ( $status == eZInputValidator::STATE_INTERMEDIATE ) 02249 $requireFixup = true; 02250 else if ( $status == eZInputValidator::STATE_INVALID ) 02251 { 02252 $inputValidated = false; 02253 $dataType = $contentObjectAttribute->dataType(); 02254 $attributeName = $dataType->attribute( 'information' ); 02255 $attributeName = $attributeName['name']; 02256 $description = $contentObjectAttribute->attribute( 'validation_error' ); 02257 $validationNameArray[] = $contentClassAttribute->attribute( 'name' ); 02258 $validationName = implode( '->', $validationNameArray ); 02259 $hasValidationError = $contentObjectAttribute->attribute( 'has_validation_error' ); 02260 if ( $hasValidationError ) 02261 { 02262 if ( !$description ) 02263 $description = false; 02264 $validationNameArray = array(); 02265 if ( $parameters['prefix-name'] ) 02266 $validationNameArray = $parameters['prefix-name']; 02267 } 02268 else 02269 { 02270 if ( !$description ) 02271 $description = 'unknown error'; 02272 } 02273 $unvalidatedAttributes[] = array( 'id' => $contentObjectAttribute->attribute( 'id' ), 02274 'identifier' => $contentClassAttribute->attribute( 'identifier' ), 02275 'name' => $validationName, 02276 'description' => $description ); 02277 } 02278 else if ( $status == eZInputValidator::STATE_ACCEPTED ) 02279 { 02280 $dataType = $contentObjectAttribute->dataType(); 02281 $attributeName = $dataType->attribute( 'information' ); 02282 $attributeName = $attributeName['name']; 02283 if ( $contentObjectAttribute->attribute( 'validation_log' ) != null ) 02284 { 02285 $description = $contentObjectAttribute->attribute( 'validation_log' ); 02286 if ( !$description ) 02287 $description = false; 02288 $validationName = $contentClassAttribute->attribute( 'name' ); 02289 if ( $parameters['prefix-name'] ) 02290 $validationName = $parameters['prefix-name'] . '->' . $validationName; 02291 $validatedAttributes[] = array( 'id' => $contentObjectAttribute->attribute( 'id' ), 02292 'identifier' => $contentClassAttribute->attribute( 'identifier' ), 02293 'name' => $validationName, 02294 'description' => $description ); 02295 } 02296 } 02297 } 02298 02299 if ( $editVersion !== null ) 02300 { 02301 $this->commitInputRelations( $editVersion ); 02302 } 02303 $this->resetInputRelationList(); 02304 02305 return $result; 02306 } 02307 02308 function fixupInput( $contentObjectAttributes, $attributeDataBaseName ) 02309 { 02310 $http = eZHTTPTool::instance(); 02311 foreach ( $contentObjectAttributes as $contentObjectAttribute ) 02312 { 02313 $contentObjectAttribute->fixupInput( $http, $attributeDataBaseName ); 02314 } 02315 } 02316 02317 function fetchInput( $contentObjectAttributes, $attributeDataBaseName, 02318 $customActionAttributeArray, $customActionParameters ) 02319 { 02320 // Global variable to cache datamaps 02321 global $eZContentObjectDataMapCache; 02322 02323 $result = array( 'attribute-input-map' => array() ); 02324 $attributeInputMap =& $result['attribute-input-map']; 02325 $http = eZHTTPTool::instance(); 02326 02327 $defaultLanguage = $this->initialLanguageCode(); 02328 02329 $this->fetchDataMap(); 02330 foreach ( $contentObjectAttributes as $contentObjectAttribute ) 02331 { 02332 $contentClassAttribute = $contentObjectAttribute->contentClassAttribute(); 02333 02334 // Check if this is a translation 02335 $currentLanguage = $contentObjectAttribute->attribute( 'language_code' ); 02336 02337 $isTranslation = false; 02338 if ( $currentLanguage != $defaultLanguage ) 02339 $isTranslation = true; 02340 02341 // If current attribute is an un-translateable translation, input should not be fetched 02342 $fetchInput = true; 02343 if ( $isTranslation == true ) 02344 { 02345 if ( !$contentClassAttribute->attribute( 'can_translate' ) ) 02346 { 02347 $fetchInput = false; 02348 } 02349 } 02350 02351 // Do not handle input for non-translateable attributes. 02352 // Input will be copyed from the std. translation on storage 02353 if ( $fetchInput ) 02354 { 02355 if ( $contentObjectAttribute->fetchInput( $http, $attributeDataBaseName ) ) 02356 { 02357 $attributeInputMap[$contentObjectAttribute->attribute('id')] = true; 02358 02359 // we fill the internal data map cache for the current version here with the attributes of the new version 02360 // this will make the data map cache inconsistent, but this is required to make it possible to use $object.data_map 02361 // in content/edit templates 02362 $attributeIdentifier = $contentObjectAttribute->attribute( 'contentclass_attribute_identifier' ); 02363 $eZContentObjectDataMapCache[$this->ID][$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute; 02364 $this->DataMap[$this->CurrentVersion][$currentLanguage][$attributeIdentifier] = $contentObjectAttribute; 02365 } 02366 02367 // Custom Action Code 02368 $this->handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName, 02369 $customActionAttributeArray, $customActionParameters ); 02370 } 02371 02372 } 02373 return $result; 02374 } 02375 02376 function handleCustomHTTPActions( $contentObjectAttribute, $attributeDataBaseName, 02377 $customActionAttributeArray, $customActionParameters ) 02378 { 02379 $http = eZHTTPTool::instance(); 02380 $customActionParameters['base_name'] = $attributeDataBaseName; 02381 if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) ) 02382 { 02383 $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id']; 02384 $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value']; 02385 $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters ); 02386 } 02387 02388 $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName, 02389 $customActionAttributeArray, $customActionParameters ); 02390 } 02391 02392 function handleAllCustomHTTPActions( $attributeDataBaseName, 02393 $customActionAttributeArray, $customActionParameters, 02394 $objectVersion = false ) 02395 { 02396 $http = eZHTTPTool::instance(); 02397 $contentObjectAttributes = $this->contentObjectAttributes( true, $objectVersion ); 02398 $oldAttributeDataBaseName = $customActionParameters['base_name']; 02399 $customActionParameters['base_name'] = $attributeDataBaseName; 02400 foreach( $contentObjectAttributes as $contentObjectAttribute ) 02401 { 02402 if ( isset( $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )] ) ) 02403 { 02404 $customActionAttributeID = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['id']; 02405 $customAction = $customActionAttributeArray[$contentObjectAttribute->attribute( 'id' )]['value']; 02406 $contentObjectAttribute->customHTTPAction( $http, $customAction, $customActionParameters ); 02407 } 02408 02409 $contentObjectAttribute->handleCustomHTTPActions( $http, $attributeDataBaseName, 02410 $customActionAttributeArray, $customActionParameters ); 02411 } 02412 $customActionParameters['base_name'] = $oldAttributeDataBaseName; 02413 } 02414 02415 static function recursionProtectionStart() 02416 { 02417 $GLOBALS["ez_content_object_recursion_protect"] = array(); 02418 } 02419 02420 static function recursionProtect( $id ) 02421 { 02422 if ( isset( $GLOBALS["ez_content_object_recursion_protect"][$id] ) ) 02423 { 02424 return false; 02425 } 02426 else 02427 { 02428 $GLOBALS["ez_content_object_recursion_protect"][$id] = true; 02429 return true; 02430 } 02431 } 02432 02433 static function recursionProtectionEnd() 02434 { 02435 unset( $GLOBALS["ez_content_object_recursion_protect"] ); 02436 } 02437 02438 /*! 02439 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02440 the calls within a db transaction; thus within db->begin and db->commit. 02441 */ 02442 function storeInput( $contentObjectAttributes, 02443 $attributeInputMap ) 02444 { 02445 $db = eZDB::instance(); 02446 $db->begin(); 02447 foreach ( $contentObjectAttributes as $contentObjectAttribute ) 02448 { 02449 if ( isset( $attributeInputMap[$contentObjectAttribute->attribute('id')] ) ) 02450 { 02451 $contentObjectAttribute->store(); 02452 } 02453 } 02454 $db->commit(); 02455 unset( $this->ContentObjectAttributes ); 02456 } 02457 02458 /*! 02459 Returns the next available version number for this object. 02460 */ 02461 function nextVersion() 02462 { 02463 $db = eZDB::instance(); 02464 $versions = $db->arrayQuery( "SELECT ( MAX( version ) + 1 ) AS next_id FROM ezcontentobject_version 02465 WHERE contentobject_id='$this->ID'" ); 02466 return $versions[0]["next_id"]; 02467 02468 } 02469 02470 /*! 02471 Returns the previous available version number for this object, if existing, false otherwise ( if the object has only one version ) 02472 */ 02473 function previousVersion() 02474 { 02475 $db = eZDB::instance(); 02476 $versions = $db->arrayQuery( "SELECT version FROM ezcontentobject_version 02477 WHERE contentobject_id='$this->ID' 02478 ORDER BY version DESC", array( 'limit' => 2 ) ); 02479 if ( count( $versions ) > 1 and isset( $versions[1]['version'] ) ) 02480 { 02481 return $versions[1]['version']; 02482 } 02483 else 02484 { 02485 return false; 02486 } 02487 } 02488 02489 /*! 02490 Returns number of exist versions. 02491 */ 02492 function getVersionCount() 02493 { 02494 $db = eZDB::instance(); 02495 $versionCount = $db->arrayQuery( "SELECT ( COUNT( version ) ) AS version_count FROM ezcontentobject_version 02496 WHERE contentobject_id='$this->ID'" ); 02497 return $versionCount[0]["version_count"]; 02498 02499 } 02500 02501 function currentLanguage() 02502 { 02503 return $this->CurrentLanguage; 02504 } 02505 02506 function currentLanguageObject() 02507 { 02508 if ( $this->CurrentLanguage ) 02509 { 02510 $language = eZContentLanguage::fetchByLocale( $this->CurrentLanguage ); 02511 } 02512 else 02513 { 02514 $language = false; 02515 } 02516 02517 return $language; 02518 } 02519 02520 function setCurrentLanguage( $lang ) 02521 { 02522 $this->CurrentLanguage = $lang; 02523 $this->Name = null; 02524 } 02525 02526 function initialLanguage() 02527 { 02528 return isset( $this->InitialLanguageID ) ? eZContentLanguage::fetch( $this->InitialLanguageID ) : false; 02529 } 02530 02531 function initialLanguageCode() 02532 { 02533 $initialLanguage = $this->initialLanguage(); 02534 // If current contentobject is "Top Level Nodes" than it doesn't have "initial Language" and "locale". 02535 return ( $initialLanguage !== false ) ? $initialLanguage->attribute( 'locale' ) : false; 02536 } 02537 02538 /*! 02539 Adds a new location (node) to the current object. 02540 \param $parenNodeID The id of the node to use as parent. 02541 \param $asObject If true it will return the new child-node as an object, if not it returns the ID. 02542 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02543 the calls within a db transaction; thus within db->begin and db->commit. 02544 */ 02545 function addLocation( $parentNodeID, $asObject = false ) 02546 { 02547 $node = eZContentObjectTreeNode::addChildTo( $this->ID, $parentNodeID, true, $this->CurrentVersion ); 02548 02549 $data = array( 'contentobject_id' => $this->ID, 02550 'contentobject_version' => $this->attribute( 'current_version' ), 02551 'parent_node' => $parentNodeID, 02552 'is_main' => 0 ); 02553 $nodeAssignment = eZNodeAssignment::create( $data ); 02554 $nodeAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_CREATE_NOP ); 02555 $nodeAssignment->store(); 02556 02557 if ( $asObject ) 02558 { 02559 return $node; 02560 } 02561 else 02562 { 02563 return $node->attribute( 'node_id' ); 02564 } 02565 } 02566 02567 /*! 02568 Adds a link to the given content object id. 02569 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02570 the calls within a db transaction; thus within db->begin and db->commit. 02571 */ 02572 function addContentObjectRelation( $toObjectID, 02573 $fromObjectVersion = false, 02574 $attributeID = 0, 02575 $relationType = eZContentObject::RELATION_COMMON ) 02576 { 02577 if ( $attributeID !== 0 ) 02578 { 02579 $relationType = eZContentObject::RELATION_ATTRIBUTE; 02580 } 02581 02582 $relationType =(int) $relationType; 02583 if ( ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) != 0 && 02584 $relationType != eZContentObject::RELATION_ATTRIBUTE ) 02585 { 02586 eZDebug::writeWarning( "Object relation type conflict", __METHOD__ ); 02587 } 02588 02589 $db = eZDB::instance(); 02590 02591 if ( !$fromObjectVersion ) 02592 $fromObjectVersion = $this->CurrentVersion; 02593 02594 $fromObjectID = $this->ID; 02595 02596 if ( !is_numeric( $toObjectID ) ) 02597 { 02598 eZDebug::writeError( "Related object ID (toObjectID): '$toObjectID', is not a numeric value.", 02599 "eZContentObject::addContentObjectRelation" ); 02600 return false; 02601 } 02602 $fromObjectID =(int) $fromObjectID; 02603 $attributeID =(int) $attributeID; 02604 $fromObjectVersion =(int) $fromObjectVersion; 02605 $relationBaseType = ( $relationType & eZContentObject::RELATION_ATTRIBUTE ) ? 02606 eZContentObject::RELATION_ATTRIBUTE : 02607 eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED | eZContentObject::RELATION_LINK; 02608 $relationTypeMatch = $db->bitAnd( 'relation_type', $relationBaseType ); 02609 $query = "SELECT count(*) AS count 02610 FROM ezcontentobject_link 02611 WHERE from_contentobject_id=$fromObjectID AND 02612 from_contentobject_version=$fromObjectVersion AND 02613 to_contentobject_id=$toObjectID AND 02614 $relationTypeMatch != 0 AND 02615 contentclassattribute_id=$attributeID AND 02616 op_code='0'"; 02617 $count = $db->arrayQuery( $query ); 02618 // if current relation does not exist 02619 if ( !isset( $count[0]['count'] ) || $count[0]['count'] == '0' ) 02620 { 02621 $db->begin(); 02622 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, relation_type ) 02623 VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, $relationType )" ); 02624 // if an object relation is being added and it is in draft, add the row with op_code 1 02625 if ( $attributeID == 0 && $fromObjectVersion != $this->CurrentVersion ) 02626 { 02627 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type ) 02628 VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, '1', $relationType )" ); 02629 } 02630 $db->commit(); 02631 } 02632 elseif ( isset( $count[0]['count'] ) && 02633 $count[0]['count'] != '0' && 02634 $attributeID == 0 && 02635 (eZContentObject::RELATION_ATTRIBUTE & $relationType) == 0 ) 02636 { 02637 $db->begin(); 02638 $newRelationType = $db->bitOr( 'relation_type', $relationType ); 02639 $db->query( "UPDATE ezcontentobject_link 02640 SET relation_type = $newRelationType 02641 WHERE from_contentobject_id=$fromObjectID AND 02642 from_contentobject_version=$fromObjectVersion AND 02643 to_contentobject_id=$toObjectID AND 02644 contentclassattribute_id=$attributeID AND 02645 op_code='0'" ); 02646 // if an object relation is being added and it is in draft, add the row with op_code 1 02647 if ( $attributeID == 0 && $fromObjectVersion != $this->CurrentVersion ) 02648 { 02649 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type ) 02650 VALUES ( $fromObjectID, $fromObjectVersion, $toObjectID, $attributeID, '1', $relationType )" ); 02651 } 02652 $db->commit(); 02653 } 02654 } 02655 02656 /*! 02657 Removes a link to the given content object id. 02658 \param $toObjectID If \c false it will delete relations to all the objects. 02659 \param $attributeID ID of class attribute. 02660 IF it is > 0 we remove relations created by a specific objectrelation[list] attribute. 02661 If it is set to 0 we remove relations created without using of objectrelation[list] attribute. 02662 If it is set to false, we remove all relations, no matter how were they created: 02663 using objectrelation[list] attribute or using "Add related objects" functionality in obect editing mode. 02664 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 02665 the calls within a db transaction; thus within db->begin and db->commit. 02666 */ 02667 function removeContentObjectRelation( $toObjectID = false, $fromObjectVersion = false, $attributeID = 0, $relationType = eZContentObject::RELATION_COMMON ) 02668 { 02669 $db = eZDB::instance(); 02670 02671 if ( !$fromObjectVersion ) 02672 $fromObjectVersion = $this->CurrentVersion; 02673 $fromObjectVersion = (int) $fromObjectVersion; 02674 $fromObjectID = $this->ID; 02675 02676 if ( $toObjectID !== false ) 02677 { 02678 $toObjectID =(int) $toObjectID; 02679 $toObjectCondition = "AND to_contentobject_id=$toObjectID"; 02680 } 02681 else 02682 $toObjectCondition = ''; 02683 02684 if ( $attributeID !== false ) 02685 { 02686 $attributeID =(int) $attributeID; 02687 $classAttributeCondition = "AND contentclassattribute_id=$attributeID"; 02688 } 02689 else 02690 $classAttributeCondition = ''; 02691 02692 $lastRelationType = 0; 02693 $db->begin(); 02694 // if an object relation is being removed from the draft, add the row with op_code -1 02695 if ( !$attributeID && $fromObjectVersion != $this->CurrentVersion ) 02696 { 02697 $rows = $db->arrayQuery( "SELECT * FROM ezcontentobject_link 02698 WHERE from_contentobject_id=$fromObjectID 02699 AND from_contentobject_version=$fromObjectVersion 02700 AND contentclassattribute_id='0' 02701 $toObjectCondition 02702 AND op_code='0'" ); 02703 foreach ( $rows as $row ) 02704 { 02705 $db->query( "INSERT INTO ezcontentobject_link ( from_contentobject_id, from_contentobject_version, to_contentobject_id, contentclassattribute_id, op_code, relation_type ) 02706 VALUES ( $fromObjectID, $fromObjectVersion, " . $row['to_contentobject_id'] . ", '0', '-1', $relationType )" ); 02707 $lastRelationType = (int) $row['relation_type']; 02708 } 02709 } 02710 02711 if ( 0 !== ( eZContentObject::RELATION_ATTRIBUTE & $relationType ) || 02712 0 != $attributeID || 02713 $relationType == $lastRelationType ) 02714 { 02715 $db->query( "DELETE FROM ezcontentobject_link 02716 WHERE from_contentobject_id=$fromObjectID AND 02717 from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND 02718 op_code='0'" ); 02719 } 02720 else 02721 { 02722 if ( $db->databaseName() == 'oracle' ) 02723 { 02724 $notRelationType = - ( $relationType + 1 ); 02725 $db->query( "UPDATE ezcontentobject_link 02726 SET relation_type = " . $db->bitAnd( 'relation_type', $notRelationType ) . " 02727 WHERE from_contentobject_id=$fromObjectID AND 02728 from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND 02729 op_code='0'" ); 02730 } 02731 else 02732 { 02733 $db->query( "UPDATE ezcontentobject_link 02734 SET relation_type = ( relation_type & ".(~$relationType)." ) 02735 WHERE from_contentobject_id=$fromObjectID AND 02736 from_contentobject_version=$fromObjectVersion $classAttributeCondition $toObjectCondition AND 02737 op_code='0'" ); 02738 } 02739 } 02740 02741 $db->commit(); 02742 02743 } 02744 02745 function copyContentObjectRelations( $currentVersion, $newVersion, $newObjectID = false ) 02746 { 02747 $objectID = $this->ID; 02748 if ( !$newObjectID ) 02749 { 02750 $newObjectID = $objectID; 02751 } 02752 02753 $db = eZDB::instance(); 02754 $db->begin(); 02755 02756 $relations = $db->arrayQuery( "SELECT to_contentobject_id, op_code, relation_type FROM ezcontentobject_link 02757 WHERE contentclassattribute_id='0' 02758 AND from_contentobject_id='$objectID' 02759 AND from_contentobject_version='$currentVersion'" ); 02760 foreach ( $relations as $relation ) 02761 { 02762 $toContentObjectID = $relation['to_contentobject_id']; 02763 $opCode = $relation['op_code']; 02764 $relationType = $relation['relation_type']; 02765 $db->query( "INSERT INTO ezcontentobject_link( contentclassattribute_id, 02766 from_contentobject_id, 02767 from_contentobject_version, 02768 to_contentobject_id, 02769 op_code, 02770 relation_type ) 02771 VALUES ( '0', '$newObjectID', '$newVersion', '$toContentObjectID', '$opCode', '$relationType' )" ); 02772 } 02773 02774 $db->commit(); 02775 } 02776 02777 static function isObjectRelationTyped() 02778 { 02779 $siteIni = eZINI::instance( 'site.ini' ); 02780 if ( $siteIni->hasVariable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) ) 02781 { 02782 if ( 'enabled' == $siteIni->variable( 'BackwardCompatibilitySettings', 'ObjectRelationTyped' ) ) 02783 { 02784 return true; 02785 } 02786 } 02787 return false; 02788 } 02789 02790 static function relationTypeMask( $allRelations = false ) 02791 { 02792 $relationTypeMask = eZContentObject::RELATION_COMMON | 02793 eZContentObject::RELATION_EMBED; 02794 02795 if ( eZContentObject::isObjectRelationTyped() ) 02796 { 02797 $relationTypeMask |= eZContentObject::RELATION_LINK; 02798 } 02799 02800 if ( $allRelations ) 02801 { 02802 $relationTypeMask |= eZContentObject::RELATION_ATTRIBUTE; 02803 } 02804 02805 return $relationTypeMask; 02806 } 02807 02808 /*! 02809 Returns the related or reverse related objects: 02810 \param $attributeID : ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE ) 02811 >0 - return relations made with attribute ID ( "related object(s)" datatype ) 02812 0 or false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE ) 02813 - return relations made with any attributes 02814 false ( $params['AllRelations'] not set ) 02815 - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead) 02816 \param $groupByAttribute : false - return all relations as an array of content objects 02817 true - return all relations groupped by attribute ID 02818 This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true 02819 \param $params : other parameters from template fetch function : 02820 $params['AllRelations'] - relation type filter : 02821 true - return ALL relations, including attribute-level 02822 false - return object-level relations 02823 >0 - bit mask of EZ_CONTENT_OBJECT_RELATION_* values 02824 $params['SortBy'] - related objects sorting mode. 02825 Supported modes: class_identifier, class_name, modified, name, published, section 02826 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true 02827 $params['RelatedClassIdentifiers'] - limit returned relations to objects of the specified class identifiers 02828 \param $reverseRelatedObjects : if "true" returns reverse related contentObjects 02829 if "false" returns related contentObjects 02830 */ 02831 function relatedObjects( $fromObjectVersion = false, 02832 $objectID = false, 02833 $attributeID = 0, 02834 $groupByAttribute = false, 02835 $params = false, 02836 $reverseRelatedObjects = false ) 02837 { 02838 if ( $fromObjectVersion == false ) 02839 $fromObjectVersion = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false; 02840 $fromObjectVersion =(int) $fromObjectVersion; 02841 if( !$objectID ) 02842 $objectID = $this->ID; 02843 $objectID =(int) $objectID; 02844 02845 $limit = ( isset( $params['Limit'] ) && is_numeric( $params['Limit'] ) ) ? $params['Limit'] : false; 02846 $offset = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset'] : false; 02847 $asObject = ( isset( $params['AsObject'] ) ) ? $params['AsObject'] : true; 02848 $loadDataMap = ( isset( $params['LoadDataMap'] ) ) ? $params['LoadDataMap'] : false; 02849 02850 02851 $db = eZDB::instance(); 02852 $sortingString = ''; 02853 $sortingInfo = array( 'attributeFromSQL' => '', 02854 'attributeWhereSQL' => '', 02855 'attributeTargetSQL' => '' ); 02856 $relatedClassIdentifiersSQL = ''; 02857 $showInvisibleNodesCond = ''; 02858 // process params (only SortBy and IgnoreVisibility currently supported): 02859 // Supported sort_by modes: 02860 // class_identifier, class_name, modified, name, published, section 02861 if ( is_array( $params ) ) 02862 { 02863 if ( isset( $params['SortBy'] ) ) 02864 { 02865 $validSortBy = array( 'class_identifier', 'class_name', 'modified', 'name', 'published', 'section' ); 02866 $sortByParam = array(); 02867 if ( is_array( $params['SortBy'] ) ) 02868 { 02869 // only one SortBy, as a simple array 02870 if ( !is_array( $params['SortBy'][0] ) ) 02871 { 02872 if ( !in_array( $params['SortBy'][0], $validSortBy ) ) 02873 eZDebug::writeWarning( "Unsupported sort_by parameter {$params['SortBy'][0]}; check the online documentation for the list of supported sort types", __METHOD__ ); 02874 else 02875 $sortByParam[] = $params['SortBy']; 02876 } 02877 // multiple SortBy, check each of them one by one, and keep valid ones 02878 else 02879 { 02880 $invalidSortBy = array(); 02881 foreach( $params['SortBy'] as $sortByTuple ) 02882 { 02883 if ( !in_array( $sortByTuple[0], $validSortBy ) ) 02884 $invalidSortBy[] = $sortByTuple[0]; 02885 else 02886 $sortByParam[] = $sortByTuple; 02887 } 02888 if ( count( $invalidSortBy ) > 0 ) 02889 { 02890 eZDebug::writeWarning( "Unsupported sort_by parameter(s) " . implode( ', ', $invalidSortBy ) . "; check the online documentation for the list of supported sort types", __METHOD__ ); 02891 } 02892 } 02893 } 02894 if ( count( $sortByParam ) > 0 ) 02895 { 02896 $sortingInfo = eZContentObjectTreeNode::createSortingSQLStrings( $sortByParam ); 02897 $sortingString = ' ORDER BY ' . $sortingInfo['sortingFields']; 02898 } 02899 } 02900 if ( isset( $params['IgnoreVisibility'] ) ) 02901 { 02902 $showInvisibleNodesCond = self::createFilterByVisibilitySQLString( $params['IgnoreVisibility'] ); 02903 } 02904 02905 // related class identifier filter 02906 $relatedClassIdentifiersSQL = ''; 02907 if ( isset( $params['RelatedClassIdentifiers'] ) && is_array( $params['RelatedClassIdentifiers'] ) ) 02908 { 02909 $relatedClassIdentifiers = array(); 02910 foreach( $params['RelatedClassIdentifiers'] as $classIdentifier ) 02911 { 02912 $relatedClassIdentifiers[] = "'" . $db->escapeString( $classIdentifier ) . "'"; 02913 } 02914 $relatedClassIdentifiersSQL = $db->generateSQLINStatement( $relatedClassIdentifiers, 'ezcontentclass.identifier', false, true, 'string' ). " AND"; 02915 unset( $classIdentifier, $relatedClassIdentifiers ); 02916 } 02917 } 02918 02919 $relationTypeMasking = ''; 02920 $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false ); 02921 if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) ) 02922 { 02923 $attributeID =(int) $attributeID; 02924 $relationTypeMasking .= " AND contentclassattribute_id=$attributeID "; 02925 $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE; 02926 } 02927 elseif ( is_bool( $relationTypeMask ) ) 02928 { 02929 $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask ); 02930 } 02931 02932 if ( $db->databaseName() == 'oracle' ) 02933 { 02934 $relationTypeMasking .= " AND bitand( relation_type, $relationTypeMask ) <> 0 "; 02935 } 02936 else 02937 { 02938 $relationTypeMasking .= " AND ( relation_type & $relationTypeMask ) <> 0 "; 02939 } 02940 02941 // Create SQL 02942 $versionNameTables = ', ezcontentobject_name '; 02943 $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation '; 02944 02945 $versionNameJoins = " AND ezcontentobject.id = ezcontentobject_name.contentobject_id AND 02946 ezcontentobject.current_version = ezcontentobject_name.content_version AND "; 02947 $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ); 02948 02949 $fromOrToContentObjectID = $reverseRelatedObjects == false ? " AND ezcontentobject.id=ezcontentobject_link.to_contentobject_id AND 02950 ezcontentobject_link.from_contentobject_id='$objectID' AND 02951 ezcontentobject_link.from_contentobject_version='$fromObjectVersion' " 02952 : " AND ezcontentobject.id=ezcontentobject_link.from_contentobject_id AND 02953 ezcontentobject_link.to_contentobject_id=$objectID AND 02954 ezcontentobject_link.from_contentobject_version=ezcontentobject.current_version "; 02955 $query = "SELECT "; 02956 02957 if ( $groupByAttribute ) 02958 { 02959 $query .= "ezcontentobject_link.contentclassattribute_id, "; 02960 } 02961 $query .= " 02962 ezcontentclass.serialized_name_list AS class_serialized_name_list, 02963 ezcontentclass.identifier as contentclass_identifier, 02964 ezcontentclass.is_container as is_container, 02965 ezcontentobject.* $versionNameTargets 02966 $sortingInfo[attributeTargetSQL] 02967 FROM 02968 ezcontentclass, 02969 ezcontentobject, 02970 ezcontentobject_link 02971 $versionNameTables 02972 $sortingInfo[attributeFromSQL] 02973 WHERE 02974 ezcontentclass.id=ezcontentobject.contentclass_id AND 02975 ezcontentclass.version=0 AND 02976 ezcontentobject.status=" . eZContentObject::STATUS_PUBLISHED . " AND 02977 $sortingInfo[attributeWhereSQL] 02978 $relatedClassIdentifiersSQL 02979 ezcontentobject_link.op_code='0' 02980 $relationTypeMasking 02981 $fromOrToContentObjectID 02982 $showInvisibleNodesCond 02983 $versionNameJoins 02984 $sortingString"; 02985 if ( !$offset && !$limit ) 02986 { 02987 $relatedObjects = $db->arrayQuery( $query ); 02988 } 02989 else 02990 { 02991 $relatedObjects = $db->arrayQuery( $query, array( 'offset' => $offset, 02992 'limit' => $limit ) ); 02993 } 02994 02995 $ret = array(); 02996 $tmp = array(); 02997 foreach ( $relatedObjects as $object ) 02998 { 02999 if ( $asObject ) 03000 { 03001 $obj = new eZContentObject( $object ); 03002 $obj->ClassName = eZContentClass::nameFromSerializedString( $object['class_serialized_name_list'] ); 03003 } 03004 else 03005 { 03006 $obj = $object; 03007 } 03008 03009 $tmp[] = $obj; 03010 03011 if ( !$groupByAttribute ) 03012 { 03013 $ret[] = $obj; 03014 } 03015 else 03016 { 03017 $classAttrID = $object['contentclassattribute_id']; 03018 03019 if ( !isset( $ret[$classAttrID] ) ) 03020 $ret[$classAttrID] = array(); 03021 03022 $ret[$classAttrID][] = $obj; 03023 } 03024 } 03025 if ( $loadDataMap && $asObject ) 03026 eZContentObject::fillNodeListAttributes( $tmp ); 03027 return $ret; 03028 } 03029 03030 /*! 03031 Returns the related objects. 03032 \param $attributeID : ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE ) 03033 >0 - return relations made with attribute ID ( "related object(s)" datatype ) 03034 0 or false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE ) 03035 - return relations made with any attributes 03036 false ( $params['AllRelations'] not set ) 03037 - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead) 03038 \param $groupByAttribute : false - return all relations as an array of content objects 03039 true - return all relations groupped by attribute ID 03040 This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true 03041 \param $params : other parameters from template fetch function : 03042 $params['AllRelations'] - relation type filter : 03043 true - return ALL relations, including attribute-level 03044 false - return object-level relations 03045 >0 - bit mask of EZ_CONTENT_OBJECT_RELATION_* values 03046 $params['SortBy'] - related objects sorting mode. 03047 Supported modes: class_identifier, class_name, modified, name, published, section 03048 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true 03049 $params['RelatedClassIdentifiers'] - limit returned relations to objects of the specified class identifiers 03050 */ 03051 function relatedContentObjectList( $fromObjectVersion = false, 03052 $fromObjectID = false, 03053 $attributeID = 0, 03054 $groupByAttribute = false, 03055 $params = false ) 03056 { 03057 eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $fromObjectID, "objectID" ); 03058 return $this->relatedObjects( $fromObjectVersion, $fromObjectID, $attributeID, $groupByAttribute, $params ); 03059 } 03060 03061 /*! 03062 Returns the xml-linked objects. 03063 */ 03064 function linkedContentObjectList( $fromObjectVersion = false, $fromObjectID = false ) 03065 { 03066 return $this->relatedObjects( $fromObjectVersion, 03067 $fromObjectID, 03068 0, 03069 false, 03070 array( 'AllRelations' => eZContentObject::RELATION_LINK ) ); 03071 } 03072 03073 /*! 03074 Returns the xml-embedded objects. 03075 */ 03076 function embeddedContentObjectList( $fromObjectVersion = false, $fromObjectID = false ) 03077 { 03078 return $this->relatedObjects( $fromObjectVersion, 03079 $fromObjectID, 03080 0, 03081 false, 03082 array( 'AllRelations' => eZContentObject::RELATION_EMBED ) ); 03083 } 03084 03085 /*! 03086 Returns the reverse xml-linked objects. 03087 */ 03088 function reverseLinkedObjectList( $fromObjectVersion = false, $fromObjectID = false ) 03089 { 03090 return $this->relatedObjects( $fromObjectVersion, 03091 $fromObjectID, 03092 0, 03093 false, 03094 array( 'AllRelations' => eZContentObject::RELATION_LINK ), 03095 true ); 03096 } 03097 03098 /*! 03099 Returns the reverse xml-embedded objects. 03100 */ 03101 function reverseEmbeddedObjectList( $fromObjectVersion = false, $fromObjectID = false ) 03102 { 03103 return $this->relatedObjects( $fromObjectVersion, 03104 $fromObjectID, 03105 0, 03106 false, 03107 array( 'AllRelations' => eZContentObject::RELATION_EMBED ), 03108 true ); 03109 } 03110 03111 // left for compatibility 03112 function relatedContentObjectArray( $fromObjectVersion = false, 03113 $fromObjectID = false, 03114 $attributeID = 0, 03115 $params = false ) 03116 { 03117 return eZContentObject::relatedContentObjectList( $fromObjectVersion, 03118 $fromObjectID, 03119 $attributeID, 03120 false, 03121 $params ); 03122 } 03123 03124 /*! 03125 \return the number of related objects 03126 \param $attributeID : ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE ) 03127 >0 - return relations made with attribute ID ( "related object(s)" datatype ) 03128 0 or false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE ) 03129 - return relations made with any attributes 03130 false ( $params['AllRelations'] not set ) 03131 - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead) 03132 \param $params : other parameters from template fetch function : 03133 $params['AllRelations'] - relation type filter : 03134 true - return ALL relations, including attribute-level 03135 false - return object-level relations 03136 >0 - bit mask of EZ_CONTENT_OBJECT_RELATION_* values 03137 $params['SortBy'] - related objects sorting mode. 03138 Supported modes: class_identifier, class_name, modified, name, published, section 03139 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true 03140 */ 03141 function relatedContentObjectCount( $fromObjectVersion = false, 03142 $attributeID = 0, 03143 $params = false ) 03144 { 03145 eZDebugSetting::writeDebug( 'kernel-content-object-related-objects', $this->ID, "relatedContentObjectCount::objectID" ); 03146 return $this->relatedObjectCount( $fromObjectVersion, 03147 $attributeID, 03148 false, 03149 $params ); 03150 } 03151 03152 /*! 03153 Returns the objects to which this object are related . 03154 \param $attributeID : ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE ) 03155 >0 - return relations made with attribute ID ( "related object(s)" datatype ) 03156 0 or false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE ) 03157 - return relations made with any attributes 03158 false ( $params['AllRelations'] not set ) 03159 - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead) 03160 \param $groupByAttribute : false - return all relations as an array of content objects 03161 true - return all relations groupped by attribute ID 03162 This parameter makes sense only when $attributeID == false or $params['AllRelations'] = true 03163 \param $params : other parameters from template fetch function : 03164 $params['AllRelations'] - relation type filter : 03165 true - return ALL relations, including attribute-level 03166 false - return object-level relations 03167 >0 - bit mask of EZ_CONTENT_OBJECT_RELATION_* values 03168 $params['SortBy'] - related objects sorting mode. 03169 Supported modes: class_identifier, class_name, modified, name, published, section 03170 $params['IgnoreVisibility'] - ignores 'hidden' state of related objects if true 03171 */ 03172 function reverseRelatedObjectList( $version = false, 03173 $attributeID = 0, 03174 $groupByAttribute = false, 03175 $params = false ) 03176 { 03177 return $this->relatedObjects( $version, $this->ID, $attributeID, $groupByAttribute, $params, true ); 03178 } 03179 03180 /*! 03181 Returns the xml-linked objects count. 03182 */ 03183 function linkedContentObjectCount( $fromObjectVersion = false ) 03184 { 03185 return $this->relatedObjectCount( $fromObjectVersion, 03186 0, 03187 false, 03188 array( 'AllRelations' => eZContentObject::RELATION_LINK ) ); 03189 } 03190 03191 /*! 03192 Returns the xml-embedded objects count. 03193 */ 03194 function embeddedContentObjectCount( $fromObjectVersion = false ) 03195 { 03196 return $this->relatedObjectCount( $fromObjectVersion, 03197 0, 03198 false, 03199 array( 'AllRelations' => eZContentObject::RELATION_EMBED ) ); 03200 } 03201 03202 /*! 03203 Returns the reverse xml-linked objects count. 03204 */ 03205 function reverseLinkedObjectCount( $fromObjectVersion = false ) 03206 { 03207 return $this->relatedObjectCount( $fromObjectVersion, 03208 0, 03209 true, 03210 array( 'AllRelations' => eZContentObject::RELATION_LINK ) ); 03211 } 03212 03213 /*! 03214 Returns the reverse xml-embedded objects count. 03215 */ 03216 function reverseEmbeddedObjectCount( $fromObjectVersion = false ) 03217 { 03218 return $this->relatedObjectCount( $fromObjectVersion, 03219 0, 03220 true, 03221 array( 'AllRelations' => eZContentObject::RELATION_EMBED ) ); 03222 } 03223 03224 /** 03225 * Fetch the number of (reverse) related objects 03226 * 03227 * @param int $version 03228 * @param int $attributeID 03229 * This parameter only makes sense if $params[AllRelations] is unset, 03230 * set to false, or matches eZContentObject::RELATION_ATTRIBUTE 03231 * Possible values: 03232 * - 0 or false: 03233 * Count relations made with any attribute 03234 * - >0 03235 * Count relations made with attribute $attributeID 03236 * @param int|false $reverseRelatedObjects 03237 * Wether to count related objects (false) or reverse related 03238 * objects (false) 03239 * @param array|false $params 03240 * Various params, as an associative array. 03241 * Possible values: 03242 * - AllRelations (bool|int) 03243 * true: count ALL relations, object and attribute level 03244 * false: only count object level relations 03245 * other: bit mask of eZContentObject::RELATION_* constants 03246 * - IgnoreVisibility (bool) 03247 * If true, 'hidden' status will be ignored 03248 * 03249 * @return int The number of (reverse) related objects for the object 03250 */ 03251 function relatedObjectCount( $version = false, $attributeID = 0, $reverseRelatedObjects = false, $params = false ) 03252 { 03253 $objectID = $this->ID; 03254 if ( $version == false ) 03255 $version = isset( $this->CurrentVersion ) ? $this->CurrentVersion : false; 03256 $version = (int) $version; 03257 03258 $db = eZDB::instance(); 03259 $showInvisibleNodesCond = ''; 03260 03261 // process params (only IgnoreVisibility currently supported): 03262 if ( is_array( $params ) ) 03263 { 03264 if ( isset( $params['IgnoreVisibility'] ) ) 03265 { 03266 $showInvisibleNodesCond = self::createFilterByVisibilitySQLString( $params['IgnoreVisibility'], 'inner_object' ); 03267 } 03268 } 03269 03270 $relationTypeMasking = ''; 03271 $relationTypeMask = isset( $params['AllRelations'] ) ? $params['AllRelations'] : ( $attributeID === false ); 03272 if ( $attributeID && ( $relationTypeMask === false || $relationTypeMask === eZContentObject::RELATION_ATTRIBUTE ) ) 03273 { 03274 $attributeID =(int) $attributeID; 03275 $relationTypeMasking .= " AND inner_link.contentclassattribute_id = $attributeID "; 03276 $relationTypeMask = eZContentObject::RELATION_ATTRIBUTE; 03277 } 03278 elseif ( is_bool( $relationTypeMask ) ) 03279 { 03280 $relationTypeMask = eZContentObject::relationTypeMask( $relationTypeMask ); 03281 } 03282 03283 if ( $db->databaseName() == 'oracle' ) 03284 { 03285 $relationTypeMasking .= " AND bitand( inner_link.relation_type, $relationTypeMask ) <> 0 "; 03286 } 03287 else 03288 { 03289 $relationTypeMasking .= " AND ( inner_link.relation_type & $relationTypeMask ) <> 0 "; 03290 } 03291 03292 if ( $reverseRelatedObjects ) 03293 { 03294 $outerObjectIDSQL = 'outer_object.id = outer_link.from_contentobject_id'; 03295 if ( is_array( $objectID ) ) 03296 { 03297 if ( count( $objectID ) > 0 ) 03298 { 03299 $objectIDSQL = ' AND ' . $db->generateSQLINStatement( $objectID, 'inner_link.to_contentobject_id', false, false, 'int' ) . ' AND 03300 inner_link.from_contentobject_version = inner_object.current_version'; 03301 } 03302 else 03303 { 03304 $objectIDSQL = ''; 03305 } 03306 } 03307 else 03308 { 03309 $objectID = (int) $objectID; 03310 $objectIDSQL = " AND inner_link.to_contentobject_id = $objectID 03311 AND inner_link.from_contentobject_version = inner_object.current_version"; 03312 } 03313 } 03314 else 03315 { 03316 $outerObjectIDSQL = 'outer_object.id = outer_link.to_contentobject_id'; 03317 $objectIDSQL = " AND inner_link.from_contentobject_id = $objectID 03318 AND inner_link.from_contentobject_version = $version"; 03319 } 03320 03321 $query = "SELECT 03322 COUNT( outer_object.id ) AS count 03323 FROM 03324 ezcontentobject outer_object, ezcontentobject inner_object, ezcontentobject_link outer_link 03325 INNER JOIN 03326 ezcontentobject_link inner_link ON outer_link.id = inner_link.id 03327 WHERE 03328 $outerObjectIDSQL 03329 AND outer_object.status = " . eZContentObject::STATUS_PUBLISHED . " 03330 AND inner_object.id = inner_link.from_contentobject_id 03331 AND inner_object.status = " . eZContentObject::STATUS_PUBLISHED . " 03332 AND inner_link.op_code = 0 03333 $objectIDSQL 03334 $relationTypeMasking 03335 $showInvisibleNodesCond"; 03336 03337 $rows = $db->arrayQuery( $query ); 03338 return $rows[0]['count']; 03339 } 03340 03341 /*! 03342 Returns the number of objects to which this object is related. 03343 \param $attributeID : ( makes sense only when $params['AllRelations'] not set or eZContentObject::RELATION_ATTRIBUTE ) 03344 >0 - return relations made with attribute ID ( "related object(s)" datatype ) 03345 0 or false ( $params['AllRelations'] is eZContentObject::RELATION_ATTRIBUTE ) 03346 - return relations made with any attributes 03347 false ( $params['AllRelations'] not set ) 03348 - return ALL relations (deprecated, use "$params['AllRelations'] = true" instead) 03349 \param $params : other parameters from template fetch function : 03350 $params['AllRelations'] - relation type filter : 03351 true - return ALL relations, including attribute-level 03352 false - return object-level relations 03353 >0 - bit mask of EZ_CONTENT_OBJECT_RELATION_* values 03354 */ 03355 function reverseRelatedObjectCount( $version = false, $attributeID = 0, $params = false ) 03356 { 03357 return $this->relatedObjectCount( $version, $attributeID, true, $params ); 03358 } 03359 03360 /*! 03361 Returns the related objects. 03362 \note This function is a duplicate of reverseRelatedObjectList(), use that function instead. 03363 */ 03364 function contentObjectListRelatingThis( $version = false ) 03365 { 03366 return $this->reverseRelatedObjectList( $version ); 03367 } 03368 03369 function publishContentObjectRelations( $version ) 03370 { 03371 $objectID = $this->ID; 03372 $currentVersion = $this->CurrentVersion; 03373 $version =(int) $version; 03374 $db = eZDB::instance(); 03375 $db->begin(); 03376 03377 $toContentObjectIDs = array(); 03378 $relationTypesArray = array(); 03379 $publishedRelations = $db->arrayQuery( "SELECT to_contentobject_id, relation_type FROM ezcontentobject_link 03380 WHERE contentclassattribute_id='0' 03381 AND from_contentobject_id='$objectID' 03382 AND from_contentobject_version='$currentVersion' 03383 AND op_code='0'" ); 03384 03385 foreach ( $publishedRelations as $relation ) 03386 { 03387 $toContentObjectIDs[] = $relation['to_contentobject_id']; 03388 $relationTypesArray[$relation['to_contentobject_id']] = (int) $relation['relation_type']; 03389 } 03390 $toContentObjectIDs = array_unique( $toContentObjectIDs ); 03391 03392 $addedOrRemovedRelations = $db->arrayQuery( "SELECT to_contentobject_id, op_code, relation_type FROM ezcontentobject_link 03393 WHERE contentclassattribute_id='0' 03394 AND from_contentobject_id='$objectID' 03395 AND from_contentobject_version='$version' 03396 AND op_code!='0' 03397 ORDER BY id ASC" ); 03398 03399 foreach ( $addedOrRemovedRelations as $relation ) 03400 { 03401 $relationType = (int) $relation['relation_type']; 03402 if ( !isset( $relationTypesArray[$relation['to_contentobject_id']] ) ) 03403 { 03404 $relationTypesArray[$relation['to_contentobject_id']] = 0; 03405 } 03406 if ( $relation['op_code'] == 1 ) 03407 { 03408 if ( !in_array( $relation['to_contentobject_id'], $toContentObjectIDs ) ) 03409 { 03410 $toContentObjectIDs[] = $relation['to_contentobject_id']; 03411 } 03412 $relationTypesArray[$relation['to_contentobject_id']] |= $relationType; 03413 } 03414 else 03415 { 03416 $relationTypesArray[$relation['to_contentobject_id']] &= ~$relationType; 03417 if ( 0 === $relationTypesArray[$relation['to_contentobject_id']] ) 03418 { 03419 $toContentObjectIDs = array_diff( $toContentObjectIDs, array( $relation['to_contentobject_id'] ) ); 03420 } 03421 } 03422 } 03423 03424 $db->query( "DELETE FROM ezcontentobject_link 03425 WHERE contentclassattribute_id='0' 03426 AND from_contentobject_id='$objectID' 03427 AND from_contentobject_version='$version'" ); 03428 03429 foreach( $toContentObjectIDs as $toContentObjectID ) 03430 { 03431 $db->query( "INSERT INTO ezcontentobject_link( contentclassattribute_id, 03432 from_contentobject_id, 03433 from_contentobject_version, 03434 to_contentobject_id, 03435 op_code, 03436 relation_type ) 03437 VALUES ( '0', '$objectID', '$version', '$toContentObjectID', '0', '{$relationTypesArray[$toContentObjectID]}' )" ); 03438 } 03439 03440 $db->commit(); 03441 } 03442 03443 /*! 03444 Get parent node IDs 03445 */ 03446 function parentNodeIDArray() 03447 { 03448 return $this->parentNodes( true, false ); 03449 } 03450 03451 /*! 03452 \param $version No longer in use, published nodes are used instead. 03453 \param $asObject If true it fetches PHP objects, otherwise it fetches IDs. 03454 \return the parnet nodes for the current object. 03455 */ 03456 function parentNodes( $version = false, $asObject = true ) 03457 { 03458 // We no longer use node-assignment table to find the parents but uses 03459 // the 'published' tree structure. 03460 $retNodes = array(); 03461 03462 $parentNodeIDs = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( $this->ID ); 03463 if ( !$parentNodeIDs ) 03464 { 03465 return $retNodes; 03466 } 03467 if ( $asObject ) 03468 { 03469 $retNodes = eZContentObjectTreeNode::fetch( $parentNodeIDs ); 03470 if ( !is_array( $retNodes ) ) 03471 { 03472 $retNodes = array( $retNodes ); 03473 } 03474 } 03475 else 03476 { 03477 $retNodes = $parentNodeIDs; 03478 } 03479 03480 return $retNodes; 03481 } 03482 03483 /*! 03484 Creates a new node assignment that will place the object as child of node \a $nodeID. 03485 \return The eZNodeAssignment object it created 03486 \param $parentNodeID The node ID of the parent node 03487 \param $isMain \c true if the created node is the main node of the object 03488 \param $remoteID A string denoting the unique remote ID of the assignment or \c false for no remote id. 03489 \param $sortField 03490 \param $sortOrder 03491 \note The returned assignment will already be stored in the database 03492 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 03493 the calls within a db transaction; thus within db->begin and db->commit. 03494 */ 03495 function createNodeAssignment( $parentNodeID, $isMain, $remoteID = false, $sortField = eZContentObjectTreeNode::SORT_FIELD_PUBLISHED, $sortOrder = eZContentObjectTreeNode::SORT_ORDER_DESC ) 03496 { 03497 $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $this->attribute( 'id' ), 03498 'contentobject_version' => $this->attribute( 'current_version' ), 03499 'parent_node' => $parentNodeID, 03500 'is_main' => ( $isMain ? 1 : 0 ), 03501 'sort_field' => $sortField, 03502 'sort_order' => $sortOrder ) ); 03503 if ( $remoteID !== false ) 03504 { 03505 $nodeAssignment->setAttribute( 'remote_id', $remoteID ); 03506 } 03507 $nodeAssignment->store(); 03508 return $nodeAssignment; 03509 } 03510 03511 /* 03512 * Creates object with nodeAssignment from given parent Node, class ID and language code. 03513 */ 03514 static function createWithNodeAssignment( $parentNode, $contentClassID, $languageCode, $remoteID = false ) 03515 { 03516 $class = eZContentClass::fetch( $contentClassID ); 03517 $parentObject = $parentNode->attribute( 'object' ); 03518 03519 // Check if the user has access to create a folder here 03520 if ( $class instanceof eZContentClass and 03521 $parentObject->checkAccess( 'create', $contentClassID, false, false, $languageCode ) == '1' ) 03522 { 03523 // Set section of the newly created object to the section's value of it's parent object 03524 $sectionID = $parentObject->attribute( 'section_id' ); 03525 03526 $db = eZDB::instance(); 03527 $db->begin(); 03528 $contentObject = $class->instantiateIn( $languageCode, false, $sectionID, false, eZContentObjectVersion::STATUS_INTERNAL_DRAFT ); 03529 $nodeAssignment = $contentObject->createNodeAssignment( $parentNode->attribute( 'node_id' ), 03530 true, $remoteID, 03531 $class->attribute( 'sort_field' ), 03532 $class->attribute( 'sort_order' ) ); 03533 $db->commit(); 03534 return $contentObject; 03535 } 03536 return null; 03537 } 03538 03539 03540 /*! 03541 Returns the node assignments for the current object. 03542 */ 03543 function assignedNodes( $asObject = true ) 03544 { 03545 $contentobjectID = $this->attribute( 'id' ); 03546 if ( $contentobjectID == null ) 03547 { 03548 $retValue = array(); 03549 return $retValue; 03550 } 03551 $query = "SELECT ezcontentobject.*, 03552 ezcontentobject_tree.*, 03553 ezcontentclass.serialized_name_list as class_serialized_name_list, 03554 ezcontentclass.identifier as class_identifier, 03555 ezcontentclass.is_container as is_container 03556 FROM ezcontentobject_tree, 03557 ezcontentobject, 03558 ezcontentclass 03559 WHERE contentobject_id=$contentobjectID AND 03560 ezcontentobject_tree.contentobject_id=ezcontentobject.id AND 03561 ezcontentclass.version=0 AND 03562 ezcontentclass.id = ezcontentobject.contentclass_id 03563 ORDER BY path_string"; 03564 $db = eZDB::instance(); 03565 $nodesListArray = $db->arrayQuery( $query ); 03566 if ( $asObject == true ) 03567 { 03568 $nodes = eZContentObjectTreeNode::makeObjectsArray( $nodesListArray ); 03569 return $nodes; 03570 } 03571 else 03572 return $nodesListArray; 03573 } 03574 03575 /*! 03576 Returns the main node id for the current object. 03577 */ 03578 function mainNodeID() 03579 { 03580 if ( !is_numeric( $this->MainNodeID ) ) 03581 { 03582 $mainNodeID = eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ) ); 03583 $this->MainNodeID = $mainNodeID; 03584 } 03585 return $this->MainNodeID; 03586 } 03587 03588 function mainNode() 03589 { 03590 return eZContentObjectTreeNode::findMainNode( $this->attribute( 'id' ), true ); 03591 } 03592 03593 /*! 03594 Sets the permissions for this object. 03595 */ 03596 function setPermissions( $permissionArray ) 03597 { 03598 $this->Permissions =& $permissionArray; 03599 } 03600 03601 /*! 03602 Returns the permission for the current object. 03603 */ 03604 function permissions( ) 03605 { 03606 return $this->Permissions; 03607 } 03608 03609 function canEditLanguages() 03610 { 03611 $availableLanguages = $this->availableLanguages(); 03612 $languages = array(); 03613 03614 foreach ( eZContentLanguage::prioritizedLanguages() as $language ) 03615 { 03616 $languageCode = $language->attribute( 'locale' ); 03617 if ( in_array( $languageCode, $availableLanguages ) && 03618 $this->canEdit( false, false, false, $languageCode ) ) 03619 { 03620 $languages[] = $language; 03621 } 03622 } 03623 03624 return $languages; 03625 } 03626 03627 function canCreateLanguages() 03628 { 03629 $availableLanguages = $this->availableLanguages(); 03630 $languages = array(); 03631 foreach ( eZContentLanguage::prioritizedLanguages() as $language ) 03632 { 03633 $languageCode = $language->attribute( 'locale' ); 03634 if ( !in_array( $languageCode, $availableLanguages ) && 03635 $this->checkAccess( 'edit', false, false, false, $languageCode ) ) 03636 { 03637 $languages[] = $language; 03638 } 03639 } 03640 03641 return $languages; 03642 } 03643 03644 function checkGroupLimitationAccess( $limitationValueList, $userID, $contentObjectID = false ) 03645 { 03646 $access = 'denied'; 03647 03648 if ( is_array( $limitationValueList ) && is_numeric( $userID ) ) 03649 { 03650 if ( $contentObjectID !== false ) 03651 { 03652 $contentObject = eZContentObject::fetch( $contentObjectID ); 03653 } 03654 else 03655 { 03656 $contentObject = $this; 03657 } 03658 03659 if ( is_object( $contentObject ) ) 03660 { 03661 // limitation value == 1, means "self group" 03662 if ( in_array( 1, $limitationValueList ) ) 03663 { 03664 // no need to check groups if user ownes this object 03665 $ownerID = $contentObject->attribute( 'owner_id' ); 03666 if ( $ownerID == $userID || $contentObject->attribute( 'id' ) == $userID ) 03667 { 03668 $access = 'allowed'; 03669 } 03670 else 03671 { 03672 // get parent node ids for 'user' and 'owner' 03673 $groupList = eZContentObjectTreeNode::getParentNodeIdListByContentObjectID( array( $userID, $ownerID ), true ); 03674 03675 // find group(s) which is common for 'user' and 'owner' 03676 $commonGroup = array_intersect( $groupList[$userID], $groupList[$ownerID] ); 03677 03678 if ( count( $commonGroup ) > 0 ) 03679 { 03680 // ok, we have at least 1 common group 03681 $access = 'allowed'; 03682 } 03683 } 03684 } 03685 } 03686 } 03687 03688 return $access; 03689 } 03690 03691 /*! 03692 Check access for the current object 03693 03694 \param function name ( edit, read, remove, etc. ) 03695 \param original class ID ( used to check access for object creation ), default false 03696 \param parent class id ( used to check access for object creation ), default false 03697 \param return access list instead of access result (optional, default false ) 03698 03699 \return 1 if has access, 0 if not. 03700 If returnAccessList is set to true, access list is returned 03701 */ 03702 function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false ) 03703 { 03704 $classID = $originalClassID; 03705 $user = eZUser::currentUser(); 03706 $userID = $user->attribute( 'contentobject_id' ); 03707 $origFunctionName = $functionName; 03708 03709 // Fetch the ID of the language if we get a string with a language code 03710 // e.g. 'eng-GB' 03711 $originalLanguage = $language; 03712 if ( is_string( $language ) && strlen( $language ) > 0 ) 03713 { 03714 $language = eZContentLanguage::idByLocale( $language ); 03715 } 03716 else 03717 { 03718 $language = false; 03719 } 03720 03721 // This will be filled in with the available languages of the object 03722 // if a Language check is performed. 03723 $languageList = false; 03724 03725 // The 'move' function simply reuses 'edit' for generic access 03726 // but adds another top-level check below 03727 // The original function is still available in $origFunctionName 03728 if ( $functionName == 'move' ) 03729 $functionName = 'edit'; 03730 03731 $accessResult = $user->hasAccessTo( 'content' , $functionName ); 03732 $accessWord = $accessResult['accessWord']; 03733 03734 /* 03735 // Uncomment this part if 'create' permissions should become implied 'edit'. 03736 // Merges in 'create' policies with 'edit' 03737 if ( $functionName == 'edit' && 03738 !in_array( $accessWord, array( 'yes', 'no' ) ) ) 03739 { 03740 // Add in create policies. 03741 $accessExtraResult = $user->hasAccessTo( 'content', 'create' ); 03742 if ( $accessExtraResult['accessWord'] != 'no' ) 03743 { 03744 $accessWord = $accessExtraResult['accessWord']; 03745 if ( isset( $accessExtraResult['policies'] ) ) 03746 { 03747 $accessResult['policies'] = array_merge( $accessResult['policies'], 03748 $accessExtraResult['policies'] ); 03749 } 03750 if ( isset( $accessExtraResult['accessList'] ) ) 03751 { 03752 $accessResult['accessList'] = array_merge( $accessResult['accessList'], 03753 $accessExtraResult['accessList'] ); 03754 } 03755 } 03756 } 03757 */ 03758 03759 if ( $origFunctionName == 'remove' or 03760 $origFunctionName == 'move' ) 03761 { 03762 $mainNode = $this->attribute( 'main_node' ); 03763 // We do not allow these actions on objects placed at top-level 03764 // - remove 03765 // - move 03766 if ( $mainNode and $mainNode->attribute( 'parent_node_id' ) <= 1 ) 03767 { 03768 return 0; 03769 } 03770 } 03771 03772 if ( $classID === false ) 03773 { 03774 $classID = $this->attribute( 'contentclass_id' ); 03775 } 03776 if ( $accessWord == 'yes' ) 03777 { 03778 return 1; 03779 } 03780 else if ( $accessWord == 'no' ) 03781 { 03782 if ( $functionName == 'edit' ) 03783 { 03784 // Check if we have 'create' access under the main parent 03785 if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) ) 03786 { 03787 $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) ); 03788 $parentObj = $mainNode[0]->attribute( 'parent_contentobject' ); 03789 $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ), 03790 $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage ); 03791 return $result; 03792 } 03793 else 03794 { 03795 return 0; 03796 } 03797 } 03798 03799 if ( $returnAccessList === false ) 03800 { 03801 return 0; 03802 } 03803 else 03804 { 03805 return $accessResult['accessList']; 03806 } 03807 } 03808 else 03809 { 03810 $policies =& $accessResult['policies']; 03811 $access = 'denied'; 03812 foreach ( array_keys( $policies ) as $pkey ) 03813 { 03814 $limitationArray =& $policies[ $pkey ]; 03815 if ( $access == 'allowed' ) 03816 { 03817 break; 03818 } 03819 03820 $limitationList = array(); 03821 if ( isset( $limitationArray['Subtree' ] ) ) 03822 { 03823 $checkedSubtree = false; 03824 } 03825 else 03826 { 03827 $checkedSubtree = true; 03828 $accessSubtree = false; 03829 } 03830 if ( isset( $limitationArray['Node'] ) ) 03831 { 03832 $checkedNode = false; 03833 } 03834 else 03835 { 03836 $checkedNode = true; 03837 $accessNode = false; 03838 } 03839 foreach ( array_keys( $limitationArray ) as $key ) 03840 { 03841 $access = 'denied'; 03842 switch( $key ) 03843 { 03844 case 'Class': 03845 { 03846 if ( $functionName == 'create' and 03847 !$originalClassID ) 03848 { 03849 $access = 'allowed'; 03850 } 03851 else if ( $functionName == 'create' and 03852 in_array( $classID, $limitationArray[$key] ) ) 03853 { 03854 $access = 'allowed'; 03855 } 03856 else if ( $functionName != 'create' and 03857 in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key] ) ) 03858 { 03859 $access = 'allowed'; 03860 } 03861 else 03862 { 03863 $access = 'denied'; 03864 $limitationList = array( 'Limitation' => $key, 03865 'Required' => $limitationArray[$key] ); 03866 } 03867 } break; 03868 03869 case 'ParentClass': 03870 { 03871 03872 if ( in_array( $this->attribute( 'contentclass_id' ), $limitationArray[$key] ) ) 03873 { 03874 $access = 'allowed'; 03875 } 03876 else 03877 { 03878 $access = 'denied'; 03879 $limitationList = array( 'Limitation' => $key, 03880 'Required' => $limitationArray[$key] ); 03881 } 03882 } break; 03883 03884 case 'ParentDepth': 03885 { 03886 $assignedNodes = $this->attribute( 'assigned_nodes' ); 03887 if ( count( $assignedNodes ) > 0 ) 03888 { 03889 foreach ( $assignedNodes as $assignedNode ) 03890 { 03891 $depth = $assignedNode->attribute( 'depth' ); 03892 if ( in_array( $depth, $limitationArray[$key] ) ) 03893 { 03894 $access = 'allowed'; 03895 break; 03896 } 03897 } 03898 } 03899 03900 if ( $access != 'allowed' ) 03901 { 03902 $access = 'denied'; 03903 $limitationList = array( 'Limitation' => $key, 03904 'Required' => $limitationArray[$key] ); 03905 } 03906 } break; 03907 03908 case 'Section': 03909 case 'User_Section': 03910 { 03911 if ( in_array( $this->attribute( 'section_id' ), $limitationArray[$key] ) ) 03912 { 03913 $access = 'allowed'; 03914 } 03915 else 03916 { 03917 $access = 'denied'; 03918 $limitationList = array( 'Limitation' => $key, 03919 'Required' => $limitationArray[$key] ); 03920 } 03921 } break; 03922 03923 case 'Language': 03924 { 03925 $languageMask = 0; 03926 // If we don't have a language list yet we need to fetch it 03927 // and optionally filter out based on $language. 03928 03929 if ( $functionName == 'create' ) 03930 { 03931 // If the function is 'create' we do not use the language_mask for matching. 03932 if ( $language !== false ) 03933 { 03934 $languageMask = $language; 03935 } 03936 else 03937 { 03938 // If the create is used and no language specified then 03939 // we need to match against all possible languages (which 03940 // is all bits set, ie. -1). 03941 $languageMask = -1; 03942 } 03943 } 03944 else 03945 { 03946 if ( $language !== false ) 03947 { 03948 if ( $languageList === false ) 03949 { 03950 $languageMask = (int)$this->attribute( 'language_mask' ); 03951 // We are restricting language check to just one language 03952 $languageMask &= (int)$language; 03953 // If the resulting mask is 0 it means that the user is trying to 03954 // edit a language which does not exist, ie. translating. 03955 // The mask will then become the language trying to edit. 03956 if ( $languageMask == 0 ) 03957 { 03958 $languageMask = $language; 03959 } 03960 } 03961 } 03962 else 03963 { 03964 $languageMask = -1; 03965 } 03966 } 03967 // Fetch limit mask for limitation list 03968 $limitMask = eZContentLanguage::maskByLocale( $limitationArray[$key] ); 03969 if ( ( $languageMask & $limitMask ) != 0 ) 03970 { 03971 $access = 'allowed'; 03972 } 03973 else 03974 { 03975 $access = 'denied'; 03976 $limitationList = array( 'Limitation' => $key, 03977 'Required' => $limitationArray[$key] ); 03978 } 03979 } break; 03980 03981 case 'Owner': 03982 case 'ParentOwner': 03983 { 03984 // if limitation value == 2, anonymous limited to current session. 03985 if ( in_array( 2, $limitationArray[$key] ) && 03986 $user->isAnonymous() ) 03987 { 03988 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' ); 03989 if ( $createdObjectIDList && 03990 in_array( $this->ID, unserialize( $createdObjectIDList ) ) ) 03991 { 03992 $access = 'allowed'; 03993 } 03994 } 03995 else if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID ) 03996 { 03997 $access = 'allowed'; 03998 } 03999 if ( $access != 'allowed' ) 04000 { 04001 $access = 'denied'; 04002 $limitationList = array ( 'Limitation' => $key, 'Required' => $limitationArray[$key] ); 04003 } 04004 } break; 04005 04006 case 'Group': 04007 case 'ParentGroup': 04008 { 04009 $access = $this->checkGroupLimitationAccess( $limitationArray[$key], $userID ); 04010 04011 if ( $access != 'allowed' ) 04012 { 04013 $access = 'denied'; 04014 $limitationList = array ( 'Limitation' => $key, 04015 'Required' => $limitationArray[$key] ); 04016 } 04017 } break; 04018 04019 case 'State': 04020 { 04021 if ( count( array_intersect( $limitationArray[$key], $this->attribute( 'state_id_array' ) ) ) == 0 ) 04022 { 04023 $access = 'denied'; 04024 $limitationList = array ( 'Limitation' => $key, 04025 'Required' => $limitationArray[$key] ); 04026 } 04027 else 04028 { 04029 $access = 'allowed'; 04030 } 04031 } break; 04032 04033 case 'Node': 04034 { 04035 $accessNode = false; 04036 $mainNodeID = $this->attribute( 'main_node_id' ); 04037 foreach ( $limitationArray[$key] as $nodeID ) 04038 { 04039 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 04040 $limitationNodeID = $node['main_node_id']; 04041 if ( $mainNodeID == $limitationNodeID ) 04042 { 04043 $access = 'allowed'; 04044 $accessNode = true; 04045 break; 04046 } 04047 } 04048 if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree ) 04049 { 04050 $access = 'denied'; 04051 // ??? TODO: if there is a limitation on Subtree, return two limitations? 04052 $limitationList = array( 'Limitation' => $key, 04053 'Required' => $limitationArray[$key] ); 04054 } 04055 else 04056 { 04057 $access = 'allowed'; 04058 } 04059 $checkedNode = true; 04060 } break; 04061 04062 case 'Subtree': 04063 { 04064 $accessSubtree = false; 04065 $assignedNodes = $this->attribute( 'assigned_nodes' ); 04066 if ( count( $assignedNodes ) != 0 ) 04067 { 04068 foreach ( $assignedNodes as $assignedNode ) 04069 { 04070 $path = $assignedNode->attribute( 'path_string' ); 04071 $subtreeArray = $limitationArray[$key]; 04072 foreach ( $subtreeArray as $subtreeString ) 04073 { 04074 if ( strstr( $path, $subtreeString ) ) 04075 { 04076 $access = 'allowed'; 04077 $accessSubtree = true; 04078 break; 04079 } 04080 } 04081 } 04082 } 04083 else 04084 { 04085 $parentNodes = $this->attribute( 'parent_nodes' ); 04086 if ( count( $parentNodes ) == 0 ) 04087 { 04088 if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID ) 04089 { 04090 $access = 'allowed'; 04091 $accessSubtree = true; 04092 } 04093 } 04094 else 04095 { 04096 foreach ( $parentNodes as $parentNode ) 04097 { 04098 $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false ); 04099 $path = $parentNode['path_string']; 04100 04101 $subtreeArray = $limitationArray[$key]; 04102 foreach ( $subtreeArray as $subtreeString ) 04103 { 04104 if ( strstr( $path, $subtreeString ) ) 04105 { 04106 $access = 'allowed'; 04107 $accessSubtree = true; 04108 break; 04109 } 04110 } 04111 } 04112 } 04113 } 04114 if ( $access != 'allowed' && $checkedNode && !$accessNode ) 04115 { 04116 $access = 'denied'; 04117 // ??? TODO: if there is a limitation on Node, return two limitations? 04118 $limitationList = array( 'Limitation' => $key, 04119 'Required' => $limitationArray[$key] ); 04120 } 04121 else 04122 { 04123 $access = 'allowed'; 04124 } 04125 $checkedSubtree = true; 04126 } break; 04127 04128 case 'User_Subtree': 04129 { 04130 $assignedNodes = $this->attribute( 'assigned_nodes' ); 04131 if ( count( $assignedNodes ) != 0 ) 04132 { 04133 foreach ( $assignedNodes as $assignedNode ) 04134 { 04135 $path = $assignedNode->attribute( 'path_string' ); 04136 $subtreeArray = $limitationArray[$key]; 04137 foreach ( $subtreeArray as $subtreeString ) 04138 { 04139 if ( strstr( $path, $subtreeString ) ) 04140 { 04141 $access = 'allowed'; 04142 } 04143 } 04144 } 04145 } 04146 else 04147 { 04148 $parentNodes = $this->attribute( 'parent_nodes' ); 04149 if ( count( $parentNodes ) == 0 ) 04150 { 04151 if ( $this->attribute( 'owner_id' ) == $userID || $this->ID == $userID ) 04152 { 04153 $access = 'allowed'; 04154 } 04155 } 04156 else 04157 { 04158 foreach ( $parentNodes as $parentNode ) 04159 { 04160 $parentNode = eZContentObjectTreeNode::fetch( $parentNode, false, false ); 04161 $path = $parentNode['path_string']; 04162 04163 $subtreeArray = $limitationArray[$key]; 04164 foreach ( $subtreeArray as $subtreeString ) 04165 { 04166 if ( strstr( $path, $subtreeString ) ) 04167 { 04168 $access = 'allowed'; 04169 break; 04170 } 04171 } 04172 } 04173 } 04174 } 04175 if ( $access != 'allowed' ) 04176 { 04177 $access = 'denied'; 04178 $limitationList = array( 'Limitation' => $key, 04179 'Required' => $limitationArray[$key] ); 04180 } 04181 } break; 04182 04183 default: 04184 { 04185 if ( strncmp( $key, 'StateGroup_', 11 ) === 0 ) 04186 { 04187 if ( count( array_intersect( $limitationArray[$key], 04188 $this->attribute( 'state_id_array' ) ) ) == 0 ) 04189 { 04190 $access = 'denied'; 04191 $limitationList = array ( 'Limitation' => $key, 04192 'Required' => $limitationArray[$key] ); 04193 } 04194 else 04195 { 04196 $access = 'allowed'; 04197 } 04198 } 04199 } 04200 } 04201 if ( $access == 'denied' ) 04202 { 04203 break; 04204 } 04205 } 04206 04207 $policyList[] = array( 'PolicyID' => $pkey, 04208 'LimitationList' => $limitationList ); 04209 } 04210 04211 if ( $access == 'denied' ) 04212 { 04213 if ( $functionName == 'edit' ) 04214 { 04215 // Check if we have 'create' access under the main parent 04216 if ( $this->attribute( 'current_version' ) == 1 && !$this->attribute( 'status' ) ) 04217 { 04218 $mainNode = eZNodeAssignment::fetchForObject( $this->attribute( 'id' ), $this->attribute( 'current_version' ) ); 04219 $parentObj = $mainNode[0]->attribute( 'parent_contentobject' ); 04220 $result = $parentObj->checkAccess( 'create', $this->attribute( 'contentclass_id' ), 04221 $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage ); 04222 if ( $result ) 04223 { 04224 $access = 'allowed'; 04225 } 04226 return $result; 04227 } 04228 } 04229 } 04230 04231 if ( $access == 'denied' ) 04232 { 04233 if ( $returnAccessList === false ) 04234 { 04235 return 0; 04236 } 04237 else 04238 { 04239 return array( 'FunctionRequired' => array ( 'Module' => 'content', 04240 'Function' => $origFunctionName, 04241 'ClassID' => $classID, 04242 'MainNodeID' => $this->attribute( 'main_node_id' ) ), 04243 'PolicyList' => $policyList ); 04244 } 04245 } 04246 else 04247 { 04248 return 1; 04249 } 04250 } 04251 } 04252 04253 // code-template::create-block: class-list-from-policy, is-object 04254 // code-template::auto-generated:START class-list-from-policy 04255 // This code is automatically generated from templates/classlistfrompolicy.ctpl 04256 // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD 04257 04258 function classListFromPolicy( $policy, $allowedLanguageCodes = false ) 04259 { 04260 $canCreateClassIDListPart = array(); 04261 $hasClassIDLimitation = false; 04262 $user = eZUser::currentUser(); 04263 $userID = $user->attribute( 'contentobject_id' ); 04264 04265 if ( isset( $policy['ParentOwner'] ) ) 04266 { 04267 // if limitation value == 2, anonymous limited to current session. 04268 if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() ) 04269 { 04270 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' ); 04271 if ( !$createdObjectIDList || 04272 !in_array( $this->ID, unserialize( $createdObjectIDList ) ) ) 04273 { 04274 return array(); 04275 } 04276 } 04277 else if ( $this->attribute( 'owner_id' ) != $userID && 04278 $this->ID != $userID ) 04279 { 04280 return array(); 04281 } 04282 } 04283 04284 if ( isset( $policy['ParentGroup'] ) ) 04285 { 04286 $access = $this->checkGroupLimitationAccess( $policy['ParentGroup'], $userID ); 04287 if ( $access !== 'allowed' ) 04288 { 04289 return array(); 04290 } 04291 } 04292 04293 if ( isset( $policy['Class'] ) ) 04294 { 04295 $canCreateClassIDListPart = $policy['Class']; 04296 $hasClassIDLimitation = true; 04297 } 04298 04299 if ( isset( $policy['User_Section'] ) ) 04300 { 04301 if ( !in_array( $this->attribute( 'section_id' ), $policy['User_Section'] ) ) 04302 { 04303 return array(); 04304 } 04305 } 04306 04307 if ( isset( $policy['User_Subtree'] ) ) 04308 { 04309 $allowed = false; 04310 $assignedNodes = $this->attribute( 'assigned_nodes' ); 04311 foreach ( $assignedNodes as $assignedNode ) 04312 { 04313 $path = $assignedNode->attribute( 'path_string' ); 04314 foreach ( $policy['User_Subtree'] as $subtreeString ) 04315 { 04316 if ( strstr( $path, $subtreeString ) ) 04317 { 04318 $allowed = true; 04319 break; 04320 } 04321 } 04322 } 04323 if( !$allowed ) 04324 { 04325 return array(); 04326 } 04327 } 04328 04329 if ( isset( $policy['Section'] ) ) 04330 { 04331 if ( !in_array( $this->attribute( 'section_id' ), $policy['Section'] ) ) 04332 { 04333 return array(); 04334 } 04335 } 04336 04337 if ( isset( $policy['ParentClass'] ) ) 04338 { 04339 if ( !in_array( $this->attribute( 'contentclass_id' ), $policy['ParentClass'] ) ) 04340 { 04341 return array(); 04342 } 04343 } 04344 04345 if ( isset( $policy['Assigned'] ) ) 04346 { 04347 if ( $this->attribute( 'owner_id' ) != $userID ) 04348 { 04349 return array(); 04350 } 04351 } 04352 04353 $allowedNode = false; 04354 if ( isset( $policy['Node'] ) ) 04355 { 04356 $allowed = false; 04357 foreach( $policy['Node'] as $nodeID ) 04358 { 04359 $mainNodeID = $this->attribute( 'main_node_id' ); 04360 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false ); 04361 if ( $mainNodeID == $node['main_node_id'] ) 04362 { 04363 $allowed = true; 04364 $allowedNode = true; 04365 break; 04366 } 04367 } 04368 if ( !$allowed && !isset( $policy['Subtree'] ) ) 04369 { 04370 return array(); 04371 } 04372 } 04373 04374 if ( isset( $policy['Subtree'] ) ) 04375 { 04376 $allowed = false; 04377 $assignedNodes = $this->attribute( 'assigned_nodes' ); 04378 foreach ( $assignedNodes as $assignedNode ) 04379 { 04380 $path = $assignedNode->attribute( 'path_string' ); 04381 foreach ( $policy['Subtree'] as $subtreeString ) 04382 { 04383 if ( strstr( $path, $subtreeString ) ) 04384 { 04385 $allowed = true; 04386 break; 04387 } 04388 } 04389 } 04390 if ( !$allowed && !$allowedNode ) 04391 { 04392 return array(); 04393 } 04394 } 04395 04396 if ( isset( $policy['Language'] ) ) 04397 { 04398 if ( $allowedLanguageCodes ) 04399 { 04400 $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] ); 04401 } 04402 else 04403 { 04404 $allowedLanguageCodes = $policy['Language']; 04405 } 04406 } 04407 04408 if ( $hasClassIDLimitation ) 04409 { 04410 return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes ); 04411 } 04412 return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes ); 04413 } 04414 04415 // This code is automatically generated from templates/classlistfrompolicy.ctpl 04416 // code-template::auto-generated:END class-list-from-policy 04417 04418 // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation 04419 // code-template::auto-generated:START can-instantiate-class-list 04420 // This code is automatically generated from templates/classcreatelist.ctpl 04421 // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD 04422 04423 /*! 04424 Finds all classes that the current user can create objects from and returns. 04425 It is also possible to filter the list event more with \a $includeFilter and \a $groupList. 04426 04427 \param $asObject If \c true then it return eZContentClass objects, if not it will 04428 be an associative array with \c name and \c id keys. 04429 \param $includeFilter If \c true then it will include only from class groups defined in 04430 \a $groupList, if not it will exclude those groups. 04431 \param $groupList An array with class group IDs that should be used in filtering, use 04432 \c false if you do not wish to filter at all. 04433 \param $fetchID A unique name for the current fetch, this must be supplied when filtering is 04434 used if you want caching to work. 04435 */ 04436 function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false ) 04437 { 04438 $ini = eZINI::instance(); 04439 $groupArray = array(); 04440 $languageCodeList = eZContentLanguage::fetchLocaleList(); 04441 $allowedLanguages = array( '*' => array() ); 04442 04443 $user = eZUser::currentUser(); 04444 $accessResult = $user->hasAccessTo( 'content' , 'create' ); 04445 $accessWord = $accessResult['accessWord']; 04446 04447 $classIDArray = array(); 04448 $classList = array(); 04449 $fetchAll = false; 04450 if ( $accessWord == 'yes' ) 04451 { 04452 $fetchAll = true; 04453 $allowedLanguages['*'] = $languageCodeList; 04454 } 04455 else if ( $accessWord == 'no' ) 04456 { 04457 // Cannot create any objects, return empty list. 04458 return $classList; 04459 } 04460 else 04461 { 04462 $policies = $accessResult['policies']; 04463 foreach ( $policies as $policyKey => $policy ) 04464 { 04465 $policyArray = $this->classListFromPolicy( $policy, $languageCodeList ); 04466 if ( empty( $policyArray ) ) 04467 { 04468 continue; 04469 } 04470 $classIDArrayPart = $policyArray['classes']; 04471 $languageCodeArrayPart = $policyArray['language_codes']; 04472 // No class limitation for this policy AND no previous limitation(s) 04473 if ( $classIDArrayPart == '*' && empty( $classIDArray ) ) 04474 { 04475 $fetchAll = true; 04476 $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) ); 04477 } 04478 else if ( is_array( $classIDArrayPart ) ) 04479 { 04480 $fetchAll = false; 04481 foreach( $classIDArrayPart as $class ) 04482 { 04483 if ( isset( $allowedLanguages[$class] ) ) 04484 { 04485 $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) ); 04486 } 04487 else 04488 { 04489 $allowedLanguages[$class] = $languageCodeArrayPart; 04490 } 04491 } 04492 $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) ); 04493 } 04494 } 04495 } 04496 04497 $db = eZDB::instance(); 04498 04499 $filterTableSQL = ''; 04500 $filterSQL = ''; 04501 // Create extra SQL statements for the class group filters. 04502 if ( is_array( $groupList ) ) 04503 { 04504 if ( count( $groupList ) == 0 ) 04505 { 04506 return $classList; 04507 } 04508 04509 $filterTableSQL = ', ezcontentclass_classgroup ccg'; 04510 $filterSQL = ( " AND" . 04511 " cc.id = ccg.contentclass_id AND" . 04512 " " ); 04513 $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' ); 04514 } 04515 04516 $classNameFilter = eZContentClassName::sqlFilter( 'cc' ); 04517 04518 if ( $fetchAll ) 04519 { 04520 // If $asObject is true we fetch all fields in class 04521 $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]"; 04522 $rows = $db->arrayQuery( "SELECT DISTINCT $fields " . 04523 "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " . 04524 "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " . 04525 "ORDER BY $classNameFilter[nameField] ASC" ); 04526 $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject ); 04527 } 04528 else 04529 { 04530 // If the constrained class list is empty we are not allowed to create any class 04531 if ( count( $classIDArray ) == 0 ) 04532 { 04533 return $classList; 04534 } 04535 04536 $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' ); 04537 // If $asObject is true we fetch all fields in class 04538 $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]"; 04539 $rows = $db->arrayQuery( "SELECT DISTINCT $fields " . 04540 "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " . 04541 "WHERE $classIDCondition AND" . 04542 " cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " . 04543 "ORDER BY $classNameFilter[nameField] ASC" ); 04544 $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject ); 04545 } 04546 04547 if ( $asObject ) 04548 { 04549 foreach ( $classList as $key => $class ) 04550 { 04551 $id = $class->attribute( 'id' ); 04552 if ( isset( $allowedLanguages[$id] ) ) 04553 { 04554 $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) ); 04555 } 04556 else 04557 { 04558 $languageCodes = $allowedLanguages['*']; 04559 } 04560 $classList[$key]->setCanInstantiateLanguages( $languageCodes ); 04561 } 04562 } 04563 04564 eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" ); 04565 return $classList; 04566 } 04567 04568 // This code is automatically generated from templates/classcreatelist.ctpl 04569 // code-template::auto-generated:END can-instantiate-class-list 04570 04571 /*! 04572 Get accesslist for specified function 04573 04574 \param function 04575 04576 \return AccessList 04577 */ 04578 function accessList( $function ) 04579 { 04580 switch( $function ) 04581 { 04582 case 'read': 04583 { 04584 return $this->checkAccess( 'read', false, false, true ); 04585 } break; 04586 04587 case 'edit': 04588 { 04589 return $this->checkAccess( 'edit', false, false, true ); 04590 } break; 04591 } 04592 return 0; 04593 } 04594 04595 /*! 04596 \return \c true if the current user can read this content object. 04597 */ 04598 function canRead( ) 04599 { 04600 if ( !isset( $this->Permissions["can_read"] ) ) 04601 { 04602 $this->Permissions["can_read"] = $this->checkAccess( 'read' ); 04603 } 04604 return ( $this->Permissions["can_read"] == 1 ); 04605 } 04606 04607 /*! 04608 \return \c true if the current user can create a pdf of this content object. 04609 */ 04610 function canPdf( ) 04611 { 04612 if ( !isset( $this->Permissions["can_pdf"] ) ) 04613 { 04614 $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' ); 04615 } 04616 return ( $this->Permissions["can_pdf"] == 1 ); 04617 } 04618 04619 /*! 04620 \return \c true if the node can be viewed as embeded object by the current user. 04621 \sa checkAccess(). 04622 */ 04623 function canViewEmbed( ) 04624 { 04625 if ( !isset( $this->Permissions["can_view_embed"] ) ) 04626 { 04627 $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' ); 04628 } 04629 return ( $this->Permissions["can_view_embed"] == 1 ); 04630 } 04631 04632 /*! 04633 \return \c true if the current user can diff this content object. 04634 */ 04635 function canDiff( ) 04636 { 04637 if ( !isset( $this->Permissions["can_diff"] ) ) 04638 { 04639 $this->Permissions["can_diff"] = $this->checkAccess( 'diff' ); 04640 } 04641 return ( $this->Permissions["can_diff"] == 1 ); 04642 } 04643 04644 function canCreate( ) 04645 { 04646 if ( !isset( $this->Permissions["can_create"] ) ) 04647 { 04648 $this->Permissions["can_create"] = $this->checkAccess( 'create' ); 04649 } 04650 return ( $this->Permissions["can_create"] == 1 ); 04651 } 04652 04653 function canEdit( $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false ) 04654 { 04655 $isCalledClean = ( func_num_args() == 0 ); 04656 if ( isset( $this->Permissions["can_edit"] ) && $isCalledClean ) 04657 { 04658 $canEdit = $this->Permissions["can_edit"]; 04659 } 04660 else 04661 { 04662 $canEdit = $this->checkAccess( 'edit', $originalClassID, $parentClassID, $returnAccessList, $language ); 04663 if ( $canEdit != 1 ) 04664 { 04665 $user = eZUser::currentUser(); 04666 if ( $user->attribute( 'contentobject_id' ) === $this->attribute( 'id' ) ) 04667 { 04668 $access = $user->hasAccessTo( 'user', 'selfedit' ); 04669 if ( $access['accessWord'] == 'yes' ) 04670 { 04671 $canEdit = 1; 04672 } 04673 } 04674 } 04675 04676 if ( $isCalledClean ) 04677 { 04678 $this->Permissions["can_edit"] = $canEdit; 04679 } 04680 } 04681 return ( $canEdit == 1 ); 04682 } 04683 04684 function canTranslate( ) 04685 { 04686 if ( !isset( $this->Permissions["can_translate"] ) ) 04687 { 04688 $this->Permissions["can_translate"] = $this->checkAccess( 'translate' ); 04689 if ( $this->Permissions["can_translate"] != 1 ) 04690 { 04691 $user = eZUser::currentUser(); 04692 if ( $user->id() == $this->attribute( 'id' ) ) 04693 { 04694 $access = $user->hasAccessTo( 'user', 'selfedit' ); 04695 if ( $access['accessWord'] == 'yes' ) 04696 { 04697 $this->Permissions["can_translate"] = 1; 04698 } 04699 } 04700 } 04701 } 04702 return ( $this->Permissions["can_translate"] == 1 ); 04703 } 04704 04705 function canRemove( ) 04706 { 04707 04708 if ( !isset( $this->Permissions["can_remove"] ) ) 04709 { 04710 $this->Permissions["can_remove"] = $this->checkAccess( 'remove' ); 04711 } 04712 return ( $this->Permissions["can_remove"] == 1 ); 04713 } 04714 04715 /*! 04716 Check if the object can be moved. (actually checks 'edit' and 'remove' permissions) 04717 \return \c true if the object can be moved by the current user. 04718 \sa checkAccess(). 04719 \deprecated The function canMove() is preferred since its naming is clearer. 04720 */ 04721 function canMove( ) 04722 { 04723 return $this->canMoveFrom(); 04724 } 04725 04726 /*! 04727 Check if the object can be moved. (actually checks 'edit' and 'remove' permissions) 04728 \return \c true if the object can be moved by the current user. 04729 \sa checkAccess(). 04730 */ 04731 function canMoveFrom( ) 04732 { 04733 04734 if ( !isset( $this->Permissions['can_move_from'] ) ) 04735 { 04736 $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' ); 04737 } 04738 return ( $this->Permissions['can_move_from'] == 1 ); 04739 } 04740 04741 /*! 04742 \return The name of the class which this object was created from. 04743 04744 \note The object will cache the class name information so multiple calls will be fast. 04745 */ 04746 function className() 04747 { 04748 if ( !is_numeric( $this->ClassID ) ) 04749 { 04750 return null; 04751 } 04752 04753 if ( $this->ClassName !== false ) 04754 return $this->ClassName; 04755 04756 $db = eZDB::instance(); 04757 $id = (int)$this->ClassID; 04758 $sql = "SELECT serialized_name_list FROM ezcontentclass WHERE id=$id and version=0"; 04759 $rows = $db->arrayQuery( $sql ); 04760 if ( count( $rows ) > 0 ) 04761 { 04762 $this->ClassName = eZContentClass::nameFromSerializedString( $rows[0]['serialized_name_list'] ); 04763 } 04764 return $this->ClassName; 04765 } 04766 04767 /*! 04768 Returns an array of the content actions which can be performed on 04769 the current object. 04770 */ 04771 function contentActionList() 04772 { 04773 $version = $this->attribute( 'current_version' ); 04774 $language = $this->initialLanguageCode(); 04775 if ( !isset( $this->ContentObjectAttributeArray[$version][$language] ) ) 04776 { 04777 $attributeList = $this->contentObjectAttributes(); 04778 $this->ContentObjectAttributeArray[$version][$language] =& $attributeList; 04779 } 04780 else 04781 $attributeList = $this->ContentObjectAttributeArray[$version][$language]; 04782 04783 // Fetch content actions if not already fetched 04784 if ( $this->ContentActionList === false ) 04785 { 04786 foreach ( $attributeList as $attribute ) 04787 { 04788 $contentActionList = $attribute->contentActionList(); 04789 if ( is_array( $contentActionList ) && !empty( $contentActionList ) ) 04790 { 04791 foreach ( $contentActionList as $action ) 04792 { 04793 if ( !$this->hasContentAction( $action['action'] ) ) 04794 { 04795 $this->ContentActionList[] = $action; 04796 } 04797 } 04798 } 04799 } 04800 } 04801 return $this->ContentActionList; 04802 } 04803 04804 /*! 04805 \return true if the content action is in the content action list 04806 */ 04807 function hasContentAction( $name ) 04808 { 04809 $return = false; 04810 if ( is_array ( $this->ContentActionList ) ) 04811 { 04812 foreach ( $this->ContentActionList as $action ) 04813 { 04814 if ( $action['action'] == $name ) 04815 { 04816 $return = true; 04817 } 04818 } 04819 } 04820 return $return; 04821 } 04822 04823 /*! 04824 \return the languages the object has been translated into/exists in. 04825 04826 Returns an array with the language codes. 04827 04828 It uses the attribute \c avail_lang as the source for the language list. 04829 */ 04830 function availableLanguages() 04831 { 04832 $languages = array(); 04833 $languageObjects = $this->languages(); 04834 04835 foreach ( $languageObjects as $languageObject ) 04836 { 04837 $languages[] = $languageObject->attribute( 'locale' ); 04838 } 04839 04840 return $languages; 04841 } 04842 04843 function availableLanguagesJsArray() 04844 { 04845 return eZContentLanguage::jsArrayByMask( $this->LanguageMask ); 04846 } 04847 04848 function languages() 04849 { 04850 return isset( $this->LanguageMask ) ? 04851 eZContentLanguage::prioritizedLanguagesByMask( $this->LanguageMask ) : 04852 array(); 04853 } 04854 04855 function allLanguages() 04856 { 04857 $languages = isset( $this->LanguageMask ) ? eZContentLanguage::languagesByMask( $this->LanguageMask ) : array(); 04858 return $languages; 04859 } 04860 04861 static function defaultLanguage() 04862 { 04863 if ( ! isset( $GLOBALS['eZContentObjectDefaultLanguage'] ) ) 04864 { 04865 $defaultLanguage = false; 04866 $language = eZContentLanguage::topPriorityLanguage(); 04867 if ( $language ) 04868 { 04869 $defaultLanguage = $language->attribute( 'locale' ); 04870 } 04871 else 04872 { 04873 $ini = eZINI::instance(); 04874 if ( $ini->hasVariable( 'RegionalSettings', 'ContentObjectLocale' ) ) 04875 { 04876 $defaultLanguage = $ini->variable( 'RegionalSettings', 'ContentObjectLocale' ); 04877 eZContentLanguage::fetchByLocale( $defaultLanguage, true ); 04878 } 04879 } 04880 $GLOBALS['eZContentObjectDefaultLanguage'] = $defaultLanguage; 04881 } 04882 04883 return $GLOBALS['eZContentObjectDefaultLanguage']; 04884 } 04885 04886 /*! 04887 \static 04888 Set default language. Checks if default language is valid. 04889 04890 \param default language. 04891 \note Deprecated. 04892 */ 04893 static function setDefaultLanguage( $lang ) 04894 { 04895 return false; 04896 } 04897 04898 /*! 04899 04900 */ 04901 function setClassName( $name ) 04902 { 04903 $this->ClassName = $name; 04904 } 04905 04906 /*! 04907 \returns an array with locale strings, these strings represents the languages which content objects are allowed to be translated into. 04908 \note the setting ContentSettings/TranslationList in site.ini determines the array. 04909 \sa translationList 04910 */ 04911 static function translationStringList() 04912 { 04913 $translationList = array(); 04914 $languageList = eZContentLanguage::fetchList(); 04915 04916 foreach ( $languageList as $language ) 04917 { 04918 $translationList[] = $language->attribute( 'locale' ); 04919 } 04920 04921 return $translationList; 04922 } 04923 04924 /*! 04925 \returns an array with locale objects, these objects represents the languages the content objects are allowed to be translated into. 04926 \note the setting ContentSettings/TranslationList in site.ini determines the array. 04927 \sa translationStringList 04928 */ 04929 static function translationList() 04930 { 04931 $translationList = array(); 04932 $languageList = eZContentLanguage::fetchList(); 04933 04934 foreach ( $languageList as $language ) 04935 { 04936 $translationList[] = $language->localeObject(); 04937 } 04938 04939 return $translationList; 04940 } 04941 04942 /*! 04943 Returns the attributes for the content object version \a $version and content object \a $contentObjectID. 04944 \a $language defines the language to fetch. 04945 \sa attributes 04946 */ 04947 function fetchClassAttributes( $version = 0, $asObject = true ) 04948 { 04949 return eZContentClassAttribute::fetchListByClassID( $this->attribute( 'contentclass_id' ), $version, $asObject ); 04950 } 04951 04952 /*! 04953 \private 04954 Maps input lange to another one if defined in $options['language_map']. 04955 If it cannot map it returns the original language. 04956 \returns string 04957 */ 04958 static function mapLanguage( $language, $options ) 04959 { 04960 if ( isset( $options['language_map'][$language] ) ) 04961 { 04962 return $options['language_map'][$language]; 04963 } 04964 return $language; 04965 } 04966 04967 /*! 04968 \static 04969 Unserialize xml structure. Create object from xml input. 04970 04971 \param package 04972 \param XML DOM Node 04973 \param parent node object. 04974 \param Options 04975 \param owner ID, override owner ID, null to use XML owner id (optional) 04976 04977 \returns created object, false if could not create object/xml invalid 04978 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 04979 the calls within a db transaction; thus within db->begin and db->commit. 04980 */ 04981 static function unserialize( $package, $domNode, &$options, $ownerID = false, $handlerType = 'ezcontentobject' ) 04982 { 04983 if ( $domNode->localName != 'object' ) 04984 { 04985 $retValue = false; 04986 return $retValue; 04987 } 04988 04989 $initialLanguage = eZContentObject::mapLanguage( $domNode->getAttribute( 'initial_language' ), $options ); 04990 if( $initialLanguage === 'skip' ) 04991 { 04992 $retValue = true; 04993 return $retValue; 04994 } 04995 04996 $sectionID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'section_id' ); 04997 if ( $ownerID === false ) 04998 { 04999 $ownerID = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'owner_id' ); 05000 } 05001 $remoteID = $domNode->getAttribute( 'remote_id' ); 05002 $name = $domNode->getAttribute( 'name' ); 05003 $classRemoteID = $domNode->getAttribute( 'class_remote_id' ); 05004 $classIdentifier = $domNode->getAttributeNS( 'http://ez.no/ezobject', 'class_identifier' ); 05005 $alwaysAvailable = ( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'always_available' ) == '1' ); 05006 05007 $contentClass = eZContentClass::fetchByRemoteID( $classRemoteID ); 05008 if ( !$contentClass ) 05009 { 05010 $contentClass = eZContentClass::fetchByIdentifier( $classIdentifier ); 05011 } 05012 05013 if ( !$contentClass ) 05014 { 05015 $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_NO_CLASS, 05016 'element_id' => $remoteID, 05017 'description' => "Can't install object '$name': Unable to fetch class with remoteID: $classRemoteID." ); 05018 $retValue = false; 05019 return $retValue; 05020 } 05021 05022 $versionListNode = $domNode->getElementsByTagName( 'version-list' )->item( 0 ); 05023 05024 $importedLanguages = array(); 05025 foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode ) 05026 { 05027 foreach ( $versionDOMNode->getElementsByTagName( 'object-translation' ) as $versionDOMNodeChild ) 05028 { 05029 $importedLanguage = eZContentObject::mapLanguage( $versionDOMNodeChild->getAttribute( 'language' ), $options ); 05030 $language = eZContentLanguage::fetchByLocale( $importedLanguage ); 05031 // Check if the language is allowed in this setup. 05032 if ( $language ) 05033 { 05034 $hasTranslation = true; 05035 } 05036 else 05037 { 05038 if ( $importedLanguage == 'skip' ) 05039 continue; 05040 05041 // if there is no needed translation in system then add it 05042 $locale = eZLocale::instance( $importedLanguage ); 05043 $translationName = $locale->internationalLanguageName(); 05044 $translationLocale = $locale->localeCode(); 05045 05046 if ( $locale->isValid() ) 05047 { 05048 eZContentLanguage::addLanguage( $locale->localeCode(), $locale->internationalLanguageName() ); 05049 $hasTranslation = true; 05050 } 05051 else 05052 $hasTranslation = false; 05053 } 05054 if ( $hasTranslation ) 05055 { 05056 $importedLanguages[] = $importedLanguage; 05057 $importedLanguages = array_unique( $importedLanguages ); 05058 } 05059 } 05060 } 05061 05062 // If object exists we return a error. 05063 // Minimum install element is an object now. 05064 05065 $contentObject = eZContentObject::fetchByRemoteID( $remoteID ); 05066 // Figure out initial language 05067 if ( !$initialLanguage || 05068 !in_array( $initialLanguage, $importedLanguages ) ) 05069 { 05070 $initialLanguage = false; 05071 foreach ( eZContentLanguage::prioritizedLanguages() as $language ) 05072 { 05073 if ( in_array( $language->attribute( 'locale' ), $importedLanguages ) ) 05074 { 05075 $initialLanguage = $language->attribute( 'locale' ); 05076 break; 05077 } 05078 } 05079 } 05080 if ( !$contentObject ) 05081 { 05082 $firstVersion = true; 05083 $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID ); 05084 } 05085 else 05086 { 05087 $firstVersion = false; 05088 $description = "Object '$name' already exists."; 05089 05090 $choosenAction = eZPackageHandler::errorChoosenAction( self::PACKAGE_ERROR_EXISTS, 05091 $options, $description, $handlerType, false ); 05092 05093 switch( $choosenAction ) 05094 { 05095 case eZPackage::NON_INTERACTIVE: 05096 case self::PACKAGE_UPDATE: 05097 { 05098 // Keep existing contentobject. 05099 } break; 05100 05101 case self::PACKAGE_REPLACE: 05102 { 05103 eZContentObjectOperations::remove( $contentObject->attribute( 'id' ) ); 05104 05105 unset( $contentObject ); 05106 $contentObject = $contentClass->instantiateIn( $initialLanguage, $ownerID, $sectionID ); 05107 $firstVersion = true; 05108 } break; 05109 05110 case self::PACKAGE_SKIP: 05111 { 05112 $retValue = true; 05113 return $retValue; 05114 } break; 05115 05116 case self::PACKAGE_NEW: 05117 { 05118 $contentObject->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) ); 05119 $contentObject->store(); 05120 unset( $contentObject ); 05121 $contentObject = $contentClass->instantiate( $ownerID, $sectionID ); 05122 $firstVersion = true; 05123 } break; 05124 05125 default: 05126 { 05127 $options['error'] = array( 'error_code' => self::PACKAGE_ERROR_EXISTS, 05128 'element_id' => $remoteID, 05129 'description' => $description, 05130 'actions' => array( self::PACKAGE_REPLACE => ezpI18n::tr( 'kernel/classes', 'Replace existing object' ), 05131 self::PACKAGE_SKIP => ezpI18n::tr( 'kernel/classes', 'Skip object' ), 05132 self::PACKAGE_NEW => ezpI18n::tr( 'kernel/classes', 'Keep existing and create a new one' ), 05133 self::PACKAGE_UPDATE => ezpI18n::tr( 'kernel/classes', 'Update existing object' ) ) ); 05134 $retValue = false; 05135 return $retValue; 05136 } break; 05137 } 05138 } 05139 05140 $db = eZDB::instance(); 05141 $db->begin(); 05142 05143 if ( $alwaysAvailable ) 05144 { 05145 // Make sure always available bit is set. 05146 $contentObject->setAttribute( 'language_mask', (int)$contentObject->attribute( 'language_mask' ) | 1 ); 05147 } 05148 05149 $contentObject->setAttribute( 'section_id', $sectionID ); 05150 $contentObject->store(); 05151 $activeVersion = false; 05152 $lastVersion = false; 05153 $versionListActiveVersion = $versionListNode->getAttribute( 'active_version' ); 05154 05155 $contentObject->setAttribute( 'remote_id', $remoteID ); 05156 $contentObject->setAttribute( 'contentclass_id', $contentClass->attribute( 'id' ) ); 05157 $contentObject->store(); 05158 05159 $sectionObject = eZSection::fetch( $sectionID ); 05160 if ( $sectionObject instanceof eZSection ) 05161 { 05162 $updateWithParentSection = false; 05163 } 05164 else 05165 { 05166 $updateWithParentSection = true; 05167 } 05168 05169 $options['language_array'] = $importedLanguages; 05170 $versionList = array(); 05171 foreach( $versionListNode->getElementsByTagName( 'version' ) as $versionDOMNode ) 05172 { 05173 unset( $nodeList ); 05174 $nodeList = array(); 05175 $contentObjectVersion = eZContentObjectVersion::unserialize( $versionDOMNode, 05176 $contentObject, 05177 $ownerID, 05178 $sectionID, 05179 $versionListActiveVersion, 05180 $firstVersion, 05181 $nodeList, 05182 $options, 05183 $package ); 05184 05185 if ( !$contentObjectVersion ) 05186 { 05187 $db->commit(); 05188 05189 $retValue = false; 05190 return $retValue; 05191 } 05192 05193 $versionStatus = $versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'status' ); 05194 $versionList[$versionDOMNode->getAttributeNS( 'http://ez.no/ezobject', 'version' )] = array( 'node_list' => $nodeList, 05195 'status' => $versionStatus ); 05196 unset( $versionStatus ); 05197 05198 $firstVersion = false; 05199 $lastVersion = $contentObjectVersion->attribute( 'version' ); 05200 if ( $versionDOMNode->getAttribute( 'version' ) == $versionListActiveVersion ) 05201 { 05202 $activeVersion = $contentObjectVersion->attribute( 'version' ); 05203 } 05204 eZNodeAssignment::setNewMainAssignment( $contentObject->attribute( 'id' ), $lastVersion ); 05205 05206 eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObject->attribute( 'id' ), 05207 'version' => $lastVersion ) ); 05208 05209 $mainNodeInfo = null; 05210 foreach ( $nodeList as $nodeInfo ) 05211 { 05212 if ( $nodeInfo['is_main'] ) 05213 { 05214 $mainNodeInfo =& $nodeInfo; 05215 break; 05216 } 05217 } 05218 if ( $mainNodeInfo ) 05219 { 05220 $existingMainNode = eZContentObjectTreeNode::fetchByRemoteID( $mainNodeInfo['parent_remote_id'], false ); 05221 if ( $existingMainNode ) 05222 { 05223 eZContentObjectTreeNode::updateMainNodeID( $existingMainNode['node_id'], 05224 $mainNodeInfo['contentobject_id'], 05225 $mainNodeInfo['contentobject_version'], 05226 $mainNodeInfo['parent_node'], 05227 $updateWithParentSection ); 05228 } 05229 } 05230 unset( $mainNodeInfo ); 05231 // Refresh $contentObject from DB. 05232 $contentObject = eZContentObject::fetch( $contentObject->attribute( 'id' ) ); 05233 } 05234 if ( !$activeVersion ) 05235 { 05236 $activeVersion = $lastVersion; 05237 } 05238 05239 /* 05240 $contentObject->setAttribute( 'current_version', $activeVersion ); 05241 */ 05242 $contentObject->setAttribute( 'name', $name ); 05243 if ( isset( $options['use_dates_from_package'] ) && $options['use_dates_from_package'] ) 05244 { 05245 $contentObject->setAttribute( 'published', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'published' ) ) ); 05246 $contentObject->setAttribute( 'modified', eZDateUtils::textToDate( $domNode->getAttributeNS( 'http://ez.no/ezobject', 'modified' ) ) ); 05247 } 05248 $contentObject->store(); 05249 05250 $versions = $contentObject->versions(); 05251 $objectName = $contentObject->name(); 05252 $objectID = $contentObject->attribute( 'id' ); 05253 foreach ( $versions as $version ) 05254 { 05255 $versionNum = $version->attribute( 'version' ); 05256 $oldVersionStatus = $version->attribute( 'status' ); 05257 $newVersionStatus = isset( $versionList[$versionNum] ) ? $versionList[$versionNum]['status'] : null; 05258 05259 // set the correct status for non-published versions 05260 if ( isset( $newVersionStatus ) && $oldVersionStatus != $newVersionStatus && $newVersionStatus != eZContentObjectVersion::STATUS_PUBLISHED ) 05261 { 05262 $version->setAttribute( 'status', $newVersionStatus ); 05263 $version->store( array( 'status' ) ); 05264 } 05265 05266 // when translation does not have object name set then we copy object name from the current object version 05267 $translations = $version->translations( false ); 05268 if ( !$translations ) 05269 continue; 05270 foreach ( $translations as $translation ) 05271 { 05272 if ( ! $contentObject->name( $versionNum, $translation ) ) 05273 { 05274 eZDebug::writeNotice( "Setting name '$objectName' for version ($versionNum) of the content object ($objectID) in language($translation)" ); 05275 $contentObject->setName( $objectName, $versionNum, $translation ); 05276 } 05277 } 05278 } 05279 05280 foreach ( $versionList[$versionListActiveVersion]['node_list'] as $nodeInfo ) 05281 { 05282 unset( $parentNode ); 05283 $parentNode = eZContentObjectTreeNode::fetchNode( $contentObject->attribute( 'id' ), 05284 $nodeInfo['parent_node'] ); 05285 if ( is_object( $parentNode ) ) 05286 { 05287 $parentNode->setAttribute( 'priority', $nodeInfo['priority'] ); 05288 $parentNode->store( array( 'priority' ) ); 05289 } 05290 } 05291 05292 $db->commit(); 05293 05294 return $contentObject; 05295 } 05296 05297 /*! 05298 Performs additional unserialization actions that need to be performed when all 05299 objects contained in the package are already installed. (maintain objects' cross-relations) 05300 */ 05301 05302 function postUnserialize( $package ) 05303 { 05304 foreach( $this->versions() as $version ) 05305 { 05306 $version->postUnserialize( $package ); 05307 } 05308 } 05309 05310 /*! 05311 \return a DOM structure of the content object and it's attributes. 05312 05313 \param package 05314 \param Content object version, true for current version, false for all, else array containing specific versions. 05315 \param package options ( optianal ) 05316 \param array of allowed nodes ( optional ) 05317 \param array of top nodes in current package export (optional ) 05318 05319 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05320 the calls within a db transaction; thus within db->begin and db->commit. 05321 */ 05322 function serialize( $package, $specificVersion = false, $options = false, $contentNodeIDArray = false, $topNodeIDArray = false ) 05323 { 05324 if ( $options && 05325 $options['node_assignment'] == 'main' ) 05326 { 05327 if ( !isset( $contentNodeIDArray[$this->attribute( 'main_node_id' )] ) ) 05328 { 05329 return false; 05330 } 05331 } 05332 05333 $dom = new DomDocument(); 05334 $objectNode = $dom->createElementNS( 'http://ez.no/ezobject', 'ezremote:object' ); 05335 05336 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:id', $this->ID ); 05337 $objectNode->setAttribute( 'name', $this->Name ); 05338 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:section_id', $this->SectionID ); 05339 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:owner_id', $this->OwnerID ); 05340 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_id', $this->ClassID ); 05341 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:published', eZDateUtils::rfc1123Date( $this->attribute( 'published' ) ) ); 05342 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:modified', eZDateUtils::rfc1123Date( $this->attribute( 'modified' ) ) ); 05343 if ( !$this->attribute( 'remote_id' ) ) 05344 { 05345 $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'object' ) ); 05346 $this->store(); 05347 } 05348 $objectNode->setAttribute( 'remote_id', $this->attribute( 'remote_id' ) ); 05349 $contentClass = $this->attribute( 'content_class' ); 05350 $objectNode->setAttribute( 'class_remote_id', $contentClass->attribute( 'remote_id' ) ); 05351 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:class_identifier', $contentClass->attribute( 'identifier' ) ); 05352 $alwaysAvailableText = '0'; 05353 if ( (int)$this->attribute( 'language_mask' ) & 1 ) 05354 { 05355 $alwaysAvailableText = '1'; 05356 } 05357 $objectNode->setAttributeNS( 'http://ez.no/ezobject', 'ezremote:always_available', $alwaysAvailableText ); 05358 05359 $versions = array(); 05360 $oneLanguagePerVersion = false; 05361 if ( $specificVersion === false ) 05362 { 05363 $versions = $this->versions(); 05364 // Since we are exporting all versions it should only contain 05365 // one language per version 05366 //$oneLanguagePerVersion = true; // uncomment to get one language per version 05367 } 05368 else if ( $specificVersion === true ) 05369 { 05370 $versions[] = $this->currentVersion(); 05371 } 05372 else 05373 { 05374 $versions[] = $this->version( $specificVersion ); 05375 // Since we are exporting a specific version it should only contain 05376 // one language per version? 05377 $oneLanguagePerVersion = true; 05378 } 05379 05380 $this->fetchClassAttributes(); 05381 05382 $exportedLanguages = array(); 05383 05384 $versionsNode = $dom->createElementNS( 'http://ez.no/object/', 'ezobject:version-list' ); 05385 $versionsNode->setAttribute( 'active_version', $this->CurrentVersion ); 05386 foreach ( $versions as $version ) 05387 { 05388 if ( !$version ) 05389 { 05390 continue; 05391 } 05392 $options['only_initial_language'] = $oneLanguagePerVersion; 05393 $versionNode = $version->serialize( $package, $options, $contentNodeIDArray, $topNodeIDArray ); 05394 if ( $versionNode ) 05395 { 05396 $importedVersionNode = $dom->importNode( $versionNode, true ); 05397 $versionsNode->appendChild( $importedVersionNode ); 05398 foreach ( $versionNode->getElementsByTagName( 'object-translation' ) as $versionNodeChild ) 05399 { 05400 $exportedLanguage = $versionNodeChild->getAttribute( 'language' ); 05401 $exportedLanguages[] = $exportedLanguage; 05402 $exportedLanguages = array_unique( $exportedLanguages ); 05403 } 05404 } 05405 unset( $versionNode ); 05406 unset( $versionNode ); 05407 } 05408 $initialLanguageCode = $this->attribute( 'initial_language_code' ); 05409 if ( in_array( $initialLanguageCode, $exportedLanguages ) ) 05410 { 05411 $objectNode->setAttribute( 'initial_language', $initialLanguageCode ); 05412 } 05413 $objectNode->appendChild( $versionsNode ); 05414 return $objectNode; 05415 } 05416 05417 /*! 05418 \return a structure with information required for caching. 05419 */ 05420 function cacheInfo( $Params ) 05421 { 05422 $contentCacheInfo =& $GLOBALS['eZContentCacheInfo']; 05423 if ( !isset( $contentCacheInfo ) ) 05424 { 05425 $user = eZUser::currentUser(); 05426 $language = false; 05427 if ( isset( $Params['Language'] ) ) 05428 { 05429 $language = $Params['Language']; 05430 } 05431 $roleList = $user->roleIDList(); 05432 $discountList = eZUserDiscountRule::fetchIDListByUserID( $user->attribute( 'contentobject_id' ) ); 05433 $contentCacheInfo = array( 'language' => $language, 05434 'role_list' => $roleList, 05435 'discount_list' => $discountList ); 05436 } 05437 return $contentCacheInfo; 05438 } 05439 05440 /*! 05441 Sets all view cache files to be expired 05442 */ 05443 static function expireAllViewCache() 05444 { 05445 eZExpiryHandler::registerShutdownFunction(); 05446 $handler = eZExpiryHandler::instance(); 05447 $handler->setTimestamp( 'content-view-cache', time() ); 05448 $handler->store(); 05449 } 05450 05451 /*! 05452 Sets all content cache files to be expired. Both view cache and cache blocks are expired. 05453 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05454 the calls within a db transaction; thus within db->begin and db->commit. 05455 */ 05456 static function expireAllCache() 05457 { 05458 eZExpiryHandler::registerShutdownFunction(); 05459 $handler = eZExpiryHandler::instance(); 05460 $handler->setTimestamp( 'content-view-cache', time() ); 05461 $handler->setTimestamp( 'template-block-cache', time() ); 05462 $handler->setTimestamp( 'content-tree-menu', time() ); 05463 $handler->store(); 05464 } 05465 05466 /*! 05467 Expires all template block cache. This should be expired anytime any content 05468 is published/modified or removed. 05469 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05470 the calls within a db transaction; thus within db->begin and db->commit. 05471 */ 05472 static function expireTemplateBlockCache() 05473 { 05474 eZExpiryHandler::registerShutdownFunction(); 05475 $handler = eZExpiryHandler::instance(); 05476 $handler->setTimestamp( 'template-block-cache', time() ); 05477 $handler->store(); 05478 } 05479 05480 /*! 05481 \static 05482 Callse eZContentObject::xpireTemplateBlockCache() unless template caching is disabled. 05483 */ 05484 static function expireTemplateBlockCacheIfNeeded() 05485 { 05486 $ini = eZINI::instance(); 05487 if ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) == 'enabled' ) 05488 eZContentObject::expireTemplateBlockCache(); 05489 } 05490 05491 /*! 05492 Sets all complex viewmode content cache files to be expired. 05493 \note Transaction unsafe. If you call several transaction unsafe methods you must enclose 05494 the calls within a db transaction; thus within db->begin and db->commit. 05495 */ 05496 static function expireComplexViewModeCache() 05497 { 05498 eZExpiryHandler::registerShutdownFunction(); 05499 $handler = eZExpiryHandler::instance(); 05500 $handler->setTimestamp( 'content-complex-viewmode-cache', time() ); 05501 $handler->store(); 05502 } 05503 05504 /*! 05505 \return if the content cache timestamp \a $timestamp is expired. 05506 */ 05507 static function isCacheExpired( $timestamp ) 05508 { 05509 eZExpiryHandler::registerShutdownFunction(); 05510 $handler = eZExpiryHandler::instance(); 05511 if ( !$handler->hasTimestamp( 'content-view-cache' ) ) 05512 return false; 05513 $expiryTime = $handler->timestamp( 'content-view-cache' ); 05514 if ( $expiryTime > $timestamp ) 05515 return true; 05516 return false; 05517 } 05518 05519 /*! 05520 \return true if the viewmode is a complex viewmode. 05521 */ 05522 static function isComplexViewMode( $viewMode ) 05523 { 05524 $ini = eZINI::instance(); 05525 $viewModes = $ini->variableArray( 'ContentSettings', 'ComplexDisplayViewModes' ); 05526 return in_array( $viewMode, $viewModes ); 05527 } 05528 05529 /*! 05530 \return true if the viewmode is a complex viewmode and the viewmode timestamp is expired. 05531 */ 05532 static function isComplexViewModeCacheExpired( $viewMode, $timestamp ) 05533 { 05534 if ( !eZContentObject::isComplexViewMode( $viewMode ) ) 05535 return false; 05536 eZExpiryHandler::registerShutdownFunction(); 05537 $handler = eZExpiryHandler::instance(); 05538 if ( !$handler->hasTimestamp( 'content-complex-viewmode-cache' ) ) 05539 return false; 05540 $expiryTime = $handler->timestamp( 'content-complex-viewmode-cache' ); 05541 if ( $expiryTime > $timestamp ) 05542 return true; 05543 return false; 05544 } 05545 05546 /*! 05547 Returns a list of all the authors for this object. The returned value is an 05548 array of eZ user objects. 05549 */ 05550 function authorArray() 05551 { 05552 $db = eZDB::instance(); 05553 05554 $userArray = $db->arrayQuery( "SELECT DISTINCT ezuser.contentobject_id, ezuser.login, ezuser.email, ezuser.password_hash, ezuser.password_hash_type 05555 FROM ezcontentobject_version, ezuser where ezcontentobject_version.contentobject_id='$this->ID' 05556 AND ezcontentobject_version.creator_id=ezuser.contentobject_id" ); 05557 05558 $return = array(); 05559 05560 foreach ( $userArray as $userRow ) 05561 { 05562 $return[] = new eZUser( $userRow ); 05563 } 05564 return $return; 05565 } 05566 05567 /*! 05568 \return the number of objects of the given class is created by the given user. 05569 */ 05570 static function fetchObjectCountByUserID( $classID, $userID, $status = false ) 05571 { 05572 $count = 0; 05573 if ( is_numeric( $classID ) and is_numeric( $userID ) ) 05574 { 05575 $db = eZDB::instance(); 05576 $classID = (int) $classID; 05577 $userID = (int) $userID; 05578 $statusSQL = $status !== false ? ( ' AND status=' . (int) $status ) : ''; 05579 $countArray = $db->arrayQuery( "SELECT count(*) AS count FROM ezcontentobject WHERE contentclass_id=$classID AND owner_id=$userID$statusSQL" ); 05580 $count = $countArray[0]['count']; 05581 } 05582 return $count; 05583 } 05584 05585 /*! 05586 \static 05587 \deprecated This method is left here only for backward compatibility. 05588 Use eZContentObjectVersion::removeVersions() method instead. 05589 */ 05590 static function removeVersions( $versionStatus = false ) 05591 { 05592 eZContentObjectVersion::removeVersions( $versionStatus ); 05593 } 05594 05595 /*! 05596 Sets the object's name to $newName: tries to find attributes that are in 'object pattern name' 05597 and updates them. 05598 \return \c true if object's name was changed, otherwise \c false. 05599 */ 05600 function rename( $newName ) 05601 { 05602 // get 'object name pattern' 05603 $objectNamePattern = ''; 05604 $contentClass = $this->contentClass(); 05605 if ( is_object( $contentClass ) ) 05606 $objectNamePattern = $contentClass->ContentObjectName; 05607 05608 if ( $objectNamePattern == '' ) 05609 return false; 05610 05611 // get parts of object's name pattern( like <attr1|attr2>, <attr3> ) 05612 $objectNamePatternPartsPattern = '/<([^>]+)>/U'; 05613 preg_match_all( $objectNamePatternPartsPattern, $objectNamePattern, $objectNamePatternParts ); 05614 05615 if( count( $objectNamePatternParts ) === 0 || count( $objectNamePatternParts[1] ) == 0 ) 05616 return false; 05617 05618 $objectNamePatternParts = $objectNamePatternParts[1]; 05619 05620 // replace all <...> with (.*) 05621 $newNamePattern = preg_replace( $objectNamePatternPartsPattern, '(.*)', $objectNamePattern ); 05622 // add terminators 05623 $newNamePattern = '/' . $newNamePattern . '/'; 05624 05625 // find parts of $newName 05626 preg_match_all( $newNamePattern, $newName, $newNameParts ); 05627 05628 // looks ok, we can create new version of object 05629 $contentObjectVersion = $this->createNewVersion(); 05630 // get contentObjectAttributes 05631 $dataMap = $contentObjectVersion->attribute( 'data_map' ); 05632 if ( count( $dataMap ) === 0 ) 05633 return false; 05634 05635 // assign parts of $newName to the object's attributes. 05636 $pos = 0; 05637 while( $pos < count( $objectNamePatternParts ) ) 05638 { 05639 $attributes = $objectNamePatternParts[$pos]; 05640 05641 // if we have something like <attr1|attr2> then 05642 // 'attr1' will be updated only. 05643 $attributes = explode( '|', $attributes ); 05644 $attribute = $attributes[0]; 05645 05646 $newNamePart = $newNameParts[$pos+1]; 05647 if ( count( $newNamePart ) === 0 ) 05648 { 05649 if( $pos === 0 ) 05650 { 05651 // whole $newName goes into the first attribute 05652 $attributeValue = $newName; 05653 } 05654 else 05655 { 05656 // all other attibutes will be set to '' 05657 $attributeValue = ''; 05658 } 05659 } 05660 else 05661 { 05662 $attributeValue = $newNamePart[0]; 05663 } 05664 05665 $contentAttribute =& $dataMap[$attribute]; 05666 $dataType = $contentAttribute->dataType(); 05667 if( is_object( $dataType ) && $dataType->isSimpleStringInsertionSupported() ) 05668 { 05669 $result = ''; 05670 $dataType->insertSimpleString( $this, $contentObjectVersion, false, $contentAttribute, $attributeValue, $result ); 05671 $contentAttribute->store(); 05672 } 05673 05674 ++$pos; 05675 } 05676 05677 $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $this->attribute( 'id' ), 05678 'version' => $contentObjectVersion->attribute( 'version') ) ); 05679 return ($operationResult != null ? true : false); 05680 } 05681 05682 function removeTranslation( $languageID ) 05683 { 05684 $language = eZContentLanguage::fetch( $languageID ); 05685 05686 if ( !$language ) 05687 { 05688 return false; 05689 } 05690 05691 // check permissions for editing 05692 if ( !$this->checkAccess( 'edit', false, false, false, $languageID ) ) 05693 { 05694 return false; 05695 } 05696 05697 // check if it is not the initial language 05698 $objectInitialLanguageID = $this->attribute( 'initial_language_id' ); 05699 if ( $objectInitialLanguageID == $languageID ) 05700 { 05701 return false; 05702 } 05703 05704 // change language_mask of the object 05705 $languageMask = (int) $this->attribute( 'language_mask' ); 05706 $languageMask = (int) $languageMask & ~ (int) $languageID; 05707 $this->setAttribute( 'language_mask', $languageMask ); 05708 05709 $db = eZDB::instance(); 05710 $db->begin(); 05711 05712 $this->store(); 05713 05714 $objectID = $this->ID; 05715 05716 // If the current version has initial_language_id $languageID, change it to the initial_language_id of the object. 05717 $currentVersion = $this->currentVersion(); 05718 if ( $currentVersion->attribute( 'initial_language_id' ) == $languageID ) 05719 { 05720 $currentVersion->setAttribute( 'initial_language_id', $objectInitialLanguageID ); 05721 $currentVersion->store(); 05722 } 05723 05724 // Remove all versions which had the language as its initial ID. Because of previous checks, it is sure we will not remove the published version. 05725 $versionsToRemove = $this->versions( true, array( 'conditions' => array( 'initial_language_id' => $languageID ) ) ); 05726 foreach ( $versionsToRemove as $version ) 05727 { 05728 $version->removeThis(); 05729 } 05730 05731 $altLanguageID = $languageID++; 05732 05733 // Remove all attributes in the language 05734 $attributes = $db->arrayQuery( "SELECT * FROM ezcontentobject_attribute 05735 WHERE contentobject_id='$objectID' 05736 AND ( language_id='$languageID' OR language_id='$altLanguageID' )" ); 05737 foreach ( $attributes as $attribute ) 05738 { 05739 $attributeObject = new eZContentObjectAttribute( $attribute ); 05740 $attributeObject->remove( $attributeObject->attribute( 'id' ), $attributeObject->attribute( 'version' ) ); 05741 unset( $attributeObject ); 05742 } 05743 05744 // Remove all names in the language 05745 $db->query( "DELETE FROM ezcontentobject_name 05746 WHERE contentobject_id='$objectID' 05747 AND ( language_id='$languageID' OR language_id='$altLanguageID' )" ); 05748 05749 // Update masks of the objects 05750 $mask = eZContentLanguage::maskForRealLanguages() - (int) $languageID; 05751 05752 if ( $db->databaseName() == 'oracle' ) 05753 { 05754 $db->query( "UPDATE ezcontentobject_version SET language_mask = bitand( language_mask, $mask ) 05755 WHERE contentobject_id='$objectID'" ); 05756 } 05757 else 05758 { 05759 $db->query( "UPDATE ezcontentobject_version SET language_mask = language_mask & $mask 05760 WHERE contentobject_id='$objectID'" ); 05761 } 05762 05763 $urlElementfilter = new eZURLAliasQuery(); 05764 $urlElementfilter->type = 'name'; 05765 // We want all languages present here, so we are turning off 05766 // language filtering 05767 $urlElementfilter->languages = false; 05768 $urlElementfilter->limit = false; 05769 05770 $nodes = $this->assignedNodes(); 05771 05772 foreach ( $nodes as $node ) 05773 { 05774 $parent = null; 05775 $textMD5 = null; 05776 05777 $urlElementfilter->actions = array( 'eznode:' . $node->attribute( 'node_id' ) ); 05778 $urlElementfilter->prepare(); 05779 $urlElements = $urlElementfilter->fetchAll(); 05780 05781 foreach ($urlElements as $url ) 05782 { 05783 if ( $url->attribute( 'lang_mask' ) === (int)$languageID or 05784 $url->attribute( 'lang_mask') === (int)$altLanguageID ) 05785 { 05786 $parent = $url->attribute( 'parent'); 05787 $textMD5 = $url->attribute( 'text_md5' ); 05788 break; 05789 } 05790 } 05791 05792 if ( $parent !== null and $textMD5 !== null ) 05793 eZURLAliasML::removeSingleEntry( $parent, $textMD5, $language ); 05794 } 05795 $db->commit(); 05796 05797 return true; 05798 } 05799 05800 function isAlwaysAvailable() 05801 { 05802 return ( $this->attribute( 'language_mask' ) & 1 ); 05803 } 05804 05805 function setAlwaysAvailableLanguageID( $languageID, $version = false ) 05806 { 05807 $db = eZDB::instance(); 05808 $db->begin(); 05809 05810 if ( $version == false ) 05811 { 05812 $version = $this->currentVersion(); 05813 if ( $languageID ) 05814 { 05815 $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) | 1 ); 05816 } 05817 else 05818 { 05819 $this->setAttribute( 'language_mask', (int)$this->attribute( 'language_mask' ) & ~1 ); 05820 } 05821 $this->store(); 05822 } 05823 05824 $objectID = $this->attribute( 'id' ); 05825 $versionID = $version->attribute( 'version' ); 05826 05827 // reset 'always available' flag 05828 $sql = "UPDATE ezcontentobject_name SET language_id="; 05829 if ( $db->databaseName() == 'oracle' ) 05830 { 05831 $sql .= "bitand( language_id, -2 )"; 05832 } 05833 else 05834 { 05835 $sql .= "language_id & ~1"; 05836 } 05837 $sql .= " WHERE contentobject_id = '$objectID' AND content_version = '$versionID'"; 05838 $db->query( $sql ); 05839 05840 if ( $languageID != false ) 05841 { 05842 $newLanguageID = $languageID | 1; 05843 $sql = "UPDATE ezcontentobject_name 05844 SET language_id='$newLanguageID' 05845 WHERE language_id='$languageID' AND contentobject_id = '$objectID' AND content_version = '$versionID'"; 05846 $db->query( $sql ); 05847 } 05848 05849 $version->setAlwaysAvailableLanguageID( $languageID ); 05850 05851 // Update url alias for all locations 05852 $nodeRows = eZContentObjectTreeNode::fetchByContentObjectID( $objectID, false ); 05853 $actions = array(); 05854 foreach ( $nodeRows as $nodeRow ) 05855 { 05856 $nodeID = (int)$nodeRow['node_id']; 05857 $actions[] = array( 'eznode', $nodeID ); 05858 } 05859 eZURLAliasML::setLangMaskAlwaysAvailable( $languageID, $actions, null ); 05860 05861 $db->commit(); 05862 } 05863 05864 function allowedAssignSectionList() 05865 { 05866 $currentUser = eZUser::currentUser(); 05867 $sectionIDList = $currentUser->canAssignToObjectSectionList( $this ); 05868 05869 $sectionList = array(); 05870 if ( in_array( '*', $sectionIDList ) ) 05871 { 05872 $sectionList = eZSection::fetchList( false ); 05873 } 05874 else 05875 { 05876 $sectionIDList[] = $this->attribute( 'section_id' ); 05877 $sectionList = eZSection::fetchFilteredList( array( 'id' => array( $sectionIDList ) ), false, false, false ); 05878 } 05879 return $sectionList; 05880 } 05881 05882 /** 05883 * Gets a list of states a user is allowed to put the content object in. 05884 * 05885 * @return array the IDs of all states we are allowed to set 05886 * @param eZUser $user the user to check the policies of, when omitted the currently logged in user will be used 05887 */ 05888 function allowedAssignStateIDList( eZUser $user = null ) 05889 { 05890 if ( !$user instanceof eZUser ) 05891 { 05892 $user = eZUser::currentUser(); 05893 } 05894 05895 $access = $user->hasAccessTo( 'state', 'assign' ); 05896 05897 $db = eZDB::instance(); 05898 $sql = 'SELECT ezcobj_state.id 05899 FROM ezcobj_state, ezcobj_state_group 05900 WHERE ezcobj_state.group_id = ezcobj_state_group.id 05901 AND ezcobj_state_group.identifier NOT LIKE \'ez%\''; 05902 if ( $access['accessWord'] == 'yes' ) 05903 { 05904 $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) ); 05905 } 05906 else if ( $access['accessWord'] == 'limited' ) 05907 { 05908 $userID = $user->attribute( 'contentobject_id' ); 05909 $classID = $this->attribute( 'contentclass_id' ); 05910 $ownerID = $this->attribute( 'owner_id' ); 05911 $sectionID = $this->attribute( 'section_id' ); 05912 $stateIDArray = $this->attribute( 'state_id_array' ); 05913 05914 $allowedStateIDList = array(); 05915 foreach ( $access['policies'] as $policy ) 05916 { 05917 foreach ( $policy as $ident => $values ) 05918 { 05919 $allowed = true; 05920 05921 switch ( $ident ) 05922 { 05923 case 'Class': 05924 { 05925 $allowed = in_array( $classID, $values ); 05926 } break; 05927 05928 case 'Owner': 05929 { 05930 $allowed = in_array( 1, $values ) and $userID != $ownerID; 05931 } break; 05932 05933 case 'Group': 05934 { 05935 $allowed = $this->checkGroupLimitationAccess( $values, $userID ) === 'allowed'; 05936 } break; 05937 05938 case 'Section': 05939 case 'User_Section': 05940 { 05941 $allowed = in_array( $sectionID, $values ); 05942 } break; 05943 05944 default: 05945 { 05946 if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 ) 05947 { 05948 $allowed = count( array_intersect( $values, $stateIDArray ) ) > 0; 05949 } 05950 } 05951 } 05952 05953 if ( !$allowed ) 05954 { 05955 continue 2; 05956 } 05957 } 05958 05959 if ( isset( $policy['NewState'] ) and count( $policy['NewState'] ) > 0 ) 05960 { 05961 $allowedStateIDList = array_merge( $allowedStateIDList, $policy['NewState'] ); 05962 } 05963 else 05964 { 05965 $allowedStateIDList = $db->arrayQuery( $sql, array( 'column' => 'id' ) ); 05966 break; 05967 } 05968 } 05969 05970 $allowedStateIDList = array_merge( $allowedStateIDList, $stateIDArray ); 05971 } 05972 else 05973 { 05974 $stateIDArray = $this->attribute( 'state_id_array' ); 05975 $allowedStateIDList = $stateIDArray; 05976 } 05977 05978 $allowedStateIDList = array_unique( $allowedStateIDList ); 05979 05980 return $allowedStateIDList; 05981 } 05982 05983 function allowedAssignStateList( eZUser $user = null ) 05984 { 05985 $allowedStateIDList = $this->allowedAssignStateIDList( $user ); 05986 05987 // retrieve state groups, and for each state group the allowed states (including the current state) 05988 $groups = eZContentObjectStateGroup::fetchByOffset( false, false ); 05989 05990 $allowedAssignList = array(); 05991 foreach ( $groups as $group ) 05992 { 05993 // we do not return any internal state 05994 // all internal states are prepended with the string : "ez_" 05995 if( strpos( $group->attribute( 'identifier' ), 'ez' ) === 0 ) 05996 continue; 05997 05998 $states = array(); 05999 $groupStates = $group->attribute( 'states' ); 06000 06001 $currentStateIDArray = $this->attribute( 'state_id_array' ); 06002 06003 $current = false; 06004 foreach ( $groupStates as $groupState ) 06005 { 06006 $stateID = $groupState->attribute( 'id' ); 06007 if ( in_array( $stateID, $allowedStateIDList ) ) 06008 { 06009 $states[] = $groupState; 06010 } 06011 06012 if ( in_array( $stateID, $currentStateIDArray ) ) 06013 { 06014 $current = $groupState; 06015 } 06016 } 06017 06018 $allowedAssignList[] = array( 'group' => $group, 'states' => $states, 'current' => $current ); 06019 } 06020 return $allowedAssignList; 06021 } 06022 06023 /** 06024 * Gets the current states of the content object. 06025 * 06026 * Uses a member variable that caches the result. 06027 * 06028 * @return array an associative array with state group id => state id pairs 06029 * @param boolean $refreshCache if the cache in the member variable needs to be refreshed 06030 */ 06031 function stateIDArray( $refreshCache = false ) 06032 { 06033 if ( !$this->ID ) 06034 return array(); 06035 06036 if ( $refreshCache || !is_array( $this->StateIDArray ) ) 06037 { 06038 $this->StateIDArray = array(); 06039 $contentObjectID = $this->ID; 06040 eZDebug::accumulatorStart( 'state_id_array', 'states' ); 06041 $sql = "SELECT contentobject_state_id, group_id FROM ezcobj_state_link, ezcobj_state 06042 WHERE ezcobj_state.id=ezcobj_state_link.contentobject_state_id AND 06043 contentobject_id=$contentObjectID"; 06044 $db = eZDB::instance(); 06045 $rows = $db->arrayQuery( $sql ); 06046 foreach ( $rows as $row ) 06047 { 06048 $this->StateIDArray[$row['group_id']] = $row['contentobject_state_id']; 06049 } 06050 eZDebug::accumulatorStop( 'state_id_array' ); 06051 } 06052 06053 return $this->StateIDArray; 06054 } 06055 06056 function stateIdentifierArray() 06057 { 06058 if ( !$this->ID ) 06059 return array(); 06060 06061 eZDebug::accumulatorStart( 'state_identifier_array', 'states' ); 06062 $return = array(); 06063 $sql = "SELECT l.contentobject_state_id, s.identifier AS state_identifier, g.identifier AS state_group_identifier 06064 FROM ezcobj_state_link l, ezcobj_state s, ezcobj_state_group g 06065 WHERE l.contentobject_id={$this->ID} AND 06066 s.id=l.contentobject_state_id AND 06067 g.id=s.group_id"; 06068 $db = eZDB::instance(); 06069 $rows = $db->arrayQuery( $sql ); 06070 foreach ( $rows as $row ) 06071 { 06072 $return[] = $row['state_group_identifier'] . '/' . $row['state_identifier']; 06073 } 06074 eZDebug::accumulatorStop( 'state_identifier_array' ); 06075 return $return; 06076 } 06077 06078 /** 06079 * Sets the state of a content object. 06080 * 06081 * Changes are stored immediately in the database, does not require a store() of the content object. 06082 * Should only be called on instances of eZContentObject that have a ID (that were stored already before). 06083 * 06084 * @param eZContentObjectState $state 06085 * @return boolean true when the state was set, false if the state equals the current state 06086 */ 06087 function assignState( eZContentObjectState $state ) 06088 { 06089 $groupID = $state->attribute( 'group_id' ); 06090 $stateID = $state->attribute( 'id' ); 06091 $contentObjectID = $this->ID; 06092 06093 $currentStateIDArray = $this->stateIDArray( true ); 06094 $currentStateID = $currentStateIDArray[$groupID]; 06095 06096 if ( $currentStateID == $stateID ) 06097 { 06098 return false; 06099 } 06100 06101 $sql = "UPDATE ezcobj_state_link 06102 SET contentobject_state_id=$stateID 06103 WHERE contentobject_state_id=$currentStateID AND 06104 contentobject_id=$contentObjectID"; 06105 eZDB::instance()->query( $sql ); 06106 06107 $this->StateIDArray[$groupID] = $stateID; 06108 06109 return true; 06110 } 06111 06112 /** 06113 * Sets the default states of a content object. 06114 * 06115 * This function is called upon instantiating a content object with {@link eZContentClass::instantiate()}, so 06116 * should normally not be called by any other code. 06117 */ 06118 function assignDefaultStates() 06119 { 06120 $db = eZDB::instance(); 06121 $db->begin(); 06122 $defaultStates = eZContentObjectState::defaults(); 06123 $contentObjectID = $this->ID; 06124 foreach ( $defaultStates as $state ) 06125 { 06126 $stateID = $state->attribute( 'id' ); 06127 $db->query( "INSERT INTO ezcobj_state_link (contentobject_state_id, contentobject_id) 06128 VALUES($stateID, $contentObjectID)" ); 06129 } 06130 $db->commit(); 06131 } 06132 06133 /** 06134 * Restores attributes for current content object when it's being restored from trash 06135 */ 06136 public function restoreObjectAttributes() 06137 { 06138 $db = eZDB::instance(); 06139 $db->begin(); 06140 06141 foreach ( $this->allContentObjectAttributes( $this->attribute( "id" ) ) as $contentObjectAttribute ) 06142 { 06143 $datatype = $contentObjectAttribute->dataType(); 06144 if ( !$datatype instanceof eZDataType ) 06145 { 06146 eZDebug::writeError( "Attribute #{$contentObjectAttribute->attribute( "id" )} from contentobject #{$this->attribute( "id" )} isn't valid", __METHOD__ ); 06147 continue; 06148 } 06149 06150 $datatype->restoreTrashedObjectAttribute( $contentObjectAttribute ); 06151 } 06152 06153 $db->commit(); 06154 } 06155 06156 public $ID; 06157 public $Name; 06158 06159 /// Stores the current language 06160 public $CurrentLanguage; 06161 06162 /// Stores the current class name 06163 public $ClassName; 06164 06165 /// Cached class identifier 06166 public $ClassIdentifier; 06167 06168 /// Contains the datamap for content object attributes 06169 public $DataMap = array(); 06170 06171 /// Contains an array of the content object actions for the current object 06172 public $ContentActionList = false; 06173 06174 /// Contains a cached version of the content object attributes for the given version and language 06175 public $ContentObjectAttributes = array(); 06176 06177 /// Contains the main node id for this object 06178 public $MainNodeID = false; 06179 06180 /// Contains the arrays of relatedobject id by fetching input for this object 06181 public $InputRelationList = array(); 06182 06183 /** 06184 * Cache for the state ID array 06185 * 06186 * @var array 06187 * @see eZContentObject::stateIDArray() 06188 */ 06189 private $StateIDArray = false; 06190 } 06191 06192 ?>