eZ Publish  [trunk]
ezcontentobjecttreenode.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZContentObjectTreeNode 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 eZContentObjectTreeNode ezcontentobjecttreenode.php
00013   \brief The class eZContentObjectTreeNode does
00014 
00015 \verbatim
00016 
00017 Some algorithms
00018 ----------
00019 1. Adding new Node
00020 Enter  1 - parent_node
00021        2 - contentobject_id,  ( that is like a node value )
00022 
00023 (a) - get path_string, depth for parent node to built path_string  and to count depth for new one
00024 (c) - calculating attributes for new node and inserting it
00025 Returns node_id for added node
00026 
00027 
00028 2. Deleting node ( or subtree )
00029 Enter - node_id
00030 
00031 3. Move subtree in tree
00032 Enter node_id,new_parent_id
00033 
00034 
00035 4. fetching subtree
00036 
00037 \endverbatim
00038 
00039 */
00040 
00041 class eZContentObjectTreeNode extends eZPersistentObject
00042 {
00043     const SORT_FIELD_PATH = 1;
00044     const SORT_FIELD_PUBLISHED = 2;
00045     const SORT_FIELD_MODIFIED = 3;
00046     const SORT_FIELD_SECTION = 4;
00047     const SORT_FIELD_DEPTH = 5;
00048     const SORT_FIELD_CLASS_IDENTIFIER = 6;
00049     const SORT_FIELD_CLASS_NAME = 7;
00050     const SORT_FIELD_PRIORITY = 8;
00051     const SORT_FIELD_NAME = 9;
00052     const SORT_FIELD_MODIFIED_SUBNODE = 10;
00053     const SORT_FIELD_NODE_ID = 11;
00054     const SORT_FIELD_CONTENTOBJECT_ID = 12;
00055 
00056     const SORT_ORDER_DESC = 0;
00057     const SORT_ORDER_ASC = 1;
00058 
00059     /*!
00060      Constructor
00061     */
00062     function eZContentObjectTreeNode( $row = array() )
00063     {
00064         $this->eZPersistentObject( $row );
00065     }
00066 
00067     static function definition()
00068     {
00069         static $definition = array( "fields" => array( "node_id" => array( 'name' => "NodeID",
00070                                                              'datatype' => 'integer',
00071                                                              'default' => 0,
00072                                                              'required' => true ),
00073                                          "parent_node_id" => array( 'name' => "ParentNodeID",
00074                                                                     'datatype' => 'integer',
00075                                                                     'default' => 0,
00076                                                                     'required' => true,
00077                                                                     'foreign_class' => 'eZContentObjectTreeNode',
00078                                                                     'foreign_attribute' => 'node_id',
00079                                                                     'multiplicity' => '1..*' ),
00080                                          "main_node_id" => array( 'name' => "MainNodeID",
00081                                                                   'datatype' => 'integer',
00082                                                                   'default' => 0,
00083                                                                   'required' => true,
00084                                                                   'foreign_class' => 'eZContentObjectTreeNode',
00085                                                                   'foreign_attribute' => 'node_id',
00086                                                                   'multiplicity' => '1..*' ),
00087                                          "contentobject_id" => array( 'name' => "ContentObjectID",
00088                                                                       'datatype' => 'integer',
00089                                                                       'default' => 0,
00090                                                                       'required' => true,
00091                                                                       'foreign_class' => 'eZContentObject',
00092                                                                       'foreign_attribute' => 'id',
00093                                                                       'multiplicity' => '1..*' ),
00094                                          'contentobject_version' => array( 'name' => 'ContentObjectVersion',
00095                                                                            'datatype' => 'integer',
00096                                                                            'default' => 0,
00097                                                                            'required' => true ),
00098                                          'contentobject_is_published' => array( 'name' => 'ContentObjectIsPublished',
00099                                                                                 'datatype' => 'integer',
00100                                                                                 'default' => 0,
00101                                                                                 'required' => true ),
00102                                          "depth" => array( 'name' => "Depth",
00103                                                            'datatype' => 'integer',
00104                                                            'default' => 0,
00105                                                            'required' => true ),
00106                                          'sort_field' => array( 'name' => 'SortField',
00107                                                                 'datatype' => 'integer',
00108                                                                 'default' => self::SORT_FIELD_PATH,
00109                                                                 'required' => true ),
00110                                          'sort_order' => array( 'name' => 'SortOrder',
00111                                                                 'datatype' => 'integer',
00112                                                                 'default' => self::SORT_ORDER_ASC,
00113                                                                 'required' => true ),
00114                                          'priority' => array( 'name' => 'Priority',
00115                                                               'datatype' => 'integer',
00116                                                               'default' => 0,
00117                                                               'required' => true ),
00118                                          'modified_subnode' => array( 'name' => 'ModifiedSubNode',
00119                                                                       'datatype' => 'integer',
00120                                                                       'default' => 0,
00121                                                                       'required' => true ),
00122                                          "path_string" => array( 'name' => "PathString",
00123                                                                  'datatype' => 'string',
00124                                                                  'default' => '',
00125                                                                  'required' => true ),
00126                                          "path_identification_string" => array( 'name' => "PathIdentificationString",
00127                                                                                 'datatype' => 'text',
00128                                                                                 'default' => '',
00129                                                                                 'required' => true ),
00130                                          'remote_id' => array( 'name' => 'RemoteID',
00131                                                                'datatype' => 'string',
00132                                                                'default' => '',
00133                                                                'required' => true ),
00134                                          "is_hidden" => array( 'name' => "IsHidden",
00135                                                                'datatype' => 'integer',
00136                                                                'default' => 0,
00137                                                                'required' => true ),
00138                                          "is_invisible" => array( 'name' => "IsInvisible",
00139                                                                   'datatype' => 'integer',
00140                                                                   'default' => 0,
00141                                                                   'required' => true ) ),
00142                       "keys" => array( "node_id" ),
00143                       "function_attributes" => array( "name" => "getName",
00144                                                       'data_map' => 'dataMap',
00145                                                       'remote_id' => 'remoteID', // Note: This overrides remote_id field
00146                                                       "object" => "object",
00147                                                       "subtree" => "subTree",
00148                                                       "children" => "children",
00149                                                       "children_count" => "childrenCount",
00150                                                       'view_count' => 'viewCount',
00151                                                       'contentobject_version_object' => 'contentObjectVersionObject',
00152                                                       'sort_array' => 'sortArray',
00153                                                       'can_read' => 'canRead',
00154                                                       'can_pdf' =>  'canPdf',
00155                                                       'can_create' => 'canCreate',
00156                                                       'can_edit' => 'canEdit',
00157                                                       'can_hide' => 'canHide',
00158                                                       'can_remove' => 'canRemove',
00159                                                       'can_move' => 'canMoveFrom',
00160                                                       'can_move_from' => 'canMoveFrom',
00161                                                       'can_add_location' => 'canAddLocation',
00162                                                       'can_remove_location' => 'canRemoveLocation',
00163                                                       'can_view_embed' => 'canViewEmbed',
00164                                                       'is_main' => 'isMain',
00165                                                       'creator' => 'creator',
00166                                                       "path_with_names" => "pathWithNames",
00167                                                       "path" => "fetchPath",
00168                                                       'path_array' => 'pathArray',
00169                                                       "parent" => "fetchParent",
00170                                                       'url' => 'url',
00171                                                       'url_alias' => 'urlAlias',
00172                                                       'class_identifier' => 'classIdentifier',
00173                                                       'class_name' => 'className',
00174                                                       'hidden_invisible_string' => 'hiddenInvisibleString',
00175                                                       'hidden_status_string' => 'hiddenStatusString',
00176                                                       'classes_js_array' => 'availableClassesJsArray',
00177                                                       'is_container' => 'classIsContainer' ),
00178                       "increment_key" => "node_id",
00179                       "class_name" => "eZContentObjectTreeNode",
00180                       "name" => "ezcontentobject_tree" );
00181         return $definition;
00182     }
00183 
00184     /*!
00185      Creates a new tree node and returns it.
00186      \param $parentNodeID The ID of the parent or \c null if the node is not known yet.
00187      \param $contentObjectID The ID of the object it points to or \c null if it is not known yet.
00188      \param $contentObjectVersion The version of the object or \c 0 if not known yet.
00189      \param $sortField Number describing the field to sort by, or \c 0 if not known yet.
00190      \param $sortOrder Which way to sort, \c true means ascending while \c false is descending.
00191      \note The attribute \c remote_id will get an automatic and unique value.
00192     */
00193     static function create( $parentNodeID = null, $contentObjectID = null, $contentObjectVersion = 0,
00194                       $sortField = 0, $sortOrder = true )
00195     {
00196         $row = array( 'node_id' => null,
00197                       'main_node_id' => null,
00198                       'parent_node_id' => $parentNodeID,
00199                       'contentobject_id' => $contentObjectID,
00200                       'contentobject_version' => $contentObjectVersion,
00201                       'contentobject_is_published' => false,
00202                       'depth' => 1,
00203                       'path_string' => null,
00204                       'path_identification_string' => null,
00205                       'is_hidden' => false,
00206                       'is_invisible' => false,
00207                       'sort_field' => $sortField,
00208                       'sort_order' => $sortOrder,
00209                       'modified_subnode' => 0,
00210                       'remote_id' => eZRemoteIdUtility::generate( 'node' ),
00211                       'priority' => 0 );
00212         $node = new eZContentObjectTreeNode( $row );
00213         return $node;
00214     }
00215 
00216     /*!
00217      \return a map with all the content object attributes where the keys are the
00218              attribute identifiers.
00219      \sa eZContentObject::fetchDataMap
00220     */
00221     function dataMap()
00222     {
00223         return $this->object()->fetchDataMap( $this->attribute( 'contentobject_version' ) );
00224     }
00225 
00226     /*!
00227      Get remote id of content node, the remote ID is often used to synchronise imports and exports.
00228      If there is no remote ID a new unique one will be generated.
00229     */
00230     function remoteID()
00231     {
00232         $remoteID = eZPersistentObject::attribute( 'remote_id', true );
00233         if ( !$remoteID )
00234         {
00235             $this->setAttribute( 'remote_id', eZRemoteIdUtility::generate( 'node' ) );
00236             $this->sync( array( 'remote_id' ) );
00237             $remoteID = eZPersistentObject::attribute( 'remote_id', true );
00238         }
00239 
00240         return $remoteID;
00241     }
00242 
00243     /*!
00244      \return true if this node is the main node.
00245      */
00246     function isMain()
00247     {
00248         return $this->NodeID == $this->MainNodeID;
00249     }
00250 
00251     /*!
00252      \return the ID of the class attribute with the given ID.
00253      False is returned if no class/attribute by that identifier is found.
00254      If multiple classes have the same identifier, the first found is returned.
00255     */
00256     static function classAttributeIDByIdentifier( $identifier )
00257     {
00258         return eZContentClassAttribute::classAttributeIDByIdentifier( $identifier );
00259     }
00260 
00261     /*!
00262      \return the ID of the class with the given ID.
00263      False is returned if no class by that identifier is found.
00264      If multiple classes have the same identifier, the first found is returned.
00265     */
00266     static function classIDByIdentifier( $identifier )
00267     {
00268         return eZContentClass::classIDByIdentifier( $identifier );
00269     }
00270 
00271     /*!
00272      \return \c true if the node can be read by the current user.
00273      \sa checkAccess().
00274     */
00275     function canRead( )
00276     {
00277         if ( !isset( $this->Permissions["can_read"] ) )
00278         {
00279             $this->Permissions["can_read"] = $this->checkAccess( 'read' );
00280         }
00281         return ( $this->Permissions["can_read"] == 1 );
00282     }
00283 
00284     /*!
00285      \return \c true if the current user can create a pdf of this content object.
00286     */
00287     function canPdf( )
00288     {
00289         if ( !isset( $this->Permissions["can_pdf"] ) )
00290         {
00291             $this->Permissions["can_pdf"] = $this->checkAccess( 'pdf' );
00292         }
00293         return ( $this->Permissions["can_pdf"] == 1 );
00294     }
00295 
00296 
00297     /*!
00298      \return \c true if the node can be viewed as embeded object by the current user.
00299      \sa checkAccess().
00300     */
00301     function canViewEmbed( )
00302     {
00303         if ( !isset( $this->Permissions["can_view_embed"] ) )
00304         {
00305             $this->Permissions["can_view_embed"] = $this->checkAccess( 'view_embed' );
00306         }
00307         return ( $this->Permissions["can_view_embed"] == 1 );
00308     }
00309 
00310     /*!
00311      \return \c true if the node can be edited by the current user.
00312      \sa checkAccess().
00313     */
00314     function canEdit( )
00315     {
00316         if ( !isset( $this->Permissions["can_edit"] ) )
00317         {
00318             $this->Permissions["can_edit"] = $this->checkAccess( 'edit' );
00319             if ( $this->Permissions["can_edit"] != 1 )
00320             {
00321                  $user = eZUser::currentUser();
00322                  if ( $user->id() == $this->ContentObject->attribute( 'id' ) )
00323                  {
00324                      $access = $user->hasAccessTo( 'user', 'selfedit' );
00325                      if ( $access['accessWord'] == 'yes' )
00326                      {
00327                          $this->Permissions["can_edit"] = 1;
00328                      }
00329                  }
00330             }
00331         }
00332         return ( $this->Permissions["can_edit"] == 1 );
00333     }
00334 
00335     /*!
00336      \return \c true if the node can be hidden by the current user.
00337      \sa checkAccess().
00338     */
00339     function canHide( )
00340     {
00341         if ( !isset( $this->Permissions["can_hide"] ) )
00342         {
00343             $this->Permissions["can_hide"] = $this->checkAccess( 'hide' );
00344         }
00345         return ( $this->Permissions["can_hide"] == 1 );
00346     }
00347 
00348     /*!
00349      \return \c true if the current user can create a new node as child of this node.
00350      \sa checkAccess().
00351     */
00352     function canCreate( )
00353     {
00354         if ( !isset( $this->Permissions["can_create"] ) )
00355         {
00356             $this->Permissions["can_create"] = $this->checkAccess( 'create' );
00357         }
00358         return ( $this->Permissions["can_create"] == 1 );
00359     }
00360 
00361     /*!
00362      \return \c true if the node can be removed by the current user.
00363      \sa checkAccess().
00364     */
00365     function canRemove( )
00366     {
00367         if ( !isset( $this->Permissions["can_remove"] ) )
00368         {
00369             $this->Permissions["can_remove"] = $this->checkAccess( 'remove' );
00370         }
00371         return ( $this->Permissions["can_remove"] == 1 );
00372     }
00373 
00374     /*!
00375      Check if the node can be moved. (actually checks 'edit' and 'remove' permissions)
00376      \return \c true if the node can be moved by the current user.
00377      \sa checkAccess().
00378      \deprecated The function canMove() is preferred since its naming is clearer.
00379     */
00380     function canMove()
00381     {
00382         return $this->canMoveFrom();
00383     }
00384 
00385     /*!
00386      Check if the node can be moved. (actually checks 'edit' and 'remove' permissions)
00387      \return \c true if the node can be moved by the current user.
00388      \sa checkAccess().
00389     */
00390     function canMoveFrom( )
00391     {
00392         if ( !isset( $this->Permissions['can_move_from'] ) )
00393         {
00394             $this->Permissions['can_move_from'] = $this->checkAccess( 'edit' ) && $this->checkAccess( 'remove' );
00395         }
00396         return ( $this->Permissions['can_move_from'] == 1 );
00397     }
00398 
00399     /*!
00400      \return \c true if a node of class \a $classID can be moved to the current node by the current user.
00401      \sa checkAccess().
00402     */
00403     function canMoveTo( $classID = false )
00404     {
00405         if ( !isset( $this->Permissions['can_move_to'] ) )
00406         {
00407             $this->Permissions['can_move_to'] = $this->checkAccess( 'create', $classID );
00408         }
00409         return ( $this->Permissions['can_move_to'] == 1 );
00410     }
00411 
00412     /*!
00413      \return \c true if a node can be swaped by the current user.
00414      \sa checkAccess().
00415     */
00416     function canSwap()
00417     {
00418         if ( !isset( $this->Permissions['can_swap'] ) )
00419         {
00420             $this->Permissions['can_swap'] = $this->checkAccess( 'edit' );
00421         }
00422         return ( $this->Permissions['can_swap'] == 1 );
00423     }
00424 
00425     /*!
00426      \return \c true if current user can add object locations to current node.
00427      \sa checkAccess()
00428     */
00429     function canAddLocation()
00430     {
00431         if ( !isset( $this->Permissions['can_add_location'] ) )
00432         {
00433             $this->Permissions['can_add_location'] = $this->checkAccess( 'can_add_location' );
00434         }
00435         return ( $this->Permissions['can_add_location'] == 1 );
00436     }
00437 
00438     /*!
00439      \return \c true if current user can add object locations to current node.
00440     */
00441     function canRemoveLocation()
00442     {
00443         if ( !isset( $this->Permissions['can_remove_location'] ) )
00444         {
00445             $this->Permissions['can_remove_location'] = $this->checkAccess( 'can_remove_location' );
00446         }
00447         return ( $this->Permissions['can_remove_location'] == 1 );
00448     }
00449 
00450     /*!
00451      \static
00452      \returns the sort key for the given classAttributeID.
00453       int|string is returend. False is returned if unsuccessful.
00454     */
00455     static function sortKeyByClassAttributeID( $classAttributeID )
00456     {
00457         return eZContentClassAttribute::sortKeyTypeByID( $classAttributeID );
00458     }
00459 
00460     /*!
00461      \static
00462     */
00463     static function dataTypeByClassAttributeID( $classAttributeID )
00464     {
00465         return eZContentClassAttribute::dataTypeByID( $classAttributeID );
00466     }
00467 
00468 
00469     /*!
00470      Fetches the number of nodes which exists in the system.
00471     */
00472     static function fetchListCount()
00473     {
00474         $sql = "SELECT count( node_id ) as count FROM ezcontentobject_tree";
00475         $db = eZDB::instance();
00476         $rows = $db->arrayQuery( $sql );
00477         return $rows[0]['count'];
00478     }
00479 
00480 
00481     /*!
00482      Fetches a list of nodes and returns it. Offset and limitation can be set if needed.
00483     */
00484     static function fetchList( $asObject = true, $offset = false, $limit = false )
00485     {
00486         $sql = "SELECT * FROM ezcontentobject_tree";
00487         $parameters = array();
00488         if ( $offset !== false )
00489             $parameters['offset'] = $offset;
00490         if ( $limit !== false )
00491             $parameters['limit'] = $limit;
00492         $db = eZDB::instance();
00493         $rows = $db->arrayQuery( $sql, $parameters );
00494         $nodes = array();
00495         if ( $asObject )
00496         {
00497             foreach ( $rows as $row )
00498             {
00499                 $nodes[] = new eZContentObjectTreeNode( $row );
00500             }
00501             return $nodes;
00502         }
00503         else
00504             return $rows;
00505     }
00506 
00507     /*!
00508         \a static
00509     */
00510     static function createSortingSQLStrings( $sortList, $treeTableName = 'ezcontentobject_tree', $allowCustomColumns = false )
00511     {
00512         $sortingInfo = array( 'sortCount'           => 0,
00513                               'sortingFields'       => " path_string ASC",
00514                               'attributeJoinCount'  => 0,
00515                               'attributeFromSQL'    => "",
00516                               'attributeTargetSQL'  => "",
00517                               'attributeWhereSQL'   => "" );
00518 
00519         if ( $sortList and is_array( $sortList ) and count( $sortList ) > 0 )
00520         {
00521             if ( count( $sortList ) > 1 and !is_array( $sortList[0] ) )
00522             {
00523                 $sortList = array( $sortList );
00524             }
00525 
00526             $sortingFields      = '';
00527             $sortCount          = 0;
00528             $attributeJoinCount = 0;
00529             $stateJoinCount     = 0;
00530             $attributeFromSQL   = "";
00531             $attributeWhereSQL  = "";
00532             $datatypeSortingTargetSQL = "";
00533 
00534             foreach ( $sortList as $sortBy )
00535             {
00536                 if ( is_array( $sortBy ) and count( $sortBy ) > 0 )
00537                 {
00538                     if ( $sortCount > 0 )
00539                     {
00540                         $sortingFields .= ', ';
00541                     }
00542 
00543                     $sortField = $sortBy[0];
00544                     switch ( $sortField )
00545                     {
00546                         case 'path':
00547                         {
00548                             $sortingFields .= 'path_string';
00549                         } break;
00550                         case 'path_string':
00551                         {
00552                             $sortingFields .= 'path_identification_string';
00553                         } break;
00554                         case 'published':
00555                         {
00556                             $sortingFields .= 'ezcontentobject.published';
00557                         } break;
00558                         case 'modified':
00559                         {
00560                             $sortingFields .= 'ezcontentobject.modified';
00561                         } break;
00562                         case 'modified_subnode':
00563                         {
00564                             $sortingFields .= 'modified_subnode';
00565                         } break;
00566                         case 'section':
00567                         {
00568                             $sortingFields .= 'ezcontentobject.section_id';
00569                         } break;
00570                         case 'node_id':
00571                         {
00572                             $sortingFields .= $treeTableName . '.node_id';
00573                         } break;
00574                         case 'contentobject_id':
00575                         {
00576                             $sortingFields .= $treeTableName . '.contentobject_id';
00577                         } break;
00578                         case 'depth':
00579                         {
00580                             $sortingFields .= 'depth';
00581                         } break;
00582                         case 'class_identifier':
00583                         {
00584                             $sortingFields .= 'ezcontentclass.identifier';
00585                         } break;
00586                         case 'class_name':
00587                         {
00588                             $classNameFilter = eZContentClassName::sqlFilter();
00589                             $sortingFields .= 'contentclass_name';
00590                             $datatypeSortingTargetSQL .= ", $classNameFilter[nameField] AS contentclass_name";
00591                             $attributeFromSQL .= ", $classNameFilter[from]";
00592                             $attributeWhereSQL .= "$classNameFilter[where] AND ";
00593                         } break;
00594                         case 'priority':
00595                         {
00596                             $sortingFields .= $treeTableName . '.priority';
00597                         } break;
00598                         case 'name':
00599                         {
00600                             $sortingFields .= 'ezcontentobject_name.name';
00601                         } break;
00602                         case 'attribute':
00603                         {
00604                             $classAttributeID = $sortBy[2];
00605                             if ( !is_numeric( $classAttributeID ) )
00606                                 $classAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $classAttributeID );
00607 
00608 
00609                             $contentAttributeTableAlias = "a$attributeJoinCount";
00610                             $datatypeFromSQL = "ezcontentobject_attribute $contentAttributeTableAlias";
00611                             $datatypeWhereSQL = "
00612                                    $contentAttributeTableAlias.contentobject_id = ezcontentobject.id AND
00613                                    $contentAttributeTableAlias.contentclassattribute_id = $classAttributeID AND
00614                                    $contentAttributeTableAlias.version = ezcontentobject_name.content_version AND";
00615                             $datatypeWhereSQL .= eZContentLanguage::sqlFilter( $contentAttributeTableAlias, 'ezcontentobject' );
00616 
00617                             $dataType = eZDataType::create( eZContentObjectTreeNode::dataTypeByClassAttributeID( $classAttributeID ) );
00618                             if( is_object( $dataType ) && $dataType->customSorting() )
00619                             {
00620                                 $params = array();
00621                                 $params['contentobject_attr_id'] = "$contentAttributeTableAlias.id";
00622                                 $params['contentobject_attr_version'] = "$contentAttributeTableAlias.version";
00623                                 $params['table_alias_suffix'] = "$attributeJoinCount";
00624 
00625                                 $sql = $dataType->customSortingSQL( $params );
00626 
00627                                 $datatypeFromSQL .= ", {$sql['from']}";
00628                                 $datatypeWhereSQL .= " AND {$sql['where']}";
00629                                 $datatypeSortingFieldSQL = $sql['sorting_field'];
00630                                 $datatypeSortingTargetSQL .= ', ' . $sql['sorting_field'];
00631                             }
00632                             else
00633                             {
00634                                 // Look up datatype for standard sorting
00635                                 $sortKeyType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $classAttributeID );
00636                                 switch ( $sortKeyType )
00637                                 {
00638                                     case 'string':
00639                                         {
00640                                             $sortKey = 'sort_key_string';
00641                                         } break;
00642 
00643                                     case 'int':
00644                                     default:
00645                                         {
00646                                             $sortKey = 'sort_key_int';
00647                                         } break;
00648                                 }
00649 
00650                                 $datatypeSortingFieldSQL = "a$attributeJoinCount.$sortKey";
00651                                 $datatypeSortingTargetSQL .= ', ' . $datatypeSortingFieldSQL;
00652                             }
00653 
00654                             $sortingFields .= "$datatypeSortingFieldSQL";
00655                             $attributeFromSQL .= ", $datatypeFromSQL";
00656                             $attributeWhereSQL .= "$datatypeWhereSQL AND ";
00657 
00658                             $attributeJoinCount++;
00659                         }break;
00660 
00661                         case 'state':
00662                         {
00663                             $stateGroupID = $sortBy[2];
00664                             if ( !is_numeric( $stateGroupID ) )
00665                             {
00666                                 $stateGroup = eZContentObjectStateGroup::fetchByIdentifier( $stateGroupID );
00667                                 if ( $stateGroup )
00668                                 {
00669                                     $stateGroupID = $stateGroup->attribute( 'id' );
00670                                 }
00671                                 else
00672                                 {
00673                                     eZDebug::writeError( "Unknown content object state group '$stateGroupID'" );
00674                                     continue 2;
00675                                 }
00676                             }
00677 
00678                             $stateAlias = "s$stateJoinCount";
00679                             $stateLinkAlias = "sl$stateJoinCount";
00680                             $sortingFields .= "$stateAlias.priority";
00681                             $datatypeSortingTargetSQL .= ", $stateAlias.priority";
00682                             $attributeFromSQL .= ", ezcobj_state $stateAlias, ezcobj_state_link $stateLinkAlias";
00683                             $attributeWhereSQL .= "$stateLinkAlias.contentobject_id=$treeTableName.contentobject_id AND
00684                                                    $stateLinkAlias.contentobject_state_id=$stateAlias.id AND
00685                                                    $stateAlias.group_id=$stateGroupID AND ";
00686                         } break;
00687 
00688                         default:
00689                         {
00690                             if ( $allowCustomColumns )
00691                             {
00692                                 $sortingFields .= $sortField;
00693                             }
00694                             else
00695                             {
00696                                 eZDebug::writeWarning( 'Unknown sort field: ' . $sortField, __METHOD__ );
00697                                 continue;
00698                             }
00699                         }
00700                     }
00701                     $sortOrder = true; // true is ascending
00702                     if ( isset( $sortBy[1] ) )
00703                         $sortOrder = $sortBy[1];
00704                     $sortingFields .= $sortOrder ? " ASC" : " DESC";
00705                     ++$sortCount;
00706                 }
00707             }
00708 
00709             $sortingInfo['sortCount']           = $sortCount;
00710             $sortingInfo['sortingFields']       = $sortingFields;
00711             $sortingInfo['attributeTargetSQL'] = $datatypeSortingTargetSQL;
00712             $sortingInfo['attributeJoinCount']  = $attributeJoinCount;
00713             $sortingInfo['attributeFromSQL']    = $attributeFromSQL;
00714             $sortingInfo['attributeWhereSQL']   = $attributeWhereSQL;
00715         }
00716         else if ( $sortList === array() )
00717         {
00718             $sortingInfo['sortingFields'] = '';
00719         }
00720 
00721         return $sortingInfo;
00722     }
00723 
00724     /*!
00725         \a static
00726     */
00727     static function createClassFilteringSQLString( $classFilterType, &$classFilterArray )
00728     {
00729         // Check for class filtering
00730         $classCondition = '';
00731 
00732         if ( isset( $classFilterType ) &&
00733              ( $classFilterType == 'include' || $classFilterType == 'exclude' ) &&
00734              count( $classFilterArray ) > 0 )
00735         {
00736             $classCondition = '  ';
00737             $i = 0;
00738             $classCount = count( $classFilterArray );
00739             $classIDArray = array();
00740             foreach ( $classFilterArray as $classID )
00741             {
00742                 $originalClassID = $classID;
00743                 // Check if classes are recerenced by identifier
00744                 if ( is_string( $classID ) && !is_numeric( $classID ) )
00745                 {
00746                     $classID = eZContentClass::classIDByIdentifier( $classID );
00747                 }
00748                 if ( is_numeric( $classID ) )
00749                 {
00750                     $classIDArray[] = $classID;
00751                 }
00752                 else
00753                 {
00754                     eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
00755                 }
00756             }
00757 
00758             if ( count( $classIDArray ) > 0  )
00759             {
00760                 $classCondition .= " ezcontentobject.contentclass_id ";
00761                 if ( $classFilterType == 'include' )
00762                     $classCondition .= " IN ";
00763                 else
00764                     $classCondition .= " NOT IN ";
00765 
00766                 $classIDString =  implode( ', ', $classIDArray );
00767                 $classCondition .= ' ( ' . $classIDString . ' ) AND';
00768             }
00769             else
00770             {
00771                 if ( count( $classIDArray ) == 0 and count( $classFilterArray ) > 0 and $classFilterType == 'include' )
00772                 {
00773                     $classCondition = false;
00774                 }
00775             }
00776         }
00777 
00778         return $classCondition;
00779     }
00780 
00781     /*!
00782         \a static
00783     */
00784     static function createExtendedAttributeFilterSQLStrings( &$extendedAttributeFilter )
00785     {
00786         $filter = array( 'tables'   => '',
00787                          'joins'    => '',
00788                          'columns'  => '',
00789                          'group_by' => '' );
00790 
00791         if ( $extendedAttributeFilter and count( $extendedAttributeFilter ) > 1 )
00792         {
00793             $extendedAttributeFilterID      = $extendedAttributeFilter['id'];
00794             $extendedAttributeFilterParams  = $extendedAttributeFilter['params'];
00795             $filterINI                      = eZINI::instance( 'extendedattributefilter.ini' );
00796 
00797             if ( !$filterINI->hasGroup( $extendedAttributeFilterID ) )
00798             {
00799                 eZDebug::writeError( "Unable to find configuration for the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
00800                 return $filter;
00801             }
00802 
00803             $filterClassName    = $filterINI->variable( $extendedAttributeFilterID, 'ClassName' );
00804             $filterMethodName   = $filterINI->variable( $extendedAttributeFilterID, 'MethodName' );
00805 
00806             if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'FileName' ) )
00807             {
00808                 $filterFile = $filterINI->variable( $extendedAttributeFilterID, 'FileName' );
00809 
00810                 if ( $filterINI->hasVariable( $extendedAttributeFilterID, 'ExtensionName' ) )
00811                 {
00812                     $extensionName = $filterINI->variable( $extendedAttributeFilterID, 'ExtensionName' );
00813                     include_once( eZExtension::baseDirectory() . "/$extensionName/$filterFile" );
00814                 }
00815                 else
00816                 {
00817                     include_once( $filterFile );
00818                 }
00819             }
00820 
00821             if ( !class_exists( $filterClassName, true ) )
00822             {
00823                 eZDebug::writeError( "Unable to find the PHP class '$filterClassName' associated with the extended attribute filter '$extendedAttributeFilterID', the filter will be ignored", __METHOD__ );
00824                 return $filter;
00825             }
00826 
00827             $classObject        = new $filterClassName();
00828             $parameterArray     = array( $extendedAttributeFilterParams );
00829 
00830             $sqlResult          = call_user_func_array( array( $classObject, $filterMethodName ), $parameterArray );
00831 
00832             $filter['tables']   = $sqlResult['tables'];
00833             $filter['joins']    = $sqlResult['joins'];
00834             $filter['columns']  = isset( $sqlResult['columns'] ) ? $sqlResult['columns'] : '';
00835 
00836             if( isset( $sqlResult['group_by'] ) )
00837                 $filter['group_by'] =  $sqlResult['group_by'];
00838         }
00839 
00840         return $filter;
00841     }
00842 
00843     /*!
00844         \a static
00845     */
00846     static function createMainNodeConditionSQLString( $mainNodeOnly )
00847     {
00848         // Main node check
00849         $mainNodeCondition = '';
00850         if ( isset( $mainNodeOnly ) && $mainNodeOnly === true )
00851         {
00852             $mainNodeCondition = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
00853         }
00854 
00855         return $mainNodeCondition;
00856     }
00857 
00858     /*!
00859         \a static
00860     */
00861     static function createObjectNameFilterConditionSQLString( $filter )
00862     {
00863         if ( !$filter )
00864             return '';
00865 
00866         $db = eZDB::instance();
00867         if ( $filter == 'others' )
00868         {
00869             $alphabet = eZAlphabetOperator::fetchAlphabet();
00870             $sql = '';
00871             foreach ( $alphabet as $letter )
00872             {
00873                 $sql .= " AND ezcontentobject_name.name NOT LIKE '". $db->escapeString( $letter ) . "%' ";
00874             }
00875             return $sql;
00876         }
00877         $objectNameFilterSQL =  " AND ezcontentobject_name.name LIKE '" . $db->escapeString( $filter ) ."%'";
00878         return $objectNameFilterSQL;
00879     }
00880 
00881 
00882     /*!
00883         \a static
00884     */
00885     static function createAttributeFilterSQLStrings( &$attributeFilter, &$sortingInfo = array( 'sortCount' => 0, 'attributeJoinCount' => 0 ) )
00886     {
00887         // Check for attribute filtering
00888 
00889         $filterSQL = array( 'from'    => '',
00890                             'where'   => '' );
00891 
00892         $totalAttributesFiltersCount = 0;
00893         $invalidAttributesFiltersCount = 0;
00894 
00895         if ( isset( $attributeFilter ) && $attributeFilter !== false )
00896         {
00897             if ( !is_array( $attributeFilter ) )
00898             {
00899                 eZDebug::writeError( "\$attributeFilter needs to be an array", __METHOD__  );
00900                 return $filterSQL;
00901             }
00902 
00903             $filterArray = $attributeFilter;
00904 
00905             // Check if first value of array is a string.
00906             // To check for and/or filtering
00907             $filterJoinType = 'AND';
00908             if ( is_string( $filterArray[0] ) )
00909             {
00910                 if ( strtolower( $filterArray[0] ) == 'or' )
00911                 {
00912                     $filterJoinType = 'OR';
00913                 }
00914                 else if ( strtolower( $filterArray[0] ) == 'and' )
00915                 {
00916                     $filterJoinType = 'AND';
00917                 }
00918                 unset( $filterArray[0] );
00919             }
00920 
00921             $attibuteFilterJoinSQL = "";
00922             $filterCount = $sortingInfo['sortCount'];
00923             $justFilterCount = 0;
00924 
00925             $db = eZDB::instance();
00926             if ( is_array( $filterArray ) )
00927             {
00928                 // Handle attribute filters and generate SQL
00929                 $totalAttributesFiltersCount = count( $filterArray );
00930 
00931                 foreach ( $filterArray as $filter )
00932                 {
00933                     $isFilterValid = true; // by default assumes that filter is valid
00934 
00935                     $filterAttributeID = $filter[0];
00936                     $filterType = $filter[1];
00937                     $filterValue = is_array( $filter[2] ) ? '' : $db->escapeString( $filter[2] );
00938 
00939                     $useAttributeFilter = false;
00940                     switch ( $filterAttributeID )
00941                     {
00942                         case 'path':
00943                         {
00944                             $filterField = 'path_string';
00945                         } break;
00946                         case 'published':
00947                         {
00948                             $filterField = 'ezcontentobject.published';
00949                         } break;
00950                         case 'modified':
00951                         {
00952                             $filterField = 'ezcontentobject.modified';
00953                         } break;
00954                         case 'modified_subnode':
00955                         {
00956                             $filterField = 'modified_subnode';
00957                         } break;
00958                         case 'node_id':
00959                         {
00960                             $filterField = 'ezcontentobject_tree.node_id';
00961                         } break;
00962                         case 'contentobject_id':
00963                         {
00964                             $filterField = 'ezcontentobject_tree.contentobject_id';
00965                         } break;
00966                         case 'section':
00967                         {
00968                             $filterField = 'ezcontentobject.section_id';
00969                         } break;
00970                         case 'state':
00971                         {
00972                             // state only supports =, !=, in, and not_in
00973                             // other operators do not make any sense in this context
00974                             $hasFilterOperator = true;
00975 
00976                             switch ( $filterType )
00977                             {
00978                                 case '=' :
00979                                 case '!=':
00980                                 {
00981                                     $subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2];
00982                                     $filterOperator = ( $filterType == '=' ? 'IN' : 'NOT IN' );
00983                                 } break;
00984 
00985                                 case 'in':
00986                                 case 'not_in' :
00987                                 {
00988                                     if ( is_array( $filter[2] ) )
00989                                     {
00990                                         $subQueryCondition = $db->generateSQLINStatement( $filter[2], 'contentobject_state_id', false, false, 'int' );
00991                                         $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
00992                                     }
00993                                     else
00994                                     {
00995                                         $hasFilterOperator = false;
00996                                     }
00997                                 } break;
00998 
00999                                 default :
01000                                 {
01001                                     $hasFilterOperator = false;
01002                                     eZDebug::writeError( "Unknown attribute filter type for state: $filterType", __METHOD__ );
01003                                 } break;
01004                             }
01005 
01006                             if ( $hasFilterOperator )
01007                             {
01008                                 if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
01009                                     $attibuteFilterJoinSQL .= " $filterJoinType ";
01010 
01011                                 $attibuteFilterJoinSQL .= "ezcontentobject.id $filterOperator (SELECT contentobject_id FROM ezcobj_state_link WHERE $subQueryCondition)";
01012 
01013                                 $filterCount++;
01014                                 $justFilterCount++;
01015                             }
01016 
01017                             continue 2;
01018                         } break;
01019                         case 'depth':
01020                         {
01021                             $filterField = 'depth';
01022                         } break;
01023                         case 'class_identifier':
01024                         {
01025                             $filterField = 'ezcontentclass.identifier';
01026                         } break;
01027                         case 'class_name':
01028                         {
01029                             $classNameFilter = eZContentClassName::sqlFilter();
01030                             $filterField = $classNameFilter['nameField'];
01031                             $filterSQL['from'] .= ", $classNameFilter[from]";
01032                             $filterSQL['where'] .= "$classNameFilter[where] AND ";
01033                         } break;
01034                         case 'priority':
01035                         {
01036                             $filterField = 'ezcontentobject_tree.priority';
01037                         } break;
01038                         case 'name':
01039                         {
01040                             $filterField = 'ezcontentobject_name.name';
01041                         } break;
01042                         case 'owner':
01043                         {
01044                             $filterField = 'ezcontentobject.owner_id';
01045                         } break;
01046                         case 'visibility':
01047                         {
01048                             $filterValue = ( $filterValue == '1' ) ? 0 : 1;
01049                             $filterField = 'ezcontentobject_tree.is_invisible';
01050                         } break;
01051                         default:
01052                         {
01053                             $useAttributeFilter = true;
01054                         } break;
01055                     }
01056 
01057                     if ( $useAttributeFilter )
01058                     {
01059                         if ( !is_numeric( $filterAttributeID ) )
01060                             $filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier( $filterAttributeID );
01061 
01062                         if ( $filterAttributeID === false )
01063                         {
01064                             $isFilterValid = false;
01065                             if( $filterJoinType === 'AND' )
01066                             {
01067                                 // go out
01068                                 $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
01069                                 break;
01070                             }
01071 
01072                             ++$invalidAttributesFiltersCount;
01073                         }
01074                         else
01075                         {
01076                             // Check datatype for filtering
01077                             $filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $filterAttributeID );
01078                             if ( $filterDataType === false )
01079                             {
01080                                 $isFilterValid = false;
01081                                 if( $filterJoinType === 'AND' )
01082                                 {
01083                                     // go out
01084                                     $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
01085                                     break;
01086                                 }
01087 
01088                                 // check next filter
01089                                 ++$invalidAttributesFiltersCount;
01090                             }
01091                             else
01092                             {
01093                                 $sortKey = false;
01094                                 if ( $filterDataType == 'string' )
01095                                 {
01096                                     $sortKey = 'sort_key_string';
01097                                 }
01098                                 else
01099                                 {
01100                                     $sortKey = 'sort_key_int';
01101                                 }
01102 
01103                                 $filterField = "a$filterCount.$sortKey";
01104 
01105                                 // Use the same joins as we do when sorting,
01106                                 // if more attributes are filtered by we will append them
01107                                 if ( $filterCount >= $sortingInfo['attributeJoinCount'] )
01108                                 {
01109                                     $filterSQL['from']  .= ", ezcontentobject_attribute a$filterCount ";
01110                                 }
01111 
01112                                 $filterSQL['where'] .= "
01113                                   a$filterCount.contentobject_id = ezcontentobject.id AND
01114                                   a$filterCount.contentclassattribute_id = $filterAttributeID AND
01115                                   a$filterCount.version = ezcontentobject_name.content_version AND ";
01116                                 $filterSQL['where'] .= eZContentLanguage::sqlFilter( "a$filterCount", 'ezcontentobject' ). ' AND ';
01117                             }
01118                         }
01119                     }
01120 
01121                     if ( $isFilterValid )
01122                     {
01123                         $hasFilterOperator = true;
01124                         // Controls quotes around filter value, some filters do this manually
01125                         $noQuotes = false;
01126                         // Controls if $filterValue or $filter[2] is used, $filterValue is already escaped
01127                         $unEscape = false;
01128 
01129                         switch ( $filterType )
01130                         {
01131                             case '=' :
01132                             {
01133                                 $filterOperator = '=';
01134                             }break;
01135 
01136                             case '!=' :
01137                             {
01138                                 $filterOperator = '<>';
01139                             }break;
01140 
01141                             case '>' :
01142                             {
01143                                 $filterOperator = '>';
01144                             }break;
01145 
01146                             case '<' :
01147                             {
01148                                 $filterOperator = '<';
01149                             }break;
01150 
01151                             case '<=' :
01152                             {
01153                                 $filterOperator = '<=';
01154                             }break;
01155 
01156                             case '>=' :
01157                             {
01158                                 $filterOperator = '>=';
01159                             }break;
01160 
01161                             case 'like':
01162                             case 'not_like':
01163                             {
01164                                 $filterOperator = ( $filterType == 'like' ? 'LIKE' : 'NOT LIKE' );
01165                                 // We escape the string ourselves, this MUST be done before wildcard replace
01166                                 $filter[2] = $db->escapeString( $filter[2] );
01167                                 $unEscape = true;
01168                                 // Since * is used as wildcard we need to transform the string to
01169                                 // use % as wildcard. The following rules apply:
01170                                 // - % -> \%
01171                                 // - * -> %
01172                                 // - \* -> *
01173                                 // - \\ -> \
01174 
01175                                 $filter[2] = preg_replace( array( '#%#m',
01176                                                                   '#(?<!\\\\)\\*#m',
01177                                                                   '#(?<!\\\\)\\\\\\*#m',
01178                                                                   '#\\\\\\\\#m' ),
01179                                                            array( '\\%',
01180                                                                   '%',
01181                                                                   '*',
01182                                                                   '\\\\' ),
01183                                                            $filter[2] );
01184                             } break;
01185 
01186                             case 'in':
01187                             case 'not_in' :
01188                             {
01189                                 $filterOperator = ( $filterType == 'in' ? 'IN' : 'NOT IN' );
01190                                 // Turn off quotes for value, we do this ourselves
01191                                 $noQuotes = true;
01192                                 if ( is_array( $filter[2] ) )
01193                                 {
01194                                     reset( $filter[2] );
01195                                     while ( list( $key, $value ) = each( $filter[2] ) )
01196                                     {
01197                                         // Non-numerics must be escaped to avoid SQL injection
01198                                         $filter[2][$key] = is_numeric( $value ) ? $value : "'" . $db->escapeString( $value ) . "'";
01199                                     }
01200                                     $filterValue = '(' .  implode( ",", $filter[2] ) . ')';
01201                                 }
01202                                 else
01203                                 {
01204                                     $hasFilterOperator = false;
01205                                 }
01206                             } break;
01207 
01208                             case 'between':
01209                             case 'not_between' :
01210                             {
01211                                 $filterOperator = ( $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN' );
01212                                 // Turn off quotes for value, we do this ourselves
01213                                 $noQuotes = true;
01214                                 if ( is_array( $filter[2] ) )
01215                                 {
01216                                     // Check for non-numerics to avoid SQL injection
01217                                     if ( !is_numeric( $filter[2][0] ) )
01218                                         $filter[2][0] = "'" . $db->escapeString( $filter[2][0] ) . "'";
01219                                     if ( !is_numeric( $filter[2][1] ) )
01220                                         $filter[2][1] = "'" . $db->escapeString( $filter[2][1] ) . "'";
01221 
01222                                     $filterValue = $filter[2][0] . ' AND ' . $filter[2][1];
01223                                 }
01224                             } break;
01225 
01226                             default :
01227                             {
01228                                 $hasFilterOperator = false;
01229                                 eZDebug::writeError( "Unknown attribute filter type: $filterType", __METHOD__ );
01230                             }break;
01231 
01232                         }
01233                         if ( $hasFilterOperator )
01234                         {
01235                             if ( ( $filterCount - $sortingInfo['sortCount'] ) > 0 )
01236                                 $attibuteFilterJoinSQL .= " $filterJoinType ";
01237 
01238                             // If $unEscape is true we get the filter value from the 2nd element instead
01239                             // which must have been escaped by filter type
01240                             $filterValue = $unEscape ? $filter[2] : $filterValue;
01241 
01242                             $attibuteFilterJoinSQL .= "$filterField $filterOperator ";
01243                             $attibuteFilterJoinSQL .= $noQuotes ? "$filterValue " : "'$filterValue' ";
01244 
01245                             $filterCount++;
01246                             $justFilterCount++;
01247                         }
01248                     }
01249                 } // end of 'foreach ( $filterArray as $filter )'
01250 
01251                 if ( $totalAttributesFiltersCount == $invalidAttributesFiltersCount )
01252                 {
01253                     eZDebug::writeNotice( "Attribute filter returned false" );
01254                     $filterSQL = false;
01255                 }
01256                 else
01257                 {
01258                     if ( $justFilterCount > 0 )
01259                         $filterSQL['where'] .= "                            ( " . $attibuteFilterJoinSQL . " ) AND ";
01260                 }
01261             } // end of 'if ( is_array( $filterArray ) )'
01262         }
01263 
01264         return $filterSQL;
01265     }
01266 
01267     /*!
01268         \a static
01269     */
01270     static function createNotEqParentSQLString( $nodeID, $depth = false, $depthOperator = 'le' )
01271     {
01272         $notEqParentString  = '';
01273         if( !$depth || $depthOperator == 'le' || $depthOperator == 'lt' )
01274         {
01275             $notEqParentString  = "ezcontentobject_tree.node_id != $nodeID AND";
01276         }
01277 
01278         return $notEqParentString;
01279     }
01280 
01281     /*!
01282         \a static
01283     */
01284     static function createPathConditionSQLString( $nodePath, $nodeDepth, $depth = false, $depthOperator = 'le' )
01285     {
01286         $pathCondition  = '';
01287         $depthCondition = '';
01288 
01289         if ( $depth )
01290         {
01291             $sqlDepthOperator = '<=';
01292             if ( $depthOperator )
01293             {
01294                 if ( $depthOperator == 'lt' )
01295                 {
01296                     $sqlDepthOperator = '<';
01297                 }
01298                 else if ( $depthOperator == 'gt' )
01299                 {
01300                     $sqlDepthOperator = '>';
01301                 }
01302                 else if ( $depthOperator == 'le' )
01303                 {
01304                     $sqlDepthOperator = '<=';
01305                 }
01306                 else if ( $depthOperator == 'ge' )
01307                 {
01308                     $sqlDepthOperator = '>=';
01309                 }
01310                 else if ( $depthOperator == 'eq' )
01311                 {
01312                     $sqlDepthOperator = '=';
01313                 }
01314             }
01315 
01316             $nodeDepth += $depth;
01317             $depthCondition = ' ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . '  and ';
01318         }
01319 
01320         $pathCondition = " ezcontentobject_tree.path_string like '$nodePath%' and $depthCondition ";
01321         return $pathCondition;
01322     }
01323 
01324     /*!
01325         \a static
01326     */
01327     static function createPathConditionAndNotEqParentSQLStrings( &$outPathConditionStr, &$outNotEqParentStr, $nodeID, $depth = false, $depthOperator = 'le' )
01328     {
01329         if ( !$depthOperator )
01330         {
01331             $depthOperator = 'le';
01332         }
01333 
01334         // check if we are only fetching children
01335         // - depth (lower than or) eqaul to 1
01336         // - depth lower than 2 = depth equal to 1
01337         $onlyChildren = ( $depth === 1 && ( $depthOperator === 'le' || $depthOperator === 'eq'  ) ) ||
01338                         ( $depth === 2 && $depthOperator === 'lt' );
01339 
01340         if ( is_array( $nodeID ) && count( $nodeID ) == 1 )
01341         {
01342             $nodeID = $nodeID[0];
01343         }
01344 
01345         if ( is_array( $nodeID ) )
01346         {
01347             $outNotEqParentStr = '';
01348 
01349             // a parent_node_id condition suffits when only fetching children
01350             if ( $onlyChildren )
01351             {
01352                 $db = eZDB::instance();
01353                 $outPathConditionStr = $db->generateSQLINStatement( $nodeID, 'ezcontentobject_tree.parent_node_id', false, true, 'int' ) . ' and';
01354             }
01355             else
01356             {
01357                 $nodeIDList             = $nodeID;
01358                 $sqlPartForOneNodeList  = array();
01359 
01360                 foreach ( $nodeIDList as $nodeID )
01361                 {
01362                     $node           = eZContentObjectTreeNode::fetch( $nodeID, false, false );
01363                     if ( !is_array( $node ) )
01364                         return false;
01365 
01366                     $nodePath       = $node['path_string'];
01367                     $nodeDepth      = $node['depth'];
01368                     $depthCond      = '';
01369                     if ( $depth )
01370                     {
01371                         $sqlDepthOperator = '<=';
01372                         if ( $depthOperator )
01373                         {
01374                             if ( $depthOperator == 'lt' )
01375                             {
01376                                 $sqlDepthOperator = '<';
01377                             }
01378                             else if ( $depthOperator == 'gt' )
01379                             {
01380                                 $sqlDepthOperator = '>';
01381                             }
01382                             else if ( $depthOperator == 'le' )
01383                             {
01384                                 $sqlDepthOperator = '<=';
01385                             }
01386                             else if ( $depthOperator == 'ge' )
01387                             {
01388                                 $sqlDepthOperator = '>=';
01389                             }
01390                             else if ( $depthOperator == 'eq' )
01391                             {
01392                                 $sqlDepthOperator = '=';
01393                             }
01394                         }
01395                         $nodeDepth += $depth;
01396                         $depthCond = ' and ezcontentobject_tree.depth '. $sqlDepthOperator . ' ' . $nodeDepth . ' ';
01397                     }
01398 
01399                     $requireNotEqParentStr      = !$depth || $depthOperator == 'le' || $depthOperator == 'lt';
01400                     $notEqParentStr             = $requireNotEqParentStr ? " and ezcontentobject_tree.node_id != $nodeID " : '';
01401                     $sqlPartForOneNodeList[]    = " ( ezcontentobject_tree.path_string like '$nodePath%'   $depthCond $notEqParentStr ) ";
01402                 }
01403                 $outPathConditionStr = implode( ' or ', $sqlPartForOneNodeList );
01404                 $outPathConditionStr = ' (' . $outPathConditionStr . ') and';
01405             }
01406         }
01407         else
01408         {
01409             if ( $nodeID == 0 )
01410             {
01411                 return false;
01412             }
01413 
01414             // a parent_node_id condition suffits when only fetching children
01415             if ( $onlyChildren )
01416             {
01417                 $outNotEqParentStr = '';
01418                 $outPathConditionStr = 'ezcontentobject_tree.parent_node_id = ' . (int) $nodeID . ' and';
01419             }
01420             else
01421             {
01422                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
01423                 if ( !is_array( $node ) )
01424                     return false;
01425 
01426                 $nodePath   = $node['path_string'];
01427                 $nodeDepth  = $node['depth'];
01428 
01429                 $outNotEqParentStr   = eZContentObjectTreeNode::createNotEqParentSQLString( $nodeID, $depth, $depthOperator );
01430                 $outPathConditionStr = eZContentObjectTreeNode::createPathConditionSQLString( $nodePath, $nodeDepth, $depth, $depthOperator );
01431             }
01432         }
01433 
01434         return true;
01435     }
01436 
01437     /*!
01438         \a static
01439     */
01440     static function createGroupBySQLStrings( &$outGroupBySelectText, &$outGroupByText, $groupBy )
01441     {
01442         if ( $groupBy )
01443         {
01444             if ( isset( $groupBy['field'] ) and isset( $groupBy['type'] ) )
01445             {
01446                 $groupByField       = $groupBy['field'];
01447                 $groupByFieldType   = $groupBy['type'];
01448 
01449                 switch ( $groupByField )
01450                 {
01451                     case 'published':
01452                     {
01453                         $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
01454                         $groupBySelect['field'] = "ezcontentobject." . $groupByField;
01455                     } break;
01456                     case 'modified':
01457                     {
01458                         $groupBySelect = eZContentObjectTreeNode::subTreeGroupByDateField( "ezcontentobject." . $groupByField, $groupByFieldType );
01459                         $groupBySelect['field'] = "ezcontentobject." . $groupByField;
01460                     } break;
01461                 }
01462 
01463                 $outGroupBySelectText = ", " . $groupBySelect['select'];
01464                 $outGroupByText = "GROUP BY " . $groupBySelect['group_field'];
01465             }
01466         }
01467     }
01468 
01469     /*!
01470         \a static
01471     */
01472     static function createVersionNameTablesSQLString( $useVersionName )
01473     {
01474         $versionNameTables = '';
01475 
01476         if ( $useVersionName )
01477         {
01478             $versionNameTables = ', ezcontentobject_name ';
01479         }
01480 
01481         return $versionNameTables;
01482     }
01483 
01484     /*!
01485         \a static
01486     */
01487     static function createVersionNameTargetsSQLString( $useVersionName )
01488     {
01489         $versionNameTargets = '';
01490 
01491         if ( $useVersionName )
01492         {
01493             $versionNameTargets = ', ezcontentobject_name.name as name,  ezcontentobject_name.real_translation ';
01494         }
01495 
01496 
01497         return $versionNameTargets;
01498     }
01499 
01500     /*!
01501         \a static
01502     */
01503     static function createVersionNameJoinsSQLString( $useVersionName, $includeAnd = true, $onlyTranslated = false, $lang = false, $treeTableName = 'ezcontentobject_tree' )
01504     {
01505         $versionNameJoins = '';
01506         if ( $useVersionName )
01507         {
01508             if ( $includeAnd )
01509             {
01510                 $versionNameJoins .= ' AND ';
01511             }
01512             $versionNameJoins .= " $treeTableName.contentobject_id = ezcontentobject_name.contentobject_id and
01513                                    $treeTableName.contentobject_version = ezcontentobject_name.content_version and ";
01514             $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
01515         }
01516         return $versionNameJoins;
01517     }
01518 
01519     /*!
01520         \a static
01521         Deprecated. Use 'createPermissionCheckingSQL' instead.
01522     */
01523     static function createPermissionCheckingSQLString( $limitationList )
01524     {
01525         $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
01526         return $sqlPermissionChecking['where'];
01527     }
01528 
01529     /*!
01530         \a static
01531     */
01532     static function createPermissionCheckingSQL( $limitationList, $treeTableName = 'ezcontentobject_tree', $tableAliasName = 'ezcontentobject_tree' )
01533     {
01534         $db = eZDB::instance();
01535 
01536         $sqlPermissionCheckingFrom = '';
01537         $sqlPermissionCheckingWhere = '';
01538         $sqlPermissionTempTables = array();
01539         $groupPermTempTable = false;
01540         $createdStateAliases = array();
01541 
01542         if ( is_array( $limitationList ) && count( $limitationList ) > 0 )
01543         {
01544             $sqlParts = array();
01545             foreach( $limitationList as $limitationArray )
01546             {
01547                 $sqlPartPart = array();
01548                 $sqlPartPartPart = array();
01549                 $sqlPlacementPart = array();
01550 
01551                 foreach ( array_keys( $limitationArray ) as $ident )
01552                 {
01553                     switch( $ident )
01554                     {
01555                         case 'Class':
01556                         {
01557                             $sqlPartPart[] = 'ezcontentobject.contentclass_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
01558                         } break;
01559 
01560                         case 'Section':
01561                         case 'User_Section':
01562                         {
01563                             $sqlPartPart[] = 'ezcontentobject.section_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
01564                         } break;
01565 
01566                         case 'Owner':
01567                         {
01568                             $user = eZUser::currentUser();
01569                             $userID = $user->attribute( 'contentobject_id' );
01570                             $sqlPartPart[] = "ezcontentobject.owner_id = '" . $db->escapeString( $userID ) . "'";
01571                         } break;
01572 
01573                         case 'Group':
01574                         {
01575                             if ( !$groupPermTempTable )
01576                             {
01577                                 $user = eZUser::currentUser();
01578                                 $userContentObject = $user->attribute( 'contentobject' );
01579                                 $parentList = $userContentObject->attribute( 'parent_nodes' );
01580 
01581                                 $groupPermTempTable = $db->generateUniqueTempTableName( 'ezgroup_perm_tmp_%' );
01582                                 $sqlPermissionTempTables[] = $groupPermTempTable;
01583 
01584                                 $db->createTempTable( "CREATE TEMPORARY TABLE $groupPermTempTable ( user_id int NOT NULL PRIMARY KEY )" );
01585                                 $db->query( "INSERT INTO $groupPermTempTable
01586                                                     SELECT DISTINCT contentobject_id AS user_id
01587                                                     FROM     ezcontentobject_tree
01588                                                     WHERE    parent_node_id IN ("  . implode( ', ', $parentList ) . ')',
01589                                             eZDBInterface::SERVER_SLAVE );
01590 
01591                                 $sqlPermissionCheckingFrom .= ', ' . $groupPermTempTable;
01592                             }
01593                             $sqlPartPart[] = "ezcontentobject.owner_id = $groupPermTempTable.user_id";
01594                         } break;
01595 
01596                         case 'Node':
01597                         {
01598                             $sqlPlacementPart[] = $tableAliasName . '.node_id in (' . implode( ', ', $limitationArray[$ident] ) . ')';
01599                         } break;
01600 
01601                         case 'Subtree':
01602                         {
01603                             $sqlSubtreePart = array();
01604                             foreach ( $limitationArray[$ident] as $limitationPathString )
01605                             {
01606                                 $sqlSubtreePart[] = "$tableAliasName.path_string like '$limitationPathString%'";
01607                             }
01608                             $sqlPlacementPart[] = implode( ' OR ', $sqlSubtreePart );
01609                         } break;
01610 
01611                         case 'User_Subtree':
01612                         {
01613                             $sqlPartUserSubtree = array();
01614                             foreach ( $limitationArray[$ident] as $limitationPathString )
01615                             {
01616                                 $sqlPartUserSubtree[] = "$tableAliasName.path_string like '$limitationPathString%'";
01617                             }
01618                             $sqlPartPart[] = implode( ' OR ', $sqlPartUserSubtree );
01619                         } break;
01620 
01621                         default:
01622                         {
01623                             if ( strncmp( $ident, 'StateGroup_', 11 ) === 0 )
01624                             {
01625                                 $stateIdentifier = substr( $ident, 11 );
01626 
01627                                 if ( !isset( $createdStateAliases[$stateIdentifier] ) )
01628                                 {
01629                                     $stateIndex = count( $createdStateAliases );
01630                                 }
01631                                 else
01632                                 {
01633                                     $stateIndex = $createdStateAliases[$stateIdentifier];
01634                                 }
01635                                 $stateTable = "ezcobj_state_{$stateIndex}_perm";
01636 
01637                                 if ( !isset( $createdStateAliases[$stateIdentifier] ) )
01638                                 {
01639                                     $createdStateAliases[$stateIdentifier] = $stateIndex;
01640                                     $stateLinkTable =  "ezcobj_state_lnk_{$stateIndex}_perm";
01641                                     $stateGroupTable = "ezcobj_state_grp_{$stateIndex}_perm";
01642                                     $stateAliasTables[$stateIdentifier] = $stateTable;
01643 
01644                                     $sqlPermissionCheckingFrom .= ", ezcobj_state_link $stateLinkTable ";
01645                                     $sqlPermissionCheckingFrom .= ", ezcobj_state_group $stateGroupTable ";
01646                                     $sqlPermissionCheckingFrom .= ", ezcobj_state $stateTable ";
01647 
01648                                     $sqlPermissionCheckingWhere .= "AND $stateLinkTable.contentobject_id = ezcontentobject.id " .
01649                                                                    "AND $stateTable.id = $stateLinkTable.contentobject_state_id " .
01650                                                                    "AND $stateTable.group_id = $stateGroupTable.id " .
01651                                                                    "AND $stateGroupTable.identifier='" . $db->escapeString( $stateIdentifier ) . "' ";
01652                                 }
01653 
01654                                 if ( count( $limitationArray[$ident] ) > 1 )
01655                                 {
01656                                     $sqlPartPart[] = $db->generateSQLINStatement( $limitationArray[$ident], "$stateTable.id" );
01657                                 }
01658                                 else
01659                                 {
01660                                     $sqlPartPart[] = "$stateTable.id = " . $limitationArray[$ident][0];
01661                                 }
01662                             }
01663                         }
01664                     }
01665                 }
01666                 if ( $sqlPlacementPart )
01667                 {
01668                     $sqlPartPart[] = '( ( ' . implode( ' ) OR ( ', $sqlPlacementPart ) . ' ) )';
01669                 }
01670                 if ( $sqlPartPartPart )
01671                 {
01672                     $sqlPartPart[] = '( ' . implode( ' ) OR ( ', $sqlPartPartPart ) . ' )';
01673                 }
01674                 $sqlParts[] = implode( ' AND ', $sqlPartPart );
01675             }
01676             $sqlPermissionCheckingWhere .= ' AND ((' . implode( ") OR (", $sqlParts ) . ')) ';
01677         }
01678 
01679         $sqlPermissionChecking = array( 'from' => $sqlPermissionCheckingFrom,
01680                                         'where' => $sqlPermissionCheckingWhere,
01681                                         'temp_tables' => $sqlPermissionTempTables );
01682 
01683         return $sqlPermissionChecking;
01684     }
01685 
01686 
01687     /*!
01688         \a static
01689 
01690         \param $limit maximum number of nodes in the path to use, starting from last node
01691     */
01692     static function createNodesConditionSQLStringFromPath( $nodePath, $includingLastNodeInThePath, $limit = false )
01693     {
01694         $pathString = false;
01695         $pathArray  = explode( '/', trim( $nodePath, '/' ) );
01696 
01697         $pathArrayCount = count( $pathArray );
01698 
01699         if ( $limit && $includingLastNodeInThePath == false )
01700         {
01701             $limit++;
01702         }
01703 
01704         $sliceOffset = $limit && $pathArrayCount > $limit ? $pathArrayCount - $limit : 0;
01705         $sliceLength = $includingLastNodeInThePath ? $pathArrayCount - $sliceOffset : $pathArrayCount - ( $sliceOffset + 1 );
01706 
01707         // only take a slice when necessary
01708         if ( ( $sliceOffset + $sliceLength ) < $pathArrayCount )
01709         {
01710             $pathArray = array_slice( $pathArray, $sliceOffset, $sliceLength );
01711         }
01712 
01713         if ( $sliceLength == 1 )
01714         {
01715             $pathString = ' node_id = ' . implode( '', $pathArray ) . ' and ';
01716         }
01717         else if ( $sliceLength > 0 )
01718         {
01719             $db = eZDB::instance();
01720             $pathString = ' ' . $db->generateSQLINStatement( $pathArray, 'node_id' ) . ' and ';
01721         }
01722 
01723         return $pathString;
01724     }
01725 
01726     /*!
01727         \a static
01728         If \a $useSettings is true \a $fetchHidden will be ignored.
01729         If \a $useSettings is false \a $fetchHidden will be used.
01730     */
01731     static function createShowInvisibleSQLString( $useSettings, $fetchHidden = true )
01732     {
01733         $showInvisibleNodesCond = '';
01734         $showInvisible          = $fetchHidden;
01735 
01736         if ( $useSettings )
01737             $showInvisible = eZContentObjectTreeNode::showInvisibleNodes();
01738 
01739         if ( !$showInvisible )
01740             $showInvisibleNodesCond = 'AND ezcontentobject_tree.is_invisible = 0';
01741 
01742         return $showInvisibleNodesCond;
01743     }
01744 
01745     /*!
01746      \a static
01747      \returns true if we should show invisible nodes (determined by ini setting), false otherwise.
01748     */
01749     static function showInvisibleNodes()
01750     {
01751         static $cachedResult;
01752 
01753         if ( !isset( $cachedResult ) )
01754         {
01755             $ini = eZINI::instance( 'site.ini' );
01756             $cachedResult = $ini->hasVariable( 'SiteAccessSettings', 'ShowHiddenNodes' ) ?
01757                             $ini->variable( 'SiteAccessSettings', 'ShowHiddenNodes' ) == 'true' :
01758                             true;
01759         }
01760 
01761         return $cachedResult;
01762     }
01763 
01764     /*!
01765         \a static
01766     */
01767     static function getLimitationList( &$limitation )
01768     {
01769         $currentUser = eZUser::currentUser();
01770         $currentUserID = $currentUser->attribute( 'contentobject_id' );
01771         $limitationList = array();
01772 
01773         if ( $limitation !== false )
01774         {
01775             $limitationList = $limitation;
01776         }
01777         else if ( isset( $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] ) )
01778         {
01779             $limitationList =& $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'];
01780             eZDebugSetting::writeDebug( 'kernel-content-treenode', $limitationList, "limitation list"  );
01781         }
01782         else
01783         {
01784             $accessResult = $currentUser->hasAccessTo( 'content', 'read' );
01785 
01786             if ( $accessResult['accessWord'] == 'no' )
01787             {
01788                 $limitationList = false;
01789                 $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = false;
01790             }
01791             else if ( $accessResult['accessWord'] == 'limited' )
01792             {
01793                 $limitationList = $accessResult['policies'];
01794                 $GLOBALS['ezpolicylimitation_list'][$currentUserID]['content']['read'] = $accessResult['policies'];
01795             }
01796         }
01797 
01798         return $limitationList;
01799     }
01800 
01801     /*!
01802      \sa subTree
01803     */
01804     static function subTreeByNodeID( $params = false, $nodeID = 0 )
01805     {
01806         if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
01807         {
01808             return null;
01809         }
01810 
01811         if ( $params === false )
01812         {
01813             $params = array( 'Depth'                    => false,
01814                              'Offset'                   => false,
01815                              //'OnlyTranslated'           => false,
01816                              'Language'                 => false,
01817                              'Limit'                    => false,
01818                              'SortBy'                   => false,
01819                              'AttributeFilter'          => false,
01820                              'ExtendedAttributeFilter'  => false,
01821                              'ClassFilterType'          => false,
01822                              'ClassFilterArray'         => false,
01823                              'GroupBy'                  => false );
01824         }
01825 
01826         $offset           = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset']             : false;
01827         //$onlyTranslated   = ( isset( $params['OnlyTranslated']      ) )                       ? $params['OnlyTranslated']     : false;
01828         $language         = ( isset( $params['Language']      ) )                             ? $params['Language']           : false;
01829         $limit            = ( isset( $params['Limit']  ) && is_numeric( $params['Limit']  ) ) ? $params['Limit']              : false;
01830         $depth            = ( isset( $params['Depth']  ) && is_numeric( $params['Depth']  ) ) ? $params['Depth']              : false;
01831         $depthOperator    = ( isset( $params['DepthOperator']     ) )                         ? $params['DepthOperator']      : false;
01832         $asObject         = ( isset( $params['AsObject']          ) )                         ? $params['AsObject']           : true;
01833         $loadDataMap      = ( isset( $params['LoadDataMap'] ) )                               ? $params['LoadDataMap']        : false;
01834         $groupBy          = ( isset( $params['GroupBy']           ) )                         ? $params['GroupBy']            : false;
01835         $mainNodeOnly     = ( isset( $params['MainNodeOnly']      ) )                         ? $params['MainNodeOnly']       : false;
01836         $ignoreVisibility = ( isset( $params['IgnoreVisibility']  ) )                         ? $params['IgnoreVisibility']   : false;
01837         $objectNameFilter = ( isset( $params['ObjectNameFilter']  ) )                         ? $params['ObjectNameFilter']   : false;
01838 
01839         if ( $offset < 0 )
01840         {
01841             $offset = abs( $offset );
01842         }
01843 
01844         if ( !isset( $params['SortBy'] ) )
01845             $params['SortBy'] = false;
01846         if ( !isset( $params['ClassFilterType'] ) )
01847             $params['ClassFilterType'] = false;
01848 
01849         $allowCustomSorting = false;
01850         if ( isset( $params['ExtendedAttributeFilter'] ) && is_array ( $params['ExtendedAttributeFilter'] ) )
01851         {
01852             $allowCustomSorting = true;
01853         }
01854 
01855         $sortingInfo             = eZContentObjectTreeNode::createSortingSQLStrings( $params['SortBy'], 'ezcontentobject_tree', $allowCustomSorting );
01856         $classCondition          = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] );
01857         if ( $classCondition === false )
01858         {
01859             eZDebug::writeNotice( "Class filter returned false" );
01860             return null;
01861         }
01862 
01863         $attributeFilter         = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo );
01864         if ( $attributeFilter === false )
01865         {
01866             return null;
01867         }
01868         $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
01869         $mainNodeOnlyCond        = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
01870 
01871         $pathStringCond     = '';
01872         $notEqParentString  = '';
01873         // If the node(s) doesn't exist we return null.
01874         if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
01875         {
01876             return null;
01877         }
01878 
01879         if ( $language )
01880         {
01881             if ( !is_array( $language ) )
01882             {
01883                 $language = array( $language );
01884             }
01885             // This call must occur after eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings,
01886             // because the parent node may not exist in Language
01887             eZContentLanguage::setPrioritizedLanguages( $language );
01888         }
01889 
01890         $groupBySelectText  = '';
01891         $groupBySQL         = $extendedAttributeFilter['group_by'];
01892         if ( !$groupBySQL )
01893         {
01894             eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
01895         }
01896         else if ( $groupBy )
01897         {
01898             eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
01899         }
01900 
01901 
01902         $useVersionName     = true;
01903 
01904         $versionNameTables  = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
01905         $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
01906         $versionNameJoins   = eZContentObjectTreeNode::createVersionNameJoinsSQLString  ( $useVersionName, false );
01907 
01908         $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
01909 
01910         if ( $language )
01911         {
01912             eZContentLanguage::clearPrioritizedLanguages();
01913         }
01914         $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
01915 
01916         $limitation = ( isset( $params['Limitation']  ) && is_array( $params['Limitation']  ) ) ? $params['Limitation']: false;
01917         $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
01918         $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
01919 
01920         // Determine whether we should show invisible nodes.
01921         $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
01922 
01923         $query = "SELECT DISTINCT
01924                        ezcontentobject.*,
01925                        ezcontentobject_tree.*,
01926                        ezcontentclass.serialized_name_list as class_serialized_name_list,
01927                        ezcontentclass.identifier as class_identifier,
01928                        ezcontentclass.is_container as is_container
01929                        $groupBySelectText
01930                        $versionNameTargets
01931                        $sortingInfo[attributeTargetSQL]
01932                        $extendedAttributeFilter[columns]
01933                    FROM
01934                       ezcontentobject_tree,
01935                       ezcontentobject,ezcontentclass
01936                       $versionNameTables
01937                       $sortingInfo[attributeFromSQL]
01938                       $attributeFilter[from]
01939                       $extendedAttributeFilter[tables]
01940                       $sqlPermissionChecking[from]
01941                    WHERE
01942                       $pathStringCond
01943                       $extendedAttributeFilter[joins]
01944                       $sortingInfo[attributeWhereSQL]
01945                       $attributeFilter[where]
01946                       ezcontentclass.version=0 AND
01947                       $notEqParentString
01948                       ezcontentobject_tree.contentobject_id = ezcontentobject.id  AND
01949                       ezcontentclass.id = ezcontentobject.contentclass_id AND
01950                       $mainNodeOnlyCond
01951                       $classCondition
01952                       $versionNameJoins
01953                       $showInvisibleNodesCond
01954                       $sqlPermissionChecking[where]
01955                       $objectNameFilterSQL
01956                       $languageFilter
01957                 $groupBySQL";
01958 
01959         if ( $sortingInfo['sortingFields'] )
01960             $query .= " ORDER BY $sortingInfo[sortingFields]";
01961 
01962         $db = eZDB::instance();
01963 
01964         $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
01965 
01966         $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
01967                                                          'limit'  => $limit ),
01968                                                   $server );
01969 
01970         if ( $asObject )
01971         {
01972             $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
01973             if ( $loadDataMap === true )
01974                 eZContentObject::fillNodeListAttributes( $retNodeList );
01975             else if ( $loadDataMap && is_numeric( $loadDataMap ) && $loadDataMap >= count( $retNodeList ) )
01976                 eZContentObject::fillNodeListAttributes( $retNodeList );
01977         }
01978         else
01979         {
01980             $retNodeList = $nodeListArray;
01981         }
01982 
01983         // cleanup temp tables
01984         $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
01985 
01986         return $retNodeList;
01987     }
01988 
01989     function subTree( $params = false )
01990     {
01991         return eZContentObjectTreeNode::subTreeByNodeID( $params, $this->attribute( 'node_id' ) );
01992     }
01993 
01994     /*!
01995     Retrieve subtrees from multiple paths.
01996 
01997     This method composes a list of objects retrieved from various node paths,
01998     sorted by criteria that are globally applied to the whole list.
01999 
02000     It is for example useful for an RSS feed that serves content from
02001     several node paths. The respective subtrees need to be amalgated and
02002     the resulting object listed sorted by publishing date to show the latest
02003     entries in chronological order.
02004 
02005     The first parameter is a multi-dimensional array containing the
02006     node IDs and filter criteria assigned to each of the nodes:
02007 
02008     array(
02009         [node_1] => array(
02010                         'ClassFilterType' =>    [filter_type],
02011                         'ClassFilterArray'  =>  [filter_array]
02012                         ),
02013          [node_2] => array(
02014                         'ClassFilterType' =>    [filter_type],
02015                         'ClassFilterArray'  =>  [filter_array]
02016                         )
02017          )
02018 
02019     The second parameter is a single-dimensional array with criteria
02020     applied to the list of objects retrieved from the various subtrees:
02021 
02022     array(
02023         'SortBy' => [sorting-criteria]
02024         )
02025     */
02026     static function subTreeMultiPaths( $nodesParams, $listParams = NULL )
02027     {
02028         if( !is_array( $nodesParams ) || !count( $nodesParams ) )
02029         {
02030             eZDebug::writeWarning( __METHOD__.': Nodes parameter must be an array with at least one key.' );
02031             return null;
02032         }
02033 
02034         if( $listParams === null )
02035         {
02036             $listParams = array(
02037                              'SortBy'                   => false,
02038                              'Offset'                   => false,
02039                              'Limit'                    => false,
02040                              'SortBy'                   => false,
02041                              'GroupBy'                  => false );
02042         }
02043 
02044         $offset           = ( isset( $listParams['Offset'] ) && is_numeric( $listParams['Offset'] ) ) ? $listParams['Offset']             : false;
02045         $limit            = ( isset( $listParams['Limit']  ) && is_numeric( $listParams['Limit']  ) ) ? $listParams['Limit']              : false;
02046         $groupBy          = ( isset( $listParams['GroupBy']                                       ) ) ? $listParams['GroupBy']            : false;
02047         if ( !isset( $listParams['SortBy'] ) )
02048         {
02049             $listParams['SortBy'] = false;
02050         }
02051         $sortBy = $listParams['SortBy'];
02052 
02053         $queryNodes = '';
02054 
02055         foreach( $nodesParams as $nodeParams )
02056         {
02057             $nodeID = $nodeParams['ParentNodeID'];
02058 
02059             if ( !is_numeric( $nodeID ) && !is_array( $nodeID ) )
02060             {
02061                 eZDebug::writeWarning( __METHOD__.': Nodes parameter must be numeric or an array with numeric values.' );
02062                 $retValue = null;
02063                 return $retValue;
02064             }
02065 
02066             if ( $nodeParams === null )
02067             {
02068                 $nodeParams = array(
02069                                  'Depth'                    => false,
02070                                  //'OnlyTranslated'           => false,
02071                                  'Language'                 => false,
02072                                  'AttributeFilter'          => false,
02073                                  'ExtendedAttributeFilter'  => false,
02074                                  'ClassFilterType'          => false,
02075                                  'ClassFilterArray'         => false );
02076             }
02077 
02078             //$onlyTranslated   = ( isset( $nodeParams['OnlyTranslated']    ) )                       ? $nodeParams['OnlyTranslated']     : false;
02079             $language         = ( isset( $nodeParams['Language']          ) )                             ? $nodeParams['Language']           : false;
02080             $depth            = ( isset( $nodeParams['Depth']  ) && is_numeric( $nodeParams['Depth']  ) ) ? $nodeParams['Depth']              : false;
02081             $depthOperator    = ( isset( $nodeParams['DepthOperator']     ) )                         ? $nodeParams['DepthOperator']      : false;
02082             $asObject         = ( isset( $nodeParams['AsObject']          ) )                         ? $nodeParams['AsObject']           : true;
02083             $mainNodeOnly     = ( isset( $nodeParams['MainNodeOnly']      ) )                         ? $nodeParams['MainNodeOnly']       : false;
02084             $ignoreVisibility = ( isset( $nodeParams['IgnoreVisibility']  ) )                         ? $nodeParams['IgnoreVisibility']   : false;
02085             if ( !isset( $nodeParams['ClassFilterType'] ) )
02086             {
02087                 $nodeParams['ClassFilterType'] = false;
02088             }
02089 
02090             if ( $language )
02091             {
02092                 if ( !is_array( $language ) )
02093                 {
02094                     $language = array( $language );
02095                 }
02096                 eZContentLanguage::setPrioritizedLanguages( $language );
02097             }
02098 
02099             $sortingInfo             = eZContentObjectTreeNode::createSortingSQLStrings( $sortBy );
02100             $classCondition          = eZContentObjectTreeNode::createClassFilteringSQLString( $nodeParams['ClassFilterType'], $nodeParams['ClassFilterArray'] );
02101             $attributeFilter         = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $nodeParams['AttributeFilter'], $sortingInfo );
02102             $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $nodeParams['ExtendedAttributeFilter'] );
02103             $mainNodeOnlyCond        = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
02104 
02105             $pathStringCond     = '';
02106             $notEqParentString  = '';
02107             // If the node(s) doesn't exist we return null.
02108 
02109             if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
02110             {
02111                 $retValue = null;
02112                 return $retValue;
02113             }
02114 
02115             $useVersionName     = true;
02116             $versionNameTables  = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
02117             $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
02118             $versionNameJoins   = eZContentObjectTreeNode::createVersionNameJoinsSQLString  ( $useVersionName, false );
02119 
02120             $languageFilter = ' AND ' . eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
02121 
02122             if ( $language )
02123             {
02124                 eZContentLanguage::clearPrioritizedLanguages();
02125             }
02126 
02127             $limitation = ( isset( $nodeParams['Limitation']  ) && is_array( $nodeParams['Limitation']  ) ) ? $nodeParams['Limitation']: false;
02128             $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
02129             $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
02130 
02131             // Determine whether we should show invisible nodes.
02132             $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
02133 
02134             $queryNodes .= " (
02135                           $pathStringCond
02136                           $extendedAttributeFilter[joins]
02137                           $sortingInfo[attributeWhereSQL]
02138                           $attributeFilter[where]
02139                           ezcontentclass.version=0 AND
02140                           $notEqParentString
02141                           ezcontentobject_tree.contentobject_id = ezcontentobject.id  AND
02142                           ezcontentclass.id = ezcontentobject.contentclass_id AND
02143                           $mainNodeOnlyCond
02144                           $classCondition
02145                           $versionNameJoins
02146                           $showInvisibleNodesCond
02147                           $sqlPermissionChecking[where]
02148                           $languageFilter
02149                       )
02150                       OR";
02151         }
02152 
02153         $groupBySelectText  = '';
02154         $groupBySQL         = $extendedAttributeFilter['group_by'];
02155         if ( !$groupBySQL )
02156         {
02157             eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
02158         }
02159         else if ( $groupBy )
02160         {
02161             eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
02162         }
02163 
02164         $query = "SELECT DISTINCT
02165                        ezcontentobject.*,
02166                        ezcontentobject_tree.*,
02167                        ezcontentclass.serialized_name_list as class_serialized_name_list,
02168                        ezcontentclass.identifier as class_identifier,
02169                        ezcontentclass.is_container as is_container
02170                        $groupBySelectText
02171                        $versionNameTargets
02172                        $sortingInfo[attributeTargetSQL]
02173                        , ".$nodeParams['ResultID']." AS resultid
02174                    FROM
02175                       ezcontentobject_tree,
02176                       ezcontentobject,ezcontentclass
02177                       $versionNameTables
02178                       $sortingInfo[attributeFromSQL]
02179                       $attributeFilter[from]
02180                       $extendedAttributeFilter[tables]
02181                       $sqlPermissionChecking[from]
02182                    WHERE
02183                       ".substr($queryNodes, 0, -2)."
02184                 $groupBySQL";
02185 
02186         if ( $sortingInfo['sortingFields'] )
02187         {
02188             $query .= " ORDER BY $sortingInfo[sortingFields]";
02189         }
02190 
02191         $db = eZDB::instance();
02192 
02193         $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
02194 
02195         if ( !$offset && !$limit )
02196         {
02197             $nodeListArray = $db->arrayQuery( $query, array(), $server );
02198         }
02199         else
02200         {
02201             $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
02202                                                               'limit'  => $limit ),
02203                                                       $server );
02204         }
02205 
02206         if ( $asObject )
02207         {
02208             $retNodeList = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
02209         }
02210         else
02211         {
02212             $retNodeList = $nodeListArray;
02213         }
02214 
02215         // cleanup temp tables
02216         $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
02217 
02218         return $retNodeList;
02219     }
02220 
02221     static function subTreeGroupByDateField( $field, $type )
02222     {
02223         $divisor = 0;
02224         switch ( $type )
02225         {
02226             case 'year':
02227             {
02228                 $divisor = 60*60*24*365;
02229             } break;
02230             case 'week':
02231             {
02232                 $divisor = 60*60*24*7;
02233             } break;
02234             case 'day':
02235             {
02236                 $divisor = 60*60*24;
02237             } break;
02238             case 'hour':
02239             {
02240                 $divisor = 60*60;
02241             } break;
02242             case 'minute':
02243             {
02244                 $divisor = 60;
02245             } break;
02246             case 'second':
02247             {
02248                 $divisor = 0;
02249             } break;
02250             default:
02251             {
02252                 eZDebug::writeError( "Unknown field type $type", __METHOD__ );
02253             }
02254         }
02255         if ( $divisor > 0 )
02256             $text = "( $field / $divisor ) AS groupbyfield";
02257         else
02258             $text = "$field AS groupbyfield";
02259         return array( 'select' => $text,
02260                       'group_field' => "( $field / $divisor )" );
02261     }
02262 
02263 
02264     /*!
02265      \sa subTreeCount
02266     */
02267     static function subTreeCountByNodeID( $params = array(), $nodeID )
02268     {
02269         if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
02270         {
02271             return null;
02272         }
02273 
02274         $language = ( isset( $params['Language'] ) ) ? $params['Language'] : false;
02275 
02276         if ( $language )
02277         {
02278             if ( !is_array( $language ) )
02279             {
02280                 $language = array( $language );
02281             }
02282             eZContentLanguage::setPrioritizedLanguages( $language );
02283         }
02284 
02285         $depth         = isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) ? $params['Depth']              : false;
02286         $depthOperator = isset( $params['DepthOperator'] )                           ? $params['DepthOperator']      : false;
02287 
02288         $pathStringCond     = '';
02289         $notEqParentString  = '';
02290         // If the node(s) doesn't exist we return null.
02291         if ( !eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator ) )
02292         {
02293             return null;
02294         }
02295 
02296         $db = eZDB::instance();
02297         $ini = eZINI::instance();
02298 
02299         // Check for class filtering
02300         $classCondition = '';
02301 
02302         if ( isset( $params['ClassFilterType'] ) and isset( $params['ClassFilterArray'] ) and
02303              ( $params['ClassFilterType'] == 'include' or $params['ClassFilterType'] == 'exclude' )
02304              and count( $params['ClassFilterArray'] ) > 0 )
02305         {
02306             $classCondition = '  ';
02307             $i = 0;
02308             $classCount = count( $params['ClassFilterArray'] );
02309             $classIDArray = array();
02310             foreach ( $params['ClassFilterArray'] as $classID )
02311             {
02312                 $originalClassID = $classID;
02313                 // Check if classes are recerenced by identifier
02314                 if ( is_string( $classID ) && !is_numeric( $classID ) )
02315                 {
02316                     $classID = eZContentClass::classIDByIdentifier( $classID );
02317                 }
02318                 if ( is_numeric( $classID ) )
02319                 {
02320                     $classIDArray[] = $classID;
02321                 }
02322                 else
02323                 {
02324                     eZDebugSetting::writeWarning( 'kernel-content-class', "Invalid class identifier in subTree() classfilterarray, classID : " . $originalClassID );
02325                 }
02326             }
02327             if ( count( $classIDArray ) > 0  )
02328             {
02329                 $classCondition .= " ezcontentobject.contentclass_id ";
02330                 if ( $params['ClassFilterType'] == 'include' )
02331                     $classCondition .= " IN ";
02332                 else
02333                     $classCondition .= " NOT IN ";
02334 
02335                 $classIDString =  implode( ', ', $classIDArray );
02336                 $classCondition .= ' ( ' . $classIDString . ' ) AND';
02337             }
02338         }
02339 
02340 
02341         // Main node check
02342         $mainNodeOnlyCond = '';
02343         if ( isset( $params['MainNodeOnly'] ) && $params['MainNodeOnly'] === true )
02344         {
02345             $mainNodeOnlyCond = 'ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND';
02346         }
02347 
02348         $attributeFilterParam = isset( $params['AttributeFilter'] ) ? $params['AttributeFilter'] : false;
02349         $attributeFilter = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $attributeFilterParam );
02350         if ( $attributeFilter === false )
02351         {
02352             return null;
02353         }
02354 
02355         //$onlyTranslated   = ( isset( $params['OnlyTranslated'] ) ) ? $params['OnlyTranslated']     : false;
02356 
02357         $useVersionName     = true;
02358 
02359         $versionNameTables  = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
02360         $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
02361         $versionNameJoins   = eZContentObjectTreeNode::createVersionNameJoinsSQLString  ( $useVersionName, false );
02362 
02363         $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
02364 
02365         if ( $language )
02366         {
02367             eZContentLanguage::clearPrioritizedLanguages();
02368         }
02369         $objectNameFilter = ( isset( $params['ObjectNameFilter']  ) ) ? $params['ObjectNameFilter']   : false;
02370         $objectNameFilterSQL = eZContentObjectTreeNode::createObjectNameFilterConditionSQLString( $objectNameFilter );
02371 
02372         $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
02373 
02374         // Determine whether we should show invisible nodes.
02375         $ignoreVisibility = isset( $params['IgnoreVisibility'] ) ? $params['IgnoreVisibility'] : false;
02376         $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
02377 
02378         $limitation = ( isset( $params['Limitation']  ) && is_array( $params['Limitation']  ) ) ? $params['Limitation']: false;
02379         $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
02380         $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
02381 
02382         $query = "SELECT
02383                         count( DISTINCT ezcontentobject_tree.node_id ) as count
02384                   FROM
02385                        ezcontentobject_tree,
02386                        ezcontentobject,ezcontentclass
02387                        $versionNameTables
02388                        $attributeFilter[from]
02389                        $extendedAttributeFilter[tables]
02390                        $sqlPermissionChecking[from]
02391                   WHERE $pathStringCond
02392                         $extendedAttributeFilter[joins]
02393                         $mainNodeOnlyCond
02394                         $classCondition
02395                         $attributeFilter[where]
02396                         ezcontentclass.version=0 AND
02397                         $notEqParentString
02398                         ezcontentobject_tree.contentobject_id = ezcontentobject.id  AND
02399                         ezcontentclass.id = ezcontentobject.contentclass_id AND
02400                         $versionNameJoins
02401                         $showInvisibleNodesCond
02402                         $sqlPermissionChecking[where]
02403                         $objectNameFilterSQL
02404                         $languageFilter ";
02405 
02406         $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
02407 
02408         $nodeListArray = $db->arrayQuery( $query, array(), $server );
02409 
02410         // cleanup temp tables
02411         $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
02412 
02413         return $nodeListArray[0]['count'];
02414     }
02415 
02416     /*!
02417      Count number of subnodes
02418 
02419      \param params array
02420     */
02421     function subTreeCount( $params = array() )
02422     {
02423         return eZContentObjectTreeNode::subTreeCountByNodeID( $params, $this->attribute( 'node_id' ) );
02424     }
02425 
02426     /*!
02427       \return The date/time list when object were published
02428     */
02429     static function calendar( $params = false, $nodeID = 0 )
02430     {
02431         if ( !is_numeric( $nodeID ) and !is_array( $nodeID ) )
02432         {
02433             return array();
02434         }
02435 
02436         if ( $params === false )
02437         {
02438             $params = array( 'Depth'                    => false,
02439                              'Offset'                   => false,
02440                              'Limit'                    => false,
02441                              'AttributeFilter'          => false,
02442                              'ExtendedAttributeFilter'  => false,
02443                              'ClassFilterType'          => false,
02444                              'ClassFilterArray'         => false,
02445                              'GroupBy'                  => false );
02446         }
02447 
02448         $offset           = ( isset( $params['Offset'] ) && is_numeric( $params['Offset'] ) ) ? $params['Offset']             : false;
02449         $limit            = ( isset( $params['Limit']  ) && is_numeric( $params['Limit']  ) ) ? $params['Limit']              : false;
02450         $depth            = ( isset( $params['Depth']  ) && is_numeric( $params['Depth']  ) ) ? $params['Depth']              : false;
02451         $depthOperator    = ( isset( $params['DepthOperator']     ) )                         ? $params['DepthOperator']      : false;
02452         $groupBy          = ( isset( $params['GroupBy']           ) )                         ? $params['GroupBy']            : false;
02453         $mainNodeOnly     = ( isset( $params['MainNodeOnly']      ) )                         ? $params['MainNodeOnly']       : false;
02454         $ignoreVisibility = ( isset( $params['IgnoreVisibility']  ) )                         ? $params['IgnoreVisibility']   : false;
02455         if ( !isset( $params['ClassFilterType'] ) )
02456             $params['ClassFilterType'] = false;
02457 
02458         $classCondition          = eZContentObjectTreeNode::createClassFilteringSQLString( $params['ClassFilterType'], $params['ClassFilterArray'] );
02459         $attributeFilter         = eZContentObjectTreeNode::createAttributeFilterSQLStrings( $params['AttributeFilter'], $sortingInfo );
02460         $extendedAttributeFilter = eZContentObjectTreeNode::createExtendedAttributeFilterSQLStrings( $params['ExtendedAttributeFilter'] );
02461         $mainNodeOnlyCond        = eZContentObjectTreeNode::createMainNodeConditionSQLString( $mainNodeOnly );
02462 
02463         $pathStringCond     = '';
02464         $notEqParentString  = '';
02465         eZContentObjectTreeNode::createPathConditionAndNotEqParentSQLStrings( $pathStringCond, $notEqParentString, $nodeID, $depth, $depthOperator );
02466 
02467         $groupBySelectText  = '';
02468         $groupBySQL         = $extendedAttributeFilter['group_by'];
02469         if ( !$groupBySQL )
02470         {
02471             eZContentObjectTreeNode::createGroupBySQLStrings( $groupBySelectText, $groupBySQL, $groupBy );
02472         }
02473         else if ( $groupBy )
02474         {
02475             eZDebug::writeError( "Cannot use group_by parameter together with extended attribute filter which sets group_by!", __METHOD__ );
02476         }
02477 
02478         $useVersionName     = true;
02479         $versionNameTables  = eZContentObjectTreeNode::createVersionNameTablesSQLString( $useVersionName );
02480         $versionNameJoins   = eZContentObjectTreeNode::createVersionNameJoinsSQLString( $useVersionName, false );
02481 
02482         $limitation = ( isset( $params['Limitation']  ) && is_array( $params['Limitation']  ) ) ? $params['Limitation']: false;
02483         $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation );
02484         $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList );
02485 
02486         // Determine whether we should show invisible nodes.
02487         $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility );
02488 
02489         $query = "SELECT DISTINCT
02490                          ezcontentobject.published as published
02491                          $groupBySelectText
02492                    FROM
02493                       ezcontentobject_tree,
02494                       ezcontentobject,ezcontentclass
02495                       $versionNameTables
02496                       $attributeFilter[from]
02497                       $extendedAttributeFilter[tables]
02498                       $sqlPermissionChecking[from]
02499                    WHERE
02500                       $pathStringCond
02501                       $extendedAttributeFilter[joins]
02502                       $attributeFilter[where]
02503                       ezcontentclass.version=0
02504                       AND
02505                       $notEqParentString
02506                       $mainNodeOnlyCond
02507                       ezcontentobject_tree.contentobject_id = ezcontentobject.id AND
02508                       ezcontentclass.id = ezcontentobject.contentclass_id AND
02509                       $classCondition
02510                       $versionNameJoins
02511                       $showInvisibleNodesCond
02512                       $sqlPermissionChecking[where]
02513                 $groupBySQL";
02514 
02515 
02516         $db = eZDB::instance();
02517 
02518         $server = count( $sqlPermissionChecking['temp_tables'] ) > 0 ? eZDBInterface::SERVER_SLAVE : false;
02519 
02520         if ( !$offset && !$limit )
02521         {
02522             $nodeListArray = $db->arrayQuery( $query, array(), $server );
02523         }
02524         else
02525         {
02526             $nodeListArray = $db->arrayQuery( $query, array( 'offset' => $offset,
02527                                                               'limit'  => $limit ),
02528                                                       $server );
02529         }
02530 
02531         // cleanup temp tables
02532         $db->dropTempTableList( $sqlPermissionChecking['temp_tables'] );
02533 
02534         return $nodeListArray;
02535     }
02536     /*!
02537      \return the children(s) of the current node as an array of eZContentObjectTreeNode objects
02538     */
02539     function childrenByName( $name )
02540     {
02541         $nodeID = $this->attribute( 'node_id' );
02542 
02543         $fromNode = $nodeID ;
02544 
02545         $nodePath = $this->attribute( 'path_string' );
02546         $nodeDepth = $this->attribute( 'depth' );
02547 
02548         $childrensPath = $nodePath ;
02549 
02550         $db = eZDB::instance();
02551         $pathString = " path_string like '$childrensPath%' and ";
02552         $depthCond = '';
02553         $nodeDepth = $this->Depth + 1;
02554         $depthCond = ' depth <= ' . $nodeDepth . ' and ';
02555 
02556         $ini = eZINI::instance();
02557         $db = eZDB::instance();
02558 
02559         $name = $db->escapeString( $name );
02560         $query = "SELECT ezcontentobject.*,
02561                              ezcontentobject_tree.*,
02562                              ezcontentclass.serialized_name_list as class_serialized_name_list,
02563                              ezcontentclass.identifier as class_identifier,
02564                              ezcontentclass.is_container as is_container
02565                       FROM
02566                             ezcontentobject_tree,
02567                             ezcontentobject,ezcontentclass
02568                       WHERE $pathString
02569                             $depthCond
02570                             ezcontentobject.name = '$name' AND
02571                             ezcontentclass.version=0 AND
02572                             node_id != $fromNode AND
02573                             ezcontentobject_tree.contentobject_id = ezcontentobject.id  AND
02574                             ezcontentclass.id = ezcontentobject.contentclass_id";
02575 
02576         $nodeListArray = $db->arrayQuery( $query );
02577 
02578         return eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
02579     }
02580 
02581     /*!
02582      Returns the first level children in sorted order.
02583     */
02584     function children()
02585     {
02586         return $this->subTree( array( 'Depth' => 1,
02587                                       'DepthOperator' => 'eq',
02588                                       'SortBy' => $this->sortArray() ) );
02589     }
02590 
02591     /*!
02592      Returns the number of children for the current node.
02593      \params $checkPolicies If \c true it will only include nodes which can be read using the current policies,
02594                             if \c false all nodes are included in count.
02595     */
02596     function childrenCount( $checkPolicies = true )
02597     {
02598         $params = array( 'Depth' => 1,
02599                          'DepthOperator' => 'eq' );
02600         if ( !$checkPolicies )
02601             $params['Limitation'] = array();
02602         return $this->subTreeCount( $params );
02603     }
02604 
02605     /*!
02606      Get amount views of content node.
02607     */
02608     function viewCount()
02609     {
02610         $count = eZViewCounter::fetch( $this->attribute( 'node_id' ), false );
02611         return (int) $count['count'];
02612     }
02613 
02614     /*!
02615      \return the sort field name for the numeric sort field ID \a $sortFieldID.
02616              Gives a warning if the ID is unknown and returns \c 'path'.
02617     */
02618     static function sortFieldName( $sortFieldID )
02619     {
02620         switch ( $sortFieldID )
02621         {
02622             default:
02623                 eZDebug::writeWarning( 'Unknown sort field ID: ' . $sortFieldID, __METHOD__ );
02624             case self::SORT_FIELD_PATH:
02625                 return 'path';
02626             case self::SORT_FIELD_PUBLISHED:
02627                 return 'published';
02628             case self::SORT_FIELD_MODIFIED:
02629                 return 'modified';
02630             case self::SORT_FIELD_SECTION:
02631                 return 'section';
02632             case self::SORT_FIELD_DEPTH:
02633                 return 'depth';
02634             case self::SORT_FIELD_CLASS_IDENTIFIER:
02635                 return 'class_identifier';
02636             case self::SORT_FIELD_CLASS_NAME:
02637                 return 'class_name';
02638             case self::SORT_FIELD_PRIORITY:
02639                 return 'priority';
02640             case self::SORT_FIELD_NAME:
02641                 return 'name';
02642             case self::SORT_FIELD_MODIFIED_SUBNODE:
02643                 return 'modified_subnode';
02644             case self::SORT_FIELD_NODE_ID:
02645                 return 'node_id';
02646             case self::SORT_FIELD_CONTENTOBJECT_ID:
02647                 return 'contentobject_id';
02648         }
02649     }
02650 
02651     /*!
02652      \return the numeric sort field ID for the sort field name \a $sortFieldName.
02653              Gives a warning if the name is unknown and returns self::SORT_FIELD_PATH.
02654     */
02655     static function sortFieldID( $sortFieldName )
02656     {
02657         switch ( $sortFieldName )
02658         {
02659             default:
02660                 eZDebug::writeWarning( 'Unknown sort field name: ' . $sortFieldName, __METHOD__ );
02661             case 'path':
02662                 return self::SORT_FIELD_PATH;
02663             case 'published':
02664                 return self::SORT_FIELD_PUBLISHED;
02665             case 'modified':
02666                 return self::SORT_FIELD_MODIFIED;
02667             case 'section':
02668                 return self::SORT_FIELD_SECTION;
02669             case 'depth':
02670                 return self::SORT_FIELD_DEPTH;
02671             case 'class_identifier':
02672                 return self::SORT_FIELD_CLASS_IDENTIFIER;
02673             case 'class_name':
02674                 return self::SORT_FIELD_CLASS_NAME;
02675             case 'priority':
02676                 return self::SORT_FIELD_PRIORITY;
02677             case 'name':
02678                 return self::SORT_FIELD_NAME;
02679             case 'modified_subnode':
02680                 return self::SORT_FIELD_MODIFIED_SUBNODE;
02681             case 'node_id':
02682                 return self::SORT_FIELD_NODE_ID;
02683             case 'contentobject_id':
02684                 return self::SORT_FIELD_CONTENTOBJECT_ID;
02685         }
02686     }
02687 
02688     /*!
02689      \return an array which defines the sorting method for this node.
02690      The array will contain one element which is an array with sort field
02691      and sort order.
02692     */
02693     function sortArray()
02694     {
02695         return eZContentObjectTreeNode::sortArrayBySortFieldAndSortOrder( $this->attribute( 'sort_field' ),
02696                                                                           $this->attribute( 'sort_order' ) );
02697     }
02698 
02699     /*!
02700      \static
02701      \return an array which defines the sorting method for this node.
02702      The array will contain one element which is an array with sort field
02703      and sort order.
02704     */
02705     static function sortArrayBySortFieldAndSortOrder( $sortField, $sortOrder )
02706     {
02707         return array( array( eZContentObjectTreeNode::sortFieldName( $sortField ),
02708                               $sortOrder ) );
02709     }
02710 
02711     /*!
02712      Will assign a section to the current node and all child objects.
02713      Only main node assignments will be updated.
02714      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02715      the calls within a db transaction; thus within db->begin and db->commit.
02716     */
02717     static function assignSectionToSubTree( $nodeID, $sectionID, $oldSectionID = false )
02718     {
02719         $db = eZDB::instance();
02720 
02721         $node = eZContentObjectTreeNode::fetch( $nodeID );
02722         $nodePath =  $node->attribute( 'path_string' );
02723 
02724         $sectionID =(int) $sectionID;
02725 
02726         $pathString = " path_string like '$nodePath%' AND ";
02727 
02728         // fetch the object id's which needs to be updated
02729         $objectIDArray = $db->arrayQuery( "SELECT
02730                                                    ezcontentobject.id
02731                                             FROM
02732                                                    ezcontentobject_tree, ezcontentobject
02733                                             WHERE
02734                                                   $pathString
02735                                                   ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
02736                                                   ezcontentobject_tree.main_node_id=ezcontentobject_tree.node_id" );
02737         if ( count( $objectIDArray ) == 0 )
02738             return;
02739 
02740         // Who assigns which section at which node should be logged.
02741         $section = eZSection::fetch( $sectionID );
02742         $object = $node->object();
02743         eZAudit::writeAudit( 'section-assign', array( 'Section ID' => $sectionID, 'Section name' => $section->attribute( 'name' ),
02744                                                       'Node ID' => $nodeID,
02745                                                       'Content object ID' => $object->attribute( 'id' ),
02746                                                       'Content object name' => $object->attribute( 'name' ),
02747                                                       'Comment' => 'Assigned a section to the current node and all child objects: eZContentObjectTreeNode::assignSectionToSubTree()' ) );
02748 
02749         $objectSimpleIDArray = array();
02750         foreach ( $objectIDArray as $objectID )
02751         {
02752             $objectSimpleIDArray[] = $objectID['id'];
02753         }
02754 
02755         $filterPart = '';
02756         if ( $oldSectionID !== false )
02757         {
02758             $oldSectionID =(int) $oldSectionID;
02759             $filterPart = " section_id = '$oldSectionID' and ";
02760         }
02761 
02762         $db->begin();
02763         foreach ( array_chunk( $objectSimpleIDArray, 100 ) as $pagedObjectIDs )
02764         {
02765             $db->query( "UPDATE ezcontentobject SET section_id='$sectionID' WHERE $filterPart " . $db->generateSQLINStatement( $pagedObjectIDs, 'id', false, true, 'int' ) );
02766             eZSearch::updateObjectsSection( $pagedObjectIDs, $sectionID );
02767         }
02768         $db->commit();
02769 
02770         // clear caches for updated objects
02771         eZContentObject::clearCache( $objectSimpleIDArray );
02772     }
02773 
02774     /*!
02775      \static
02776      Updates the main node selection for the content object \a $objectID.
02777 
02778      \param $mainNodeID The ID of the node that should be that main node
02779      \param $objectID The ID of the object that all nodes belong to
02780      \param $version The version of the object to update node assignments, use \c false for currently published version.
02781      \param $parentMainNodeID The ID of the parent node of the new main placement
02782 
02783      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
02784      the calls within a db transaction; thus within db->begin and db->commit.
02785     */
02786     static function updateMainNodeID( $mainNodeID, $objectID, $version = false, $parentMainNodeID, $updateSection = true )
02787     {
02788         $mainNodeID = (int)$mainNodeID;
02789         $parentMainNodeID = (int)$parentMainNodeID;
02790         $objectID = (int)$objectID;
02791         $version = (int)$version;
02792 
02793         $db = eZDB::instance();
02794         $db->begin();
02795         $db->query( "UPDATE ezcontentobject_tree SET main_node_id=$mainNodeID WHERE contentobject_id=$objectID" );
02796         if ( !$version )
02797         {
02798             $rows = $db->arrayQuery( "SELECT current_version FROM ezcontentobject WHERE id=$objectID" );
02799             $version = $rows[0]['current_version'];
02800         }
02801         $db->query( "UPDATE eznode_assignment SET is_main=1 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node=$parentMainNodeID" );
02802         $db->query( "UPDATE eznode_assignment SET is_main=0 WHERE contentobject_id=$objectID AND contentobject_version=$version AND parent_node!=$parentMainNodeID" );
02803 
02804         $contentObject = eZContentObject::fetch( $objectID );
02805         $parentContentObject = eZContentObject::fetchByNodeID( $parentMainNodeID );
02806         if ( $updateSection && $contentObject->attribute( 'section_id' ) != $parentContentObject->attribute( 'section_id' ) )
02807         {
02808             $newSectionID = $parentContentObject->attribute( 'section_id' );
02809             eZContentObjectTreeNode::assignSectionToSubTree( $mainNodeID, $newSectionID );
02810         }
02811 
02812         $db->commit();
02813 
02814     }
02815 
02816     function fetchByCRC( $pathStr )
02817     {
02818         eZDebug::writeWarning( "Obsolete: use eZURLAlias instead", __METHOD__ );
02819         return null;
02820     }
02821 
02822     static function fetchByContentObjectID( $contentObjectID, $asObject = true, $contentObjectVersion = false )
02823     {
02824         $conds = array( 'contentobject_id' => $contentObjectID );
02825         if ( $contentObjectVersion !== false )
02826         {
02827             $conds['contentobject_version'] = $contentObjectVersion;
02828         }
02829         return eZPersistentObject::fetchObjectList( eZContentObjectTreeNode::definition(),
02830                                                     null,
02831                                                     $conds,
02832                                                     null,
02833                                                     null,
02834                                                     $asObject );
02835     }
02836 
02837     static function fetchByRemoteID( $remoteID, $asObject = true )
02838     {
02839         return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "remote_id" => $remoteID ) );
02840     }
02841 
02842     static function fetchByPath( $pathString, $asObject = true )
02843     {
02844         return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_string" => $pathString ) );
02845     }
02846 
02847     static function fetchByURLPath( $pathString, $asObject = true )
02848     {
02849         if ( $pathString == "" )
02850         {
02851             eZDebug::writeWarning( 'Can not fetch, given URLPath is empty', __METHOD__ );
02852             return null;
02853         }
02854 
02855         return eZContentObjectTreeNode::fetch( false, false, $asObject, array( "path_identification_string" => $pathString ) );
02856     }
02857 
02858     /**
02859      * Fetches path_identification_string for a list of nodes
02860      *
02861      * @param array(int) $nodeList
02862      *
02863      * @return array Associative array
02864      */
02865     static function fetchAliasesFromNodeList( $nodeList )
02866     {
02867         if ( !is_array( $nodeList ) || count( $nodeList ) < 1 )
02868             return array();
02869 
02870         $db = eZDB::instance();
02871         $where = $db->generateSQLINStatement( $nodeList, 'node_id', false, false, 'int' );
02872         $query = "SELECT node_id, path_identification_string FROM ezcontentobject_tree WHERE $where";
02873         $pathListArray = $db->arrayQuery( $query );
02874         return $pathListArray;
02875     }
02876 
02877     /**
02878      * Get Main Node Id ( or Main Node if $asObject = true ) by Content Object Id.
02879      *
02880      * @param int $objectID
02881      * @param boolean $asObject
02882      *
02883      * @return int|null
02884      */
02885     static function findMainNode( $objectID, $asObject = false )
02886     {
02887         $objectID = (int)$objectID;
02888         $query = "SELECT node_id
02889                   FROM ezcontentobject_tree
02890                   WHERE contentobject_id=$objectID AND
02891                   main_node_id = node_id";
02892         $db = eZDB::instance();
02893         $nodeListArray = $db->arrayQuery( $query );
02894         if ( count( $nodeListArray ) == 1 )
02895         {
02896             if ( $asObject )
02897             {
02898                  return eZContentObjectTreeNode::fetch( $nodeListArray[0]['node_id'] );
02899             }
02900             else
02901             {
02902                 return $nodeListArray[0]['node_id'];
02903             }
02904 
02905         }
02906         else if ( count( $nodeListArray ) > 1 )
02907         {
02908             eZDebug::writeError( $nodeListArray , "There are more then one main_node for objectID: $objectID" );
02909         }
02910 
02911         return null;
02912     }
02913 
02914     /**
02915      * Fetches the main nodes for an array of object id's
02916      * @param array(int) $objectIDArray an array of object IDs
02917      * @param bool $asObject
02918      *        Wether to return the result as an array of eZContentObjectTreeNode
02919      *        (true) or as an array of associative arrays (false)
02920      * @return array(array|eZContentObjectTreeNode)
02921      */
02922     static function findMainNodeArray( $objectIDArray, $asObject = true )
02923     {
02924         if ( count( $objectIDArray ) )
02925         {
02926             $db = eZDB::instance();
02927             $objectIDINSQL = $db->generateSQLINStatement( $objectIDArray, 'ezcontentobject_tree.contentobject_id', false, false, 'int' );
02928             $query="SELECT ezcontentobject.*,
02929                            ezcontentobject_tree.*,
02930                            ezcontentclass.serialized_name_list as class_serialized_name_list,
02931                            ezcontentclass.identifier as class_identifier,
02932                            ezcontentclass.is_container as is_container
02933                     FROM ezcontentobject_tree,
02934                          ezcontentobject,
02935                          ezcontentclass
02936                     WHERE $objectIDINSQL AND
02937                           ezcontentobject_tree.main_node_id = ezcontentobject_tree.node_id AND
02938                           ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
02939                           ezcontentclass.version=0  AND
02940                           ezcontentclass.id = ezcontentobject.contentclass_id";
02941 
02942             $nodeListArray = $db->arrayQuery( $query );
02943             if ( $asObject )
02944             {
02945                 $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
02946                 return $retNodeArray;
02947             }
02948             else
02949             {
02950                 return $nodeListArray;
02951             }
02952         }
02953         return null;
02954     }
02955 
02956 
02957     /**
02958      * Fetches a node by ID
02959      *
02960      * @param int|array $nodeID Either a node ID or array of node IDs
02961      * @param string $lang language code to fetch the node in. If not provided, the prioritized language list is used
02962      * @param bool $asObject True to fetch the node as an eZContentObjectTreeNode, false to fetch its attributes as an array
02963      * @param array $conditions An associative array (field => value) of fetch conditions. Will be applied as is to the SQL query
02964      *
02965      * @return eZContentObjectTreeNode
02966     */
02967     static function fetch( $nodeID = false, $lang = false, $asObject = true, $conditions = false )
02968     {
02969         $returnValue = null;
02970         $db = eZDB::instance();
02971         if ( ( is_numeric( $nodeID ) && $nodeID == 1 ) ||
02972              ( is_array( $nodeID ) && count( $nodeID ) === 1 && $nodeID[0] == 1 ) )
02973         {
02974             $query = "SELECT *
02975                 FROM ezcontentobject_tree
02976                 WHERE node_id = 1";
02977         }
02978         else
02979         {
02980             $versionNameTables = ', ezcontentobject_name ';
02981             $versionNameTargets = ', ezcontentobject_name.name as name,  ezcontentobject_name.real_translation ';
02982             $versionNameJoins = " and  ezcontentobject_tree.contentobject_id = ezcontentobject_name.contentobject_id and
02983                                   ezcontentobject_tree.contentobject_version = ezcontentobject_name.content_version and ";
02984             if ( $lang )
02985             {
02986                 $lang = $db->escapeString( $lang );
02987                 $versionNameJoins .= " ezcontentobject_name.content_translation = '$lang' ";
02988             }
02989             else
02990             {
02991                 $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' );
02992             }
02993 
02994             $languageFilter = ' AND '.eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
02995 
02996             $sqlCondition = '';
02997 
02998             if ( $nodeID !== false )
02999             {
03000                 if ( is_array( $nodeID ) )
03001                 {
03002                     if( count( $nodeID ) === 1 )
03003                     {
03004                         $sqlCondition = 'node_id = ' . (int) $nodeID[0] . ' AND ';
03005                     }
03006                     else
03007                     {
03008                         $sqlCondition = $db->generateSQLINStatement( $nodeID, 'node_id', false, true, 'int' ) . ' AND ';
03009                     }
03010                 }
03011                 else
03012                 {
03013                     $sqlCondition = 'node_id = ' . (int) $nodeID . ' AND ';
03014                 }
03015             }
03016 
03017             if ( is_array( $conditions ) )
03018             {
03019                 foreach( $conditions as $key => $condition )
03020                 {
03021                     if ( is_string( $condition ) )
03022                     {
03023                         $condition = $db->escapeString( $condition );
03024                         $condition = "'$condition'";
03025                     }
03026 
03027                     $sqlCondition .= "ezcontentobject_tree." . $db->escapeString( $key ) . "=$condition AND ";
03028                 }
03029             }
03030 
03031             if ( $sqlCondition == '' )
03032             {
03033                 eZDebug::writeWarning( 'Cannot fetch node, emtpy ID or no conditions given', __METHOD__ );
03034                 return $returnValue;
03035             }
03036 
03037             $query="SELECT ezcontentobject.*,
03038                        ezcontentobject_tree.*,
03039                        ezcontentclass.serialized_name_list as class_serialized_name_list,
03040                        ezcontentclass.identifier as class_identifier,
03041                        ezcontentclass.is_container as is_container
03042                        $versionNameTargets
03043                 FROM ezcontentobject_tree,
03044                      ezcontentobject,
03045                      ezcontentclass
03046                      $versionNameTables
03047                 WHERE $sqlCondition
03048                       ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
03049                       ezcontentclass.version=0  AND
03050                       ezcontentclass.id = ezcontentobject.contentclass_id
03051                       $languageFilter
03052                       $versionNameJoins";
03053         }
03054         $nodeListArray = $db->arrayQuery( $query );
03055 
03056         if ( count( $nodeListArray ) > 0 )
03057         {
03058             if ( $asObject )
03059             {
03060                 $returnValue = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
03061                 if ( count( $returnValue ) === 1 )
03062                     $returnValue = $returnValue[0];
03063             }
03064             else
03065             {
03066                 if ( count( $nodeListArray ) === 1 )
03067                     $returnValue = $nodeListArray[0];
03068                 else
03069                     $returnValue = $nodeListArray;
03070             }
03071         }
03072 
03073         return $returnValue;
03074     }
03075 
03076     /*!
03077      \static
03078      Finds the node for the object \a $contentObjectID which placed as child of node \a $parentNodeID.
03079      \return An eZContentObjectTreeNode object or \c null if no node was found.
03080     */
03081     static function fetchNode( $contentObjectID, $parentNodeID )
03082     {
03083         $returnValue = null;
03084         $ini = eZINI::instance();
03085         $db = eZDB::instance();
03086         $contentObjectID =(int) $contentObjectID;
03087         $parentNodeID =(int) $parentNodeID;
03088         $query = "SELECT ezcontentobject_tree.*,
03089                          ezcontentclass.serialized_name_list as class_serialized_name_list,
03090                          ezcontentclass.identifier as class_identifier,
03091                          ezcontentclass.is_container as is_container
03092                   FROM ezcontentobject_tree,
03093                        ezcontentobject,
03094                        ezcontentclass
03095                   WHERE ezcontentobject_tree.contentobject_id = '$contentObjectID' AND
03096                         ezcontentobject.id = '$contentObjectID' AND
03097                         ezcontentobject_tree.parent_node_id = '$parentNodeID' AND
03098                         ezcontentclass.version=0 AND
03099                         ezcontentclass.id = ezcontentobject.contentclass_id AND ".
03100                         eZContentLanguage::languagesSQLFilter( 'ezcontentobject' );
03101 
03102         $nodeListArray = $db->arrayQuery( $query );
03103         if ( count( $nodeListArray ) == 1 )
03104         {
03105             $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray, false );
03106             $returnValue = $retNodeArray[0];
03107         }
03108         return $returnValue;
03109     }
03110 
03111     function fetchParent()
03112     {
03113         return $this->fetch( $this->attribute( 'parent_node_id' ) );
03114     }
03115 
03116     function pathArray()
03117     {
03118         $pathString = $this->attribute( 'path_string' );
03119         $pathItems = explode( '/', $pathString );
03120         $pathArray = array();
03121         foreach ( $pathItems as $pathItem )
03122         {
03123             if ( $pathItem != '' )
03124                 $pathArray[] = (int) $pathItem;
03125         }
03126         return $pathArray;
03127     }
03128 
03129 
03130     function fetchPath()
03131     {
03132         $nodePath = $this->attribute( 'path_string' );
03133 
03134         return eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, false, true );
03135     }
03136 
03137     /*!
03138      \static
03139      \return An array with content node objects that is present in the node path \a $nodePath.
03140      \param $withLastNode If \c true the last node in the path is included in the list.
03141                           The last node is the node which the path was fetched from.
03142      \param $asObjects If \c true then return PHP objects, if not return raw row data.
03143      \param $limit maximum number of nodes in the path to use, starting from last node
03144     */
03145     static function fetchNodesByPathString( $nodePath, $withLastNode = false, $asObjects = true, $limit = false )
03146     {
03147         $nodesListArray = array();
03148         $pathString = eZContentObjectTreeNode::createNodesConditionSQLStringFromPath( $nodePath, $withLastNode, $limit );
03149 
03150         if ( $pathString  )
03151         {
03152             $useVersionName     = true;
03153             $versionNameTables  = eZContentObjectTreeNode::createVersionNameTablesSQLString ( $useVersionName );
03154             $versionNameTargets = eZContentObjectTreeNode::createVersionNameTargetsSQLString( $useVersionName );
03155             $versionNameJoins   = eZContentObjectTreeNode::createVersionNameJoinsSQLString  ( $useVersionName );
03156 
03157             $query = "SELECT ezcontentobject.*,
03158                              ezcontentobject_tree.*,
03159                              ezcontentclass.serialized_name_list as class_serialized_name_list,
03160                              ezcontentclass.identifier as class_identifier,
03161                              ezcontentclass.is_container as is_container
03162                              $versionNameTargets
03163                       FROM ezcontentobject_tree,
03164                            ezcontentobject,
03165                            ezcontentclass
03166                            $versionNameTables
03167                       WHERE $pathString
03168                             ezcontentobject_tree.contentobject_id=ezcontentobject.id  AND
03169                             ezcontentclass.version=0 AND
03170                             ezcontentclass.id = ezcontentobject.contentclass_id
03171                             $versionNameJoins
03172                       ORDER BY path_string";
03173 
03174             $db = eZDB::instance();
03175             $nodesListArray = $db->arrayQuery( $query );
03176         }
03177 
03178         if ( $asObjects )
03179         {
03180             return eZContentObjectTreeNode::makeObjectsArray( $nodesListArray );
03181         }
03182         return $nodesListArray;
03183     }
03184 
03185 
03186     /*!
03187      \static
03188      Extracts each node that in the path from db and returns an array of class identifiers
03189      \param $nodePath A string containing the path of the node, it consists of
03190                       node IDs starting from the root and delimited by / (slash).
03191      \param $withLastNode If \c true the last node in the path is included in the list.
03192                           The last node is the node which the path was fetched from.
03193      \param $limit maximum number of nodes in the path to use, starting from last node
03194      \return An array with class identifier and node ID.
03195 
03196      Example
03197      \code
03198      $list = fetchClassIdentifierListByPathString( '/2/10/', false );
03199      \endcode
03200     */
03201     static function fetchClassIdentifierListByPathString( $nodePath, $withLastNode, $limit = false )
03202     {
03203         $itemList = array();
03204         $nodes = eZContentObjectTreeNode::fetchNodesByPathString( $nodePath, $withLastNode, false, $limit );
03205 
03206         foreach ( $nodes as $node )
03207         {
03208             $itemList[]  = array( 'node_id'          => $node['node_id'],
03209                                   'class_identifier' => $node['class_identifier'] );
03210         }
03211 
03212         return $itemList;
03213     }
03214 
03215     /*!
03216      \deprecated This function should no longer be used, use the eZContentClass::instantiate and eZNodeAssignment::create instead.
03217     */
03218     function createObject( $contentClassID, $parentNodeID = 2 )
03219     {
03220         $class = eZContentClass::fetch( $contentClassID );
03221         $parentNode = eZContentObjectTreeNode::fetch( $parentNodeID );
03222         $parentContentObject = $parentNode->attribute( 'object' );
03223         $sectionID = $parentContentObject->attribute( 'section_id' );
03224         $object = $class->instantiate( false, $sectionID );
03225 
03226 //        $parentContentObject = $parentNode->attribute( 'contentobject' );
03227 
03228         $node = eZContentObjectTreeNode::addChildTo( $object->attribute( "id" ), $parentNodeID, true );
03229 //        $object->setAttribute( "main_node_id", $node->attribute( 'node_id' ) );
03230         $node->setAttribute( 'main_node_id', $node->attribute( 'node_id' ) );
03231         $object->store();
03232         $node->store();
03233 
03234         return $object;
03235     }
03236 
03237     /*!
03238      Add a child for this node to the object tree.
03239      \param $contentobjectID      The ID of the contentobject the child-node should point to.
03240      \param $asObject             If true it will return the new child-node as an object, if not it returns the ID.
03241      \param $contentObjectVersion The version to use on the newly created child-node, if
03242                                   false it uses the current_version of the specified object.
03243      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03244      the calls within a db transaction; thus within db->begin and db->commit.
03245     */
03246     function addChild( $contentobjectID, $asObject = false, $contentObjectVersion = false )
03247     {
03248         return self::addChildTo( $contentobjectID, $this->attribute( 'node_id' ), $asObject, $contentObjectVersion );
03249     }
03250 
03251     /*!
03252      Add a child to the object tree.
03253      \param $contentobjectID      The ID of the contentobject the child-node should point to.
03254      \param $nodeID               The ID of the parent-node to add child-node to.
03255      \param $asObject             If true it will return the new child-node as an object, if not it returns the ID.
03256      \param $contentObjectVersion The version to use on the newly created child-node, if
03257                                   false it uses the current_version of the specified object.
03258      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03259      the calls within a db transaction; thus within db->begin and db->commit.
03260     */
03261     static function addChildTo( $contentobjectID, $nodeID, $asObject = false, $contentObjectVersion = false )
03262     {
03263         $node = eZContentObjectTreeNode::fetch( $nodeID );
03264         $contentObject = eZContentObject::fetch( $contentobjectID );
03265         if ( !$contentObject )
03266         {
03267             return false;
03268         }
03269 
03270         if ( !$contentObjectVersion )
03271         {
03272             $contentObjectVersion = $contentObject->attribute( 'current_version' );
03273         }
03274 
03275         $db = eZDB::instance();
03276         $parentMainNodeID = $node->attribute( 'node_id' ); //$parent->attribute( 'main_node_id' );
03277         $parentPath = $node->attribute( 'path_string' );
03278         $parentDepth = $node->attribute( 'depth' );
03279         $isInvinsible = $node->attribute( 'is_invisible' );
03280 
03281         $nodeDepth = $parentDepth + 1 ;
03282 
03283         $insertedNode = eZContentObjectTreeNode::create( $parentMainNodeID, $contentobjectID );
03284 
03285         // set default sorting from content class
03286         $contentClass = $contentObject->attribute( 'content_class' );
03287         $insertedNode->setAttribute( 'sort_field', $contentClass->attribute( 'sort_field' ) );
03288         $insertedNode->setAttribute( 'sort_order', $contentClass->attribute( 'sort_order' ) );
03289 
03290         $insertedNode->setAttribute( 'depth', $nodeDepth );
03291         $insertedNode->setAttribute( 'path_string', '/TEMPPATH' );
03292 
03293         $insertedNode->setAttribute( 'contentobject_version', $contentObjectVersion );
03294 
03295         // If the parent node is invisible, the new created node should be invisible as well.
03296         $insertedNode->setAttribute( 'is_invisible', $isInvinsible );
03297 
03298         $db->begin();
03299         $insertedNode->store();
03300         $insertedID = $insertedNode->attribute( 'node_id' );
03301         $newNodePath = $parentPath . $insertedID . '/';
03302         $insertedNode->setAttribute( 'path_string', $newNodePath );
03303         $insertedNode->store();
03304         $db->commit();
03305 
03306         if ( $asObject )
03307         {
03308             return $insertedNode;
03309         }
03310         else
03311         {
03312             return $insertedID;
03313         }
03314     }
03315 
03316     /*!
03317      \return an url alias for the current node. It will generate a unique alias.
03318     */
03319     function pathWithNames( $regenerateCurrent = false )
03320     {
03321         // Only set name if current node is not the content root
03322         $ini = eZINI::instance( 'content.ini' );
03323         $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' );
03324         if ( $this->attribute( 'node_id' ) != $contentRootID )
03325         {
03326             $pathArray = $this->pathArray();
03327             // Get rid of node with ID 1 (a special node)
03328             array_shift( $pathArray );
03329             if ( $regenerateCurrent )
03330             {
03331                 // Get rid of current node, path element for this will be calculated
03332                 array_pop( $pathArray );
03333             }
03334             if ( count( $pathArray ) == 0 )
03335             {
03336                 $path = '';
03337             }
03338             else
03339             {
03340                 $path = eZURLAliasML::fetchPathByActionList( "eznode", $pathArray, $this->CurrentLanguage );
03341             }
03342 
03343             // Fallback in case fetchPathByActionList() fails,
03344             // then we ask for the path from the parent and generate the current
03345             // entry ourselves.
03346             if ( $path === null )
03347             {
03348                 if ( $this->attribute( 'depth' ) == 0 ) // Top node should just return empty string
03349                 {
03350                     return '';
03351                 }
03352 
03353                 eZDebug::writeError( __METHOD__ . "() failed to fetch path of node " . $this->attribute( 'node_id' ) . ", falling back to generated url entries. Run updateniceurls.php to fix the problem." );
03354 
03355                 // Return a perma-link when the path lookup failed, this link will always work
03356                 $path = 'content/view/full/' . $this->attribute( 'node_id' );
03357                 return $path;
03358             }
03359 
03360             if ( $regenerateCurrent )
03361             {
03362                 $nodeName = $this->attribute( 'name' );
03363                 $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $this->attribute( 'node_id' ) );
03364 
03365                 if ( $path != '' )
03366                 {
03367                     $path .= '/' . $nodeName ;
03368                 }
03369                 else
03370                 {
03371                     $path  = $nodeName ;
03372                 }
03373             }
03374         }
03375         else
03376         {
03377             $path = '';
03378         }
03379 
03380         if ( $regenerateCurrent )
03381         {
03382             $path = $this->checkPath( $path );
03383         }
03384         return $path;
03385     }
03386 
03387     /*!
03388      Check if a node with the same name already exists. If so create a $name + __x value.
03389     */
03390     function checkPath( $path )
03391     {
03392         $path = eZURLAliasML::cleanURL( $path );
03393         $elements = explode( "/", $path );
03394         $element = array_pop( $elements );
03395         return $this->adjustPathElement( $element );
03396     }
03397 
03398     /*!
03399      Checks the path element $element against reserved words and existing elements.
03400      If the path element is already used, it will append a number and try again.
03401 
03402      The adjusted path element is returned.
03403 
03404      \param $element The desired url element name
03405      \param $useParentFromNodeObject Use the parent from node object as a base
03406                                      for checking name collisions. This is needed
03407                                      when moving nodes, and the url entries are
03408                                      not updated yet.
03409 
03410      \code
03411      echo $node->adjustPathElement( 'Content' ); // outputs Content1
03412      \endcode
03413      */
03414     function adjustPathElement( $element, $useParentFromNodeObject = false )
03415     {
03416         $nodeID       = (int)$this->attribute( 'node_id' );
03417         $parentNodeID = (int)$this->attribute( 'parent_node_id' );
03418         $action       = "eznode:" . $nodeID;
03419 
03420         $elements = eZURLAliasML::fetchByAction( 'eznode', $nodeID );
03421         if ( count( $elements ) > 0 and !$useParentFromNodeObject )
03422         {
03423             $parentElementID = (int)$elements[0]->attribute( 'parent' );
03424             return eZURLAliasML::findUniqueText( $parentElementID, $element, $action );
03425         }
03426         else
03427         {
03428             $parentElements = eZURLAliasML::fetchByAction( 'eznode', $parentNodeID );
03429             if ( count( $parentElements ) > 0 && $parentElements[0]->attribute( 'text' ) != '' )
03430             {
03431                 // Pick one of the parents and get the ID
03432                 $parentElementID = (int)$parentElements[0]->attribute( 'id' );
03433                 return eZURLAliasML::findUniqueText( $parentElementID, $element, $action );
03434             }
03435         }
03436         return eZURLAliasML::findUniqueText( 0, $element, $action );
03437     }
03438 
03439     /*!
03440      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03441      the calls within a db transaction; thus within db->begin and db->commit.
03442      \deprecated Use updateSubTreePath() instead.
03443      */
03444     function updateURLAlias()
03445     {
03446         eZDebug::writeWarning( __METHOD__ . " is deprecated, use updateSubTreePath() instead" );
03447         return $this->updateSubTreePath();
03448     }
03449 
03450     /*!
03451      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03452      the calls within a db transaction; thus within db->begin and db->commit.
03453      */
03454     function updateSubTreePath( $updateParent = true, $nodeMove = false )
03455     {
03456         $changeCount = 0;
03457 
03458         $nodeID       = $this->attribute( 'node_id' );
03459         $parentNodeID = $this->attribute( 'parent_node_id' );
03460 
03461         // Avoid recursion due to database inconsistencies
03462         if ( $nodeID === $parentNodeID )
03463         {
03464             eZDebug::writeError( "Parent node ID equals node ID for node: $nodeID. The node cannot be a parent of itself!", __METHOD__ );
03465             return false;
03466         }
03467 
03468         // Only set name if current node is not the content root
03469         $ini = eZINI::instance( 'content.ini' );
03470         $contentRootID = $ini->variable( 'NodeSettings', 'RootNode' );
03471         $obj           = $this->object();
03472         $alwaysMask    = ( $obj->attribute( 'language_mask' ) & 1 );
03473         $languages     = $obj->allLanguages();
03474         $nameList      = array();
03475 
03476         $initialLanguageID      = $obj->attribute( 'initial_language_id' );
03477         $pathIdentificationName = false;
03478         foreach ( $languages as $language )
03479         {
03480             $nodeName = '';
03481             if ( $nodeID != $contentRootID )
03482             {
03483                 $objClass = $obj->attribute( 'content_class' );
03484                 $nodeName = $objClass->urlAliasName( $obj, false, $language->attribute( 'locale' ) );
03485                 $nodeName = eZURLAliasFilter::processFilters( $nodeName, $language, $this );
03486                 $nodeName = eZURLAliasML::convertToAlias( $nodeName, 'node_' . $nodeID );
03487                 $nodeName = $this->adjustPathElement( $nodeName, $nodeMove );
03488 
03489                 // Compatibility mode:
03490                 // Store name for the 'path_identification_string' column.
03491                 if ( $initialLanguageID == $language->attribute( 'id' ) )
03492                 {
03493                     $pathIdentificationName = eZURLAliasML::convertToAliasCompat( $nodeName, 'node_' . $nodeID );
03494                 }
03495             }
03496             $nameList[] = array( 'text'     => $nodeName,
03497                                  'language' => $language );
03498         }
03499 
03500         $parentActionName  = "eznode";
03501         $parentActionValue = $parentNodeID;
03502 
03503         $parentElementID = false;
03504         $existingElements = eZURLAliasML::fetchByAction( "eznode", $nodeID );
03505         $existingElementID = null;
03506         if ( count( $existingElements ) > 0 )
03507         {
03508             $existingElementID = $existingElements[0]->attribute( 'id' );
03509             $parentElementID = $existingElements[0]->attribute( 'parent' );
03510         }
03511 
03512         // If we have parent element it means the node is already published
03513         // and we have to see if it has been moved
03514         if ( $parentNodeID != 1 and $updateParent )
03515         {
03516             $parents = eZURLAliasML::fetchByAction( "eznode", $parentNodeID );
03517             if ( count( $parents ) == 0 )
03518             {
03519                 $parentNode = $this->fetchParent();
03520 
03521                 if ( !$parentNode )
03522                 {
03523                     return false;
03524                 }
03525 
03526                 $result = $parentNode->updateSubTreePath();
03527                 if ( !$result )
03528                 {
03529                     return false;
03530                 }
03531                 $parents = eZURLAliasML::fetchByAction( $parentActionName, $parentActionValue );
03532                 if ( count( $parents ) == 0 )
03533                 {
03534                     return false;
03535                 }
03536                 $oldParentElementID = $parentElementID;
03537                 foreach ( $parents as $paren )
03538                 {
03539                     $parentElementID = 0;
03540                     if ( $paren->attribute( 'text' ) != '' )
03541                     {
03542                         $parentElementID = (int)$paren->attribute( 'link' );
03543                         break;
03544                     }
03545                 }
03546             }
03547             else
03548             {
03549                 $oldParentElementID = $parentElementID;
03550                 $parentElementID = 0;
03551                 foreach ( $parents as $paren )
03552                 {
03553                     if ( $paren->attribute( 'text' ) != '' )
03554                     {
03555                         $parentElementID = (int)$paren->attribute( 'link' );
03556                         break;
03557                     }
03558                 }
03559             }
03560         }
03561         else // Parent is ID 1, ie. this node is top-level
03562         {
03563         }
03564 
03565         $this->updatePathIdentificationString( $pathIdentificationName );
03566 
03567         $languageID = $obj->attribute( 'initial_language_id' );
03568         $cleanup    = false;
03569         foreach ( $nameList as $nameEntry )
03570         {
03571             $text     = $nameEntry['text'];
03572             $language = $nameEntry['language'];
03573             $result = eZURLAliasML::storePath( $text, 'eznode:' . $nodeID, $language, false, $alwaysMask, $parentElementID, $cleanup );
03574             if ( $result['status'] === true )
03575                 $changeCount++;
03576         }
03577         return $changeCount;
03578     }
03579 
03580     /*!
03581      \private
03582      Updates the path_identification_string field in ezcontentobject_tree by
03583      fetching the value from the parent and appending $pathIdentificationName.
03584 
03585      \note This stores the current object to the database
03586      */
03587     function updatePathIdentificationString( $pathIdentificationName )
03588     {
03589         // Update the path_identification_string column for the node
03590         $pathIdentificationString = '';
03591         if ( $this->attribute( 'parent_node_id' ) != 1 )
03592         {
03593             if ( !isset( $parentNode ) )
03594                 $parentNode = $this->fetchParent();
03595             // Avoid crashes due to database inconsistencies
03596             if ( $parentNode instanceof eZContentObjectTreeNode )
03597             {
03598                 $pathIdentificationString = $parentNode->attribute( 'path_identification_string' );
03599             }
03600             else
03601             {
03602                 eZDebug::writeError( 'Failed to fetch parent node for pathIdentificationName "' . $pathIdentificationName .
03603                                      '" and parent_node_id ' . $this->attribute( 'parent_node_id' ), __METHOD__ );
03604             }
03605         }
03606         if ( strlen( $pathIdentificationString ) > 0 )
03607             $pathIdentificationString .= '/' . $pathIdentificationName;
03608         else
03609             $pathIdentificationString = $pathIdentificationName;
03610         if ( $this->attribute( 'path_identification_string' ) != $pathIdentificationString )
03611         {
03612             $this->setAttribute( 'path_identification_string', $pathIdentificationString );
03613             $this->sync();
03614         }
03615     }
03616 
03617     /*!
03618      \static
03619      \sa removeThis
03620     */
03621     static function removeNode( $nodeID = 0 )
03622     {
03623         $node = eZContentObjectTreeNode::fetch( $nodeID );
03624         if ( !is_object( $node ) )
03625         {
03626             return;
03627         }
03628 
03629         return $node->removeThis();
03630     }
03631 
03632     /*!
03633       Removes the current node.
03634       Use ->removeNodeFromTree() if you need to handle main node change + remove object if needed
03635 
03636       \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03637      the calls within a db transaction; thus within db->begin and db->commit.
03638     */
03639     function removeThis()
03640     {
03641         $ini = eZINI::instance();
03642 
03643         $object = $this->object();
03644         $nodeID = $this->attribute( 'node_id' );
03645         if ( eZAudit::isAuditEnabled() )
03646         {
03647             // Set audit params.
03648             $objectID = $object->attribute( 'id' );
03649             $objectName = $object->attribute( 'name' );
03650 
03651             eZAudit::writeAudit( 'content-delete', array( 'Node ID' => $nodeID, 'Object ID' => $objectID, 'Content Name' => $objectName,
03652                                                           'Comment' => 'Removed the current node: eZContentObjectTreeNode::removeNode()' ) );
03653         }
03654 
03655         $db = eZDB::instance();
03656         $db->begin();
03657 
03658         $nodePath = $this->attribute( 'path_string' );
03659         $childrensPath = $nodePath ;
03660 
03661         $pathString = " path_string like '$childrensPath%' ";
03662 
03663         $urlAlias = $this->attribute( 'url_alias' );
03664 
03665         // Remove static cache
03666         if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
03667         {
03668             $optionArray = array( 'iniFile'      => 'site.ini',
03669                                   'iniSection'   => 'ContentSettings',
03670                                   'iniVariable'  => 'StaticCacheHandler' );
03671 
03672             $options = new ezpExtensionOptions( $optionArray );
03673 
03674             $staticCacheHandler = eZExtension::getHandlerClass( $options );
03675 
03676             $staticCacheHandler->removeURL( "/" . $urlAlias );
03677             $staticCacheHandler->generateAlwaysUpdatedCache();
03678 
03679             $parent = $this->fetchParent();
03680         }
03681 
03682         $db->query( "DELETE FROM ezcontentobject_tree
03683                             WHERE $pathString OR
03684                             path_string = '$nodePath'" );
03685 
03686         // Re-cache parent node
03687         if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
03688         {
03689             if ( $parent )
03690             {
03691                 $staticCacheHandler->cacheURL( "/" . $parent->urlAlias() );
03692             }
03693         }
03694 
03695         // Clean up URL alias entries
03696         eZURLAliasML::removeByAction( 'eznode', $nodeID );
03697 
03698         // Clean up content cache
03699         eZContentCacheManager::clearContentCacheIfNeeded( $this->attribute( 'contentobject_id' ) );
03700 
03701         // clean up user cache
03702         if ( in_array( $object->attribute( 'contentclass_id' ), eZUser::contentClassIDs() ) )
03703         {
03704             eZUser::removeSessionData( $object->attribute( 'id' ) );
03705             eZUser::purgeUserCacheByUserId( $object->attribute( 'id' ) );
03706         }
03707 
03708         $parentNode = $this->attribute( 'parent' );
03709         if ( is_object( $parentNode ) )
03710         {
03711             eZContentCacheManager::clearContentCacheIfNeeded( $parentNode->attribute( 'contentobject_id' ) );
03712             $parentNode->updateAndStoreModified();
03713         }
03714 
03715         // Clean up policies and limitations
03716         eZRole::cleanupByNode( $this );
03717 
03718         // Clean up recent items
03719         eZContentBrowseRecent::removeRecentByNodeID( $nodeID );
03720 
03721         // Clean up bookmarks
03722         eZContentBrowseBookmark::removeByNodeID( $nodeID );
03723 
03724         // Clean up tip-a-friend counter
03725         eZTipafriendCounter::removeForNode( $nodeID );
03726 
03727         // Clean up view counter
03728         eZViewCounter::removeCounter( $nodeID );
03729 
03730         $db->commit();
03731     }
03732 
03733     /*!
03734      \static
03735      Returns information on what will happen if all subtrees in \a $deleteIDArray
03736      is removed. The returned structure is:
03737      - move_to_trash     - \c true if removed objects can be moved to trash,
03738                            some objects are not allowed to be in trash (e.g user).
03739      - total_child_count - The total number of children for all delete items
03740      - can_remove_all    - Will be set to \c true if all selected items can be removed, \c false otherwise
03741      - delete_list - A list of all subtrees that should be removed, structure:
03742      -- node               - The content node
03743      -- object             - The content object
03744      -- class              - The content class
03745      -- node_name          - The name of the node
03746      -- child_count        - Total number of child items below the node
03747      -- can_remove         - Boolean which tells if the user has permission to remove the node
03748      -- can_remove_subtree - Boolean which tells if the user has permission to remove items in the subtree
03749      -- new_main_node_id   - The new main node ID for the node if it needs to be moved, or \c false if not
03750      -- object_node_count  - The number of nodes the object has (before removal)
03751      -- sole_node_count    - The number of nodes in the subtree (excluding current) that does
03752                              not have multiple locations.
03753 
03754      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03755      the calls within a db transaction; thus within db->begin and db->commit.
03756     */
03757     static function subtreeRemovalInformation( $deleteIDArray )
03758     {
03759         return eZContentObjectTreeNode::removeSubtrees( $deleteIDArray, true, true );
03760     }
03761 
03762     /*!
03763      \static
03764      Will remove the nodes in the subtrees defined in \a $deleteIDArray,
03765      it will only remove the nodes unless there are no more nodes for
03766      an object in which case the object is removed too.
03767 
03768      \param $moveToTrash If \c true it will move the object to trash, if \c false
03769                          the object will be purged from the system.
03770      \param $infoOnly If set to \c true then it will not remove the subtree
03771                       but instead return information on what will happen
03772                       if it is removed. See subtreeRemovalInformation() for the
03773                       returned structure.
03774 
03775      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
03776      the calls within a db transaction; thus within db->begin and db->commit.
03777     */
03778     static function removeSubtrees( $deleteIDArray, $moveToTrash = true, $infoOnly = false )
03779     {
03780         $moveToTrashAllowed = true;
03781         $deleteResult = array();
03782         $totalChildCount = 0;
03783         $totalLoneNodeCount = 0;
03784         $canRemoveAll = true;
03785         $hasPendingObject = false;
03786 
03787         $db = eZDB::instance();
03788         $db->begin();
03789 
03790         $userClassIDArray = eZUser::contentClassIDs();
03791 
03792         foreach ( $deleteIDArray as $deleteID )
03793         {
03794             $node = eZContentObjectTreeNode::fetch( $deleteID );
03795             if ( $node === null )
03796                 continue;
03797 
03798             $object = $node->attribute( 'object' );
03799             if ( $object === null )
03800                 continue;
03801 
03802             $class = $object->attribute( 'content_class' );
03803             $canRemove = $node->attribute( 'can_remove' );
03804             $canRemoveSubtree = true;
03805 
03806             $nodeID = $node->attribute( 'node_id' );
03807             $nodeName = $object->attribute( 'name' );
03808 
03809             $childCount = 0;
03810             $newMainNodeID = false;
03811             $objectNodeCount = 0;
03812             $readableChildCount = 0;
03813 
03814             if ( $canRemove )
03815             {
03816                 $isUserClass = in_array( $class->attribute( 'id' ), $userClassIDArray );
03817 
03818                 if ( $moveToTrashAllowed and $isUserClass )
03819                 {
03820                     $moveToTrashAllowed = false;
03821                 }
03822                 $readableChildCount = $node->subTreeCount( array( 'Limitation' => array() ) );
03823                 $childCount = $node->subTreeCount( array( 'IgnoreVisibility' => true ) );
03824                 $totalChildCount += $childCount;
03825 
03826                 $allAssignedNodes = $object->attribute( 'assigned_nodes' );
03827                 $objectNodeCount = count( $allAssignedNodes );
03828                 // We need to find a new main node ID if we are trying
03829                 // to remove the current main node.
03830                 if ( $node->attribute( 'main_node_id' ) == $nodeID )
03831                 {
03832                     if ( count( $allAssignedNodes ) > 1 )
03833                     {
03834                         foreach( $allAssignedNodes as $assignedNode )
03835                         {
03836                             $assignedNodeID = $assignedNode->attribute( 'node_id' );
03837                             if ( $assignedNodeID == $nodeID )
03838                                 continue;
03839                             $newMainNodeID = $assignedNodeID;
03840                             break;
03841                         }
03842                     }
03843                 }
03844 
03845                 if ( $infoOnly )
03846                 {
03847                     // Find the number of items in the subtree we are allowed to remove
03848                     // if this differs from the total count it means we have items we cannot remove
03849                     // We do this by fetching the limitation list for content/remove
03850                     // and passing it to the subtree count function.
03851                     $currentUser = eZUser::currentUser();
03852                     $accessResult = $currentUser->hasAccessTo( 'content', 'remove' );
03853                     if ( $accessResult['accessWord'] == 'limited' )
03854                     {
03855                         $limitationList = $accessResult['policies'];
03856                         $removeableChildCount = $node->subTreeCount( array( 'Limitation' => $limitationList, 'IgnoreVisibility' => true ) );
03857                         $canRemoveSubtree = ( $removeableChildCount == $childCount );
03858                         $canRemove = $canRemoveSubtree;
03859                     }
03860                     //check if there is sub object in pending status
03861                     $limitCount = 100;
03862                     $offset = 0;
03863                     while( 1 )
03864                     {
03865                         $children = $node->subTree( array( 'Limitation' => array(),
03866                                                             'SortBy' => array( 'path' , false ),
03867                                                             'Offset' => $offset,
03868                                                             'Limit' => $limitCount,
03869                                                             'IgnoreVisibility' => true,
03870                                                             'AsObject' => false ) );
03871                         // fetch pending node assignment(pending object)
03872                         $idList = array();
03873                         //add node itself into idList
03874                         if( $offset === 0 )
03875                         {
03876                             $idList[] = $nodeID;
03877                         }
03878                         foreach( $children as $child )
03879                         {
03880                             $idList[] = $child['node_id'];
03881                         }
03882 
03883                         if( count( $idList ) === 0 )
03884                         {
03885                             break;
03886                         }
03887                         $pendingChildCount = eZNodeAssignment::fetchChildCountByVersionStatus( $idList,
03888                                                                                        eZContentObjectVersion::STATUS_PENDING );
03889                         if( $pendingChildCount !== 0 )
03890                         {
03891                             // there is pending object
03892                             $hasPendingObject = true;
03893                             break;
03894                         }
03895                         $offset += $limitCount;
03896                     }
03897                 }
03898 
03899                 // We will only remove the subtree if are allowed
03900                 // and are told to do so.
03901                 if ( $canRemove and !$infoOnly )
03902                 {
03903                     $moveToTrashTemp = $moveToTrash;
03904                     if ( !$moveToTrashAllowed )
03905                         $moveToTrashTemp = false;
03906 
03907                     // Remove children, fetching them by 100 to avoid memory overflow.
03908                     // removeNodeFromTree -> removeThis handles cache clearing
03909                     while ( 1 )
03910                     {
03911                         // We should remove the latest subitems first,
03912                         // so we should fetch subitems sorted by 'path_string' DESC
03913                         $children = $node->subTree( array( 'Limitation' => array(),
03914                                                            'SortBy' => array( 'path' , false ),
03915                                                            'Limit' => 100,
03916                                                            'IgnoreVisibility' => true ) );
03917                         if ( !$children )
03918                             break;
03919 
03920                         foreach ( $children as $child )
03921                         {
03922                             $childObject = $child->attribute( 'object' );
03923                             $child->removeNodeFromTree( $moveToTrashTemp );
03924                             eZContentObject::clearCache();
03925                         }
03926                     }
03927 
03928                     $node->removeNodeFromTree( $moveToTrashTemp );
03929                 }
03930             }
03931             if ( !$canRemove )
03932                 $canRemoveAll = false;
03933 
03934             // Do not create info list if we are removing subtrees
03935             if ( !$infoOnly )
03936                 continue;
03937 
03938             $soleNodeCount = $node->subtreeSoleNodeCount();
03939             $totalLoneNodeCount += $soleNodeCount;
03940             if ( $objectNodeCount <= 1 )
03941                 ++$totalLoneNodeCount;
03942 
03943             $item = array( "nodeName" => $nodeName, // Backwards compatibility
03944                            "childCount" => $childCount, // Backwards compatibility
03945                            "additionalWarning" => '', // Backwards compatibility, this will always be empty
03946                            'node' => $node,
03947                            'object' => $object,
03948                            'class' => $class,
03949                            'node_name' => $nodeName,
03950                            'child_count' => $childCount,
03951                            'object_node_count' => $objectNodeCount,
03952                            'sole_node_count' => $soleNodeCount,
03953                            'can_remove' => $canRemove,
03954                            'can_remove_subtree' => $canRemoveSubtree,
03955                            'real_child_count' => $readableChildCount,
03956                            'new_main_node_id' => $newMainNodeID );
03957             $deleteResult[] = $item;
03958         }
03959 
03960         $db->commit();
03961 
03962         if ( !$infoOnly )
03963             return true;
03964 
03965         if ( $moveToTrashAllowed and $totalLoneNodeCount == 0 )
03966             $moveToTrashAllowed = false;
03967 
03968         return array( 'move_to_trash' => $moveToTrashAllowed,
03969                       'total_child_count' => $totalChildCount,
03970                       'can_remove_all' => $canRemoveAll,
03971                       'delete_list' => $deleteResult,
03972                       'has_pending_object' => $hasPendingObject,
03973                       'reverse_related_count' => eZContentObjectTreeNode::reverseRelatedCount( $deleteIDArray ) );
03974     }
03975 
03976     /*!
03977      \private
03978      \static
03979      Return reverse related count for specified node
03980 
03981      \param $nodeIDArray, array of node id's
03982 
03983      \return reverse related count.
03984     */
03985     static function reverseRelatedCount( $nodeIDArray )
03986     {
03987         // Select count of all elements having reverse relations. And ignore those items that don't relate to objects other than being removed.
03988         if ( $nodeIDArray === array() )
03989         {
03990             return 0;
03991         }
03992 
03993         foreach( $nodeIDArray as $nodeID )
03994         {
03995             $contentObjectTreeNode = eZContentObjectTreeNode::fetch( $nodeID, false, false );
03996             $tempPathString = $contentObjectTreeNode['path_string'];
03997 
03998             // Create WHERE section
03999             $pathStringArray[] = "tree.path_string like '$tempPathString%'";
04000             $path2StringArray[] = "tree2.path_string like '$tempPathString%'";
04001         }
04002         $path_strings = '( ' . implode( ' OR ', $pathStringArray ) . ' ) ';
04003         $path_strings_where = '( ' . implode( ' OR ', $path2StringArray ) . ' ) ';
04004 
04005         // Total count of sub items
04006         $db = eZDB::instance();
04007         $countOfItems = $db->arrayQuery( "SELECT COUNT( DISTINCT( tree.node_id ) ) as count
04008                                                   FROM  ezcontentobject_tree tree,  ezcontentobject obj,
04009                                                         ezcontentobject_link link LEFT JOIN ezcontentobject_tree tree2
04010                                                         ON link.from_contentobject_id = tree2.contentobject_id
04011                                                   WHERE $path_strings
04012                                                         and link.to_contentobject_id = tree.contentobject_id
04013                                                         and obj.id = link.from_contentobject_id
04014                                                         and obj.current_version = link.from_contentobject_version
04015                                                         and not $path_strings_where" );
04016 
04017         if ( $countOfItems )
04018         {
04019             return $countOfItems[0]['count'];
04020         }
04021     }
04022 
04023     /*!
04024      Will check if you are  removing the main node in which case it relocates
04025      the main node before removing it. It will also remove the object if there
04026      no more node assignments for it.
04027      \param $moveToTrash If \c true it will move the object to trash, if \c false
04028                          the object will be purged from the system.
04029 
04030      \note This uses remove() to do the actual node removal but has some extra logic
04031      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
04032      the calls within a db transaction; thus within db->begin and db->commit.
04033     */
04034     function removeNodeFromTree( $moveToTrash = true )
04035     {
04036         $nodeID = $this->attribute( 'node_id' );
04037         $object = $this->object();
04038         $assignedNodes = $object->attribute( 'assigned_nodes' );
04039         if ( $nodeID == $this->attribute( 'main_node_id' ) )
04040         {
04041             if ( count( $assignedNodes ) > 1 )
04042             {
04043                 $newMainNode = false;
04044                 foreach ( $assignedNodes as $assignedNode )
04045                 {
04046                     $assignedNodeID = $assignedNode->attribute( 'node_id' );
04047                     if ( $assignedNodeID == $nodeID )
04048                         continue;
04049                     $newMainNode = $assignedNode;
04050                     break;
04051                 }
04052 
04053                 // We need to change the main node ID before we remove the current node
04054                 $db = eZDB::instance();
04055                 $db->begin();
04056                 eZContentObjectTreeNode::updateMainNodeID( $newMainNode->attribute( 'node_id' ),
04057                                                            $object->attribute( 'id' ),
04058                                                            $object->attribute( 'current_version' ),
04059                                                            $newMainNode->attribute( 'parent_node_id' ) );
04060                 $this->removeThis();
04061                 eZSearch::addObject( $object );
04062                 $db->commit();
04063             }
04064             else
04065             {
04066                 // This is the last assignment so we remove the object too
04067                 $db = eZDB::instance();
04068                 $db->begin();
04069                 $this->removeThis();
04070 
04071                 if ( $moveToTrash )
04072                 {
04073                     // saving information about this node in ..trash_node table
04074                     $trashNode = eZContentObjectTrashNode::createFromNode( $this );
04075                     $db = eZDB::instance();
04076                     $db->begin();
04077                     $trashNode->storeToTrash();
04078                     $db->commit();
04079                     $object->removeThis();
04080                 }
04081                 else
04082                 {
04083                     $object->purge();
04084                 }
04085                 $db->commit();
04086             }
04087         }
04088         else
04089         {
04090             $this->removeThis();
04091             if ( count( $assignedNodes ) > 1 )
04092             {
04093                 eZSearch::addObject( $object );
04094             }
04095         }
04096     }
04097 
04098     /*!
04099      \return The number of nodes in the current subtree that have no other placements.
04100     */
04101     function subtreeSoleNodeCount( $params = array() )
04102     {
04103         $nodeID = $this->attribute( 'node_id' );
04104         $node = $this;
04105 
04106         $depth = false;
04107         if ( isset( $params['Depth'] ) && is_numeric( $params['Depth'] ) )
04108         {
04109             $depth = $params['Depth'];
04110 
04111         }
04112 
04113         $fromNode = $nodeID;
04114 
04115         $nodePath = null;
04116         $nodeDepth = 0;
04117         if ( count( $node ) != 0 )
04118         {
04119             $nodePath = $node->attribute( 'path_string' );
04120             $nodeDepth = $node->attribute( 'depth' );
04121         }
04122 
04123         $childPath = $nodePath;
04124 
04125         $db = eZDB::instance();
04126         $pathString = " ezcot.path_string like '$childPath%' and ";
04127 
04128         $notEqParentString = "ezcot.node_id != $fromNode";
04129         $depthCond = '';
04130         if ( $depth )
04131         {
04132 
04133             $nodeDepth += $depth;
04134             if ( isset( $params[ 'DepthOperator' ] ) && $params[ 'DepthOperator' ] == 'eq' )
04135             {
04136                 $depthCond = ' ezcot.depth = ' . $nodeDepth . '';
04137                 $notEqParentString = '';
04138             }
04139             else
04140                 $depthCond = ' ezcot.depth <= ' . $nodeDepth . ' and ';
04141         }
04142 
04143         $tmpTableName = $db->generateUniqueTempTableName( 'eznode_count_%' );
04144         $db->createTempTable( "CREATE TEMPORARY TABLE $tmpTableName ( count int )" );
04145         $query = "INSERT INTO $tmpTableName
04146                   SELECT
04147                           count( ezcot.main_node_id ) AS count
04148                     FROM
04149                           ezcontentobject_tree ezcot,
04150                           ezcontentobject_tree ezcot_all
04151                     WHERE
04152                            $pathString
04153                            $depthCond
04154                            $notEqParentString
04155                            and ezcot.contentobject_id = ezcot_all.contentobject_id
04156                     GROUP BY ezcot_all.main_node_id
04157                     HAVING count( ezcot.main_node_id ) <= 1";
04158 
04159         $db->query( $query, eZDBInterface::SERVER_SLAVE );
04160         $query = "SELECT count( * ) AS count
04161                   FROM $tmpTableName";
04162 
04163         $rows = $db->arrayQuery( $query, array(), eZDBInterface::SERVER_SLAVE );
04164         $db->dropTempTable( "DROP TABLE $tmpTableName" );
04165         return $rows[0]['count'];
04166     }
04167 
04168     /*!
04169       Moves the node to the given node.
04170       \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
04171      the calls within a db transaction; thus within db->begin and db->commit.
04172     */
04173     function move( $newParentNodeID, $nodeID = 0 )
04174     {
04175         if ( $nodeID == 0 )
04176         {
04177             $node = $this;
04178             $nodeID = $node->attribute( 'node_id' );
04179         }
04180         else
04181         {
04182             $node = eZContentObjectTreeNode::fetch( $nodeID );
04183         }
04184 
04185         $oldPath = $node->attribute( 'path_string' );
04186         $oldParentNodeID = $node->attribute( 'parent_node_id' );
04187         $newParentNodeID =(int) $newParentNodeID;
04188         if ( $oldParentNodeID != $newParentNodeID )
04189         {
04190             $node->updateAndStoreModified();
04191             // Who moves which content should be logged.
04192             $object = $node->object();
04193             eZAudit::writeAudit( 'content-move', array( 'Node ID' => $node->attribute( 'node_id' ),
04194                                                         'Old parent node ID' => $oldParentNodeID, 'New parent node ID' => $newParentNodeID,
04195                                                         'Object ID' => $object->attribute( 'id' ), 'Content Name' => $object->attribute( 'name' ),
04196                                                         'Comment' => 'Moved the node to the given node: eZContentObjectTreeNode::move()' ) );
04197 
04198             $newParentNode = eZContentObjectTreeNode::fetch( $newParentNodeID );
04199             $newParentPath = $newParentNode->attribute( 'path_string' );
04200             $newParentDepth = $newParentNode->attribute( 'depth' );
04201             $newPath = $newParentPath . $nodeID;
04202             $oldDepth = $node->attribute( 'depth' );
04203 
04204             $oldPathLength = strlen( $oldPath );
04205             $moveQuery = "UPDATE
04206                                  ezcontentobject_tree
04207                           SET
04208                                  parent_node_id = $newParentNodeID
04209                           WHERE
04210                                  node_id = $nodeID";
04211             $db = eZDB::instance();
04212             $subStringString = $db->subString( 'path_string', $oldPathLength );
04213             $newPathString   = $db->concatString( array( "'$newPath'", $subStringString ) );
04214             $moveQuery1 = "UPDATE
04215                                  ezcontentobject_tree
04216                            SET
04217                                  path_string = $newPathString,
04218                                  depth = depth + $newParentDepth - $oldDepth + 1
04219                            WHERE
04220                                  path_string LIKE '$oldPath%'";
04221             $db->begin();
04222             $db->query( $moveQuery );
04223             $db->query( $moveQuery1 );
04224 
04225             /// role system clean up
04226             // Clean up policies and limitations
04227 
04228             $expireRoleCache = false;
04229 
04230             $limitationsToFix = eZPolicyLimitation::findByType( 'SubTree', $node->attribute( 'path_string' ), false );
04231             if ( count( $limitationsToFix ) > 0 )
04232             {
04233                 $limitationIDString = $db->generateSQLINStatement( $limitationsToFix, 'limitation_id' );
04234                 $subStringString =  $db->subString( 'value', $oldPathLength );
04235                 $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );
04236 
04237                 $query = "UPDATE
04238                                 ezpolicy_limitation_value
04239                           SET
04240                                 value = $newValue
04241                           WHERE
04242                                 value LIKE '$oldPath%' AND $limitationIDString";
04243 
04244                 $db->query( $query );
04245 
04246                 $expireRoleCache = true;
04247             }
04248 
04249             // clean up limitations on role assignment level
04250             $countRows = $db->arrayQuery( "SELECT COUNT(*) AS row_count FROM ezuser_role WHERE limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'" );
04251             $assignmentsToFixCount = $countRows[0]['row_count'];
04252 
04253             if ( $assignmentsToFixCount > 0 )
04254             {
04255                 $subStringString =  $db->subString( 'limit_value', $oldPathLength );
04256                 $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );
04257 
04258                 $db->query( "UPDATE
04259                                 ezuser_role
04260                              SET
04261                                 limit_value = $newValue
04262                              WHERE
04263                                 limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'"
04264                           );
04265 
04266                $expireRoleCache = true;
04267            }
04268 
04269             if ( $expireRoleCache )
04270             {
04271                 eZRole::expireCache();
04272             }
04273 
04274             // Update "is_invisible" node attribute.
04275             $newNode = eZContentObjectTreeNode::fetch( $nodeID );
04276             eZContentObjectTreeNode::updateNodeVisibility( $newNode, $newParentNode );
04277             $db->commit();
04278         }
04279     }
04280 
04281     function checkAccess( $functionName, $originalClassID = false, $parentClassID = false, $returnAccessList = false, $language = false )
04282     {
04283         $classID = $originalClassID;
04284         $user = eZUser::currentUser();
04285         $userID = $user->attribute( 'contentobject_id' );
04286 
04287         // Fetch the ID of the language if we get a string with a language code
04288         // e.g. 'eng-GB'
04289         $originalLanguage = $language;
04290         if ( is_string( $language ) && strlen( $language ) > 0 )
04291         {
04292             $language = eZContentLanguage::idByLocale( $language );
04293         }
04294         else
04295         {
04296             $language = false;
04297         }
04298 
04299         // This will be filled in with the available languages of the object
04300         // if a Language check is performed.
04301         $languageList = false;
04302 
04303         // This will be filled if parent object is needed.
04304         $parentObject = false;
04305 
04306         $origFunctionName = $functionName;
04307         // The 'move' function simply reuses 'edit' for generic access
04308         // but adds another top-level check below
04309         // The original function is still available in $origFunctionName
04310         if ( $functionName == 'move' )
04311             $functionName = 'edit';
04312 
04313         // Manage locations depends if it's removal or not.
04314         if ( $functionName == 'can_add_location' ||
04315              $functionName == 'can_remove_location' )
04316         {
04317             $functionName = 'manage_locations';
04318         }
04319 
04320         $accessResult = $user->hasAccessTo( 'content' , $functionName );
04321         $accessWord = $accessResult['accessWord'];
04322         if ( $origFunctionName == 'can_remove_location' )
04323         {
04324             if ( $this->ParentNodeID <= 1 )
04325             {
04326                 return 0;
04327             }
04328             $currentNode = eZContentObjectTreeNode::fetch( $this->ParentNodeID );
04329             if ( !$currentNode instanceof eZContentObjectTreeNode )
04330             {
04331                 return 0;
04332             }
04333             $contentObject = $currentNode->attribute( 'object' );
04334         }
04335         else
04336         {
04337             $currentNode = $this;
04338             $contentObject = $this->attribute( 'object' );
04339         }
04340 
04341         /*
04342         // Uncomment this part if 'create' permissions should become implied 'edit'.
04343         // Merges in 'create' policies with 'edit'
04344         if ( $functionName == 'edit' &&
04345              !in_array( $accessWord, array( 'yes', 'no' ) ) )
04346         {
04347             // Add in create policies.
04348             $accessExtraResult = $user->hasAccessTo( 'content', 'create' );
04349             if ( $accessExtraResult['accessWord'] != 'no' )
04350             {
04351                 $accessWord = $accessExtraResult['accessWord'];
04352                 if ( isset( $accessExtraResult['policies'] ) )
04353                 {
04354                     $accessResult['policies'] = array_merge( $accessResult['policies'],
04355                                                              $accessExtraResult['policies'] );
04356                 }
04357                 if ( isset( $accessExtraResult['accessList'] ) )
04358                 {
04359                     $accessResult['accessList'] = array_merge( $accessResult['accessList'],
04360                                                                $accessExtraResult['accessList'] );
04361                 }
04362             }
04363         }
04364         */
04365 
04366         if ( $origFunctionName == 'remove' or
04367              $origFunctionName == 'move' or
04368              $origFunctionName == 'can_remove_location' )
04369         {
04370             // We do not allow these actions on top-level nodes
04371             // - remove
04372             // - move
04373             if ( $this->ParentNodeID <= 1 )
04374             {
04375                 return 0;
04376             }
04377         }
04378 
04379         if ( $classID === false )
04380         {
04381             $classID = $contentObject->attribute( 'contentclass_id' );
04382         }
04383         if ( $accessWord == 'yes' )
04384         {
04385             return 1;
04386         }
04387         else if ( $accessWord == 'no' )
04388         {
04389             if ( $functionName == 'edit' )
04390             {
04391                 // Check if we have 'create' access under the main parent
04392                 $object = $currentNode->object();
04393                 if ( $object && $object->attribute( 'current_version' ) == 1 && !$object->attribute( 'status' ) )
04394                 {
04395                     $mainNode = eZNodeAssignment::fetchForObject( $object->attribute( 'id' ), $object->attribute( 'current_version' ) );
04396                     $parentObj = $mainNode[0]->attribute( 'parent_contentobject' );
04397                     $result = $parentObj->checkAccess( 'create', $object->attribute( 'contentclass_id' ),
04398                                                        $parentObj->attribute( 'contentclass_id' ), false, $originalLanguage );
04399                     return $result;
04400                 }
04401                 else
04402                 {
04403                     return 0;
04404                 }
04405             }
04406 
04407             return 0;
04408         }
04409         else
04410         {
04411             $policies = $accessResult['policies'];
04412             $access = 'denied';
04413 
04414             foreach ( $policies as $pkey => $limitationArray )
04415             {
04416                 if ( $access == 'allowed' )
04417                 {
04418                     break;
04419                 }
04420 
04421                 $limitationList = array();
04422                 if ( isset( $limitationArray['Subtree' ] ) )
04423                 {
04424                     $checkedSubtree = false;
04425                 }
04426                 else
04427                 {
04428                     $checkedSubtree = true;
04429                     $accessSubtree = false;
04430                 }
04431                 if ( isset( $limitationArray['Node'] ) )
04432                 {
04433                     $checkedNode = false;
04434                 }
04435                 else
04436                 {
04437                     $checkedNode = true;
04438                     $accessNode = false;
04439                 }
04440                 foreach ( $limitationArray as $key => $valueList  )
04441                 {
04442                     $access = 'denied';
04443                     switch( $key )
04444                     {
04445                         case 'Class':
04446                         {
04447                             if ( $functionName == 'create' and
04448                                  !$originalClassID )
04449                             {
04450                                 $access = 'allowed';
04451                             }
04452                             else if ( $functionName == 'create' and
04453                                       in_array( $classID, $valueList ) )
04454                             {
04455                                 $access = 'allowed';
04456                             }
04457                             else if ( $functionName != 'create' and
04458                                       in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) )
04459                             {
04460                                 $access = 'allowed';
04461                             }
04462                             else
04463                             {
04464                                 $access = 'denied';
04465                                 $limitationList = array( 'Limitation' => $key,
04466                                                          'Required' => $valueList );
04467                             }
04468                         } break;
04469 
04470                         case 'ParentClass':
04471                         {
04472                             if (  in_array( $contentObject->attribute( 'contentclass_id' ), $valueList ) )
04473                             {
04474                                 $access = 'allowed';
04475                             }
04476                             else
04477                             {
04478                                 $access = 'denied';
04479                                 $limitationList = array( 'Limitation' => $key,
04480                                                          'Required' => $valueList );
04481                             }
04482                         } break;
04483 
04484                         case 'Section':
04485                         case 'User_Section':
04486                         {
04487                             if ( in_array( $contentObject->attribute( 'section_id' ), $valueList ) )
04488                             {
04489                                 $access = 'allowed';
04490                             }
04491                             else
04492                             {
04493                                 $access = 'denied';
04494                                 $limitationList = array( 'Limitation' => $key,
04495                                                          'Required' => $valueList );
04496                             }
04497                         } break;
04498 
04499                         case 'Language':
04500                         {
04501                             $languageMask = 0;
04502                             // If we don't have a language list yet we need to fetch it
04503                             // and optionally filter out based on $language.
04504                             if ( $functionName == 'create' )
04505                             {
04506                                 // If the function is 'create' we do not use the language_mask for matching.
04507                                 if ( $language !== false )
04508                                 {
04509                                     $languageMask = $language;
04510                                 }
04511                                 else
04512                                 {
04513                                     // If the create is used and no language specified then
04514                                     // we need to match against all possible languages (which
04515                                     // is all bits set, ie. -1).
04516                                     $languageMask = -1;
04517                                 }
04518                             }
04519                             else
04520                             {
04521                                 if ( $language !== false )
04522                                 {
04523                                     if ( $languageList === false )
04524                                     {
04525                                         $languageMask = $contentObject->attribute( 'language_mask' );
04526                                         // We are restricting language check to just one language
04527                                         $languageMask &= $language;
04528                                         // If the resulting mask is 0 it means that the user is trying to
04529                                         // edit a language which does not exist, ie. translating.
04530                                         // The mask will then become the language trying to edit.
04531                                         if ( $languageMask == 0 )
04532                                         {
04533                                             $languageMask = $language;
04534                                         }
04535                                     }
04536                                 }
04537                                 else
04538                                 {
04539                                     $languageMask = -1;
04540                                 }
04541                             }
04542                             // Fetch limit mask for limitation list
04543                             $limitMask = eZContentLanguage::maskByLocale( $valueList );
04544                             if ( ( $languageMask & $limitMask ) != 0 )
04545                             {
04546                                 $access = 'allowed';
04547                             }
04548                             else
04549                             {
04550                                 $access = 'denied';
04551                                 $limitationList = array( 'Limitation' => $key,
04552                                                          'Required' => $valueList );
04553                             }
04554                         } break;
04555 
04556                         case 'Owner':
04557                         case 'ParentOwner':
04558                         {
04559                             // if limitation value == 2, anonymous limited to current session.
04560                             if ( in_array( 2, $valueList ) &&
04561                                  $user->isAnonymous() )
04562                             {
04563                                 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
04564                                 if ( $createdObjectIDList &&
04565                                      in_array( $contentObject->attribute( 'id' ), unserialize( $createdObjectIDList ) ) )
04566                                 {
04567                                     $access = 'allowed';
04568                                 }
04569                             }
04570                             else if ( $contentObject->attribute( 'owner_id' ) == $userID || $contentObject->attribute( 'id' ) == $userID )
04571                             {
04572                                 $access = 'allowed';
04573                             }
04574                             if ( $access != 'allowed' )
04575                             {
04576                                 $access = 'denied';
04577                                 $limitationList = array ( 'Limitation' => $key );
04578                             }
04579                         } break;
04580 
04581                         case 'Group':
04582                         case 'ParentGroup':
04583                         {
04584                             $access = $contentObject->checkGroupLimitationAccess( $valueList, $userID );
04585 
04586                             if ( $access != 'allowed' )
04587                             {
04588                                 $access = 'denied';
04589                                 $limitationList = array ( 'Limitation' => $key,
04590                                                           'Required' => $valueList );
04591                             }
04592                         } break;
04593 
04594                         case 'State':
04595                         {
04596                             if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 )
04597                             {
04598                                 $access = 'denied';
04599                                 $limitationList = array ( 'Limitation' => $key,
04600                                                           'Required' => $valueList );
04601                             }
04602                             else
04603                             {
04604                                 $access = 'allowed';
04605                             }
04606                         } break;
04607 
04608                         case 'ParentDepth':
04609                         {
04610                             if ( in_array( $currentNode->attribute( 'depth' ), $valueList ) )
04611                             {
04612                                 $access = 'allowed';
04613                             }
04614                             else
04615                             {
04616                                 $access = 'denied';
04617                                 $limitationList = array( 'Limitation' => $key,
04618                                                          'Required' => $valueList );
04619                             }
04620                         } break;
04621 
04622                         case 'Node':
04623                         {
04624                             $accessNode = false;
04625                             $mainNodeID = $currentNode->attribute( 'main_node_id' );
04626                             foreach ( $valueList as $nodeID )
04627                             {
04628                                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
04629                                 $limitationNodeID = $node['main_node_id'];
04630                                 if ( $mainNodeID == $limitationNodeID )
04631                                 {
04632                                     $access = 'allowed';
04633                                     $accessNode = true;
04634                                     break;
04635                                 }
04636                             }
04637                             if ( $access != 'allowed' && $checkedSubtree && !$accessSubtree )
04638                             {
04639                                 $access = 'denied';
04640                                 // ??? TODO: if there is a limitation on Subtree, return two limitations?
04641                                 $limitationList = array( 'Limitation' => $key,
04642                                                          'Required' => $valueList );
04643                             }
04644                             else
04645                             {
04646                                 $access = 'allowed';
04647                             }
04648                             $checkedNode = true;
04649                         } break;
04650 
04651                         case 'Subtree':
04652                         {
04653                             $accessSubtree = false;
04654                             $path = $currentNode->attribute( 'path_string' );
04655                             $subtreeArray = $valueList;
04656                             foreach ( $subtreeArray as $subtreeString )
04657                             {
04658                                 if ( strstr( $path, $subtreeString ) )
04659                                 {
04660                                     $access = 'allowed';
04661                                     $accessSubtree = true;
04662                                     break;
04663                                 }
04664                             }
04665                             if ( $access != 'allowed' && $checkedNode && !$accessNode )
04666                             {
04667                                 $access = 'denied';
04668                                 // ??? TODO: if there is a limitation on Node, return two limitations?
04669                                 $limitationList = array( 'Limitation' => $key,
04670                                                          'Required' => $valueList );
04671                             }
04672                             else
04673                             {
04674                                 $access = 'allowed';
04675                             }
04676                             $checkedSubtree = true;
04677                         } break;
04678 
04679                         case 'User_Subtree':
04680                         {
04681                             $path = $currentNode->attribute( 'path_string' );
04682                             $subtreeArray = $valueList;
04683                             foreach ( $subtreeArray as $subtreeString )
04684                             {
04685                                 if ( strstr( $path, $subtreeString ) )
04686                                 {
04687                                     $access = 'allowed';
04688                                 }
04689                             }
04690                             if ( $access != 'allowed' )
04691                             {
04692                                 $access = 'denied';
04693                                 $limitationList = array( 'Limitation' => $key,
04694                                                          'Required' => $valueList );
04695                             }
04696                         } break;
04697 
04698                         default:
04699                         {
04700                             if ( strncmp( $key, 'StateGroup_', 11 ) === 0 )
04701                             {
04702                                 if ( count( array_intersect( $valueList, $contentObject->attribute( 'state_id_array' ) ) ) == 0 )
04703                                 {
04704                                     $access = 'denied';
04705                                     $limitationList = array ( 'Limitation' => $key,
04706                                                               'Required' => $valueList );
04707                                 }
04708                                 else
04709                                 {
04710                                     $access = 'allowed';
04711                                 }
04712                             }
04713                         }
04714                     }
04715 
04716                     if ( $access == 'denied' )
04717                     {
04718                         break;
04719                     }
04720                 }
04721 
04722                 $policyList[] = array( 'PolicyID' => $pkey,
04723                                        'LimitationList' => $limitationList );
04724             }
04725             if ( $access == 'denied' )
04726             {
04727                 $accessList = array( 'FunctionRequired' => array ( 'Module' => 'content',
04728                                                                    'Function' => $origFunctionName,
04729                                                                    'ClassID' => $classID,
04730                                                                    'MainNodeID' => $currentNode->attribute( 'main_node_id' ) ),
04731                                      'PolicyList' => $policyList );
04732                 return 0;
04733             }
04734             else
04735             {
04736                 return 1;
04737             }
04738         }
04739     }
04740 
04741     // code-template::create-block: class-list-from-policy, is-node
04742     // code-template::auto-generated:START class-list-from-policy
04743     // This code is automatically generated from templates/classlistfrompolicy.ctpl
04744     // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
04745 
04746     function classListFromPolicy( $policy, $allowedLanguageCodes = false )
04747     {
04748         $canCreateClassIDListPart = array();
04749         $hasClassIDLimitation = false;
04750         $user = eZUser::currentUser();
04751         $userID = $user->attribute( 'contentobject_id' );
04752 
04753         $object = false;
04754         if ( isset( $policy['ParentOwner'] ) )
04755         {
04756             if ( $object === false )
04757                 $object = $this->attribute( 'object' );
04758 
04759             // if limitation value == 2, anonymous limited to current session.
04760             if ( in_array( 2, $policy['ParentOwner'] ) && $user->isAnonymous() )
04761             {
04762                 $createdObjectIDList = eZPreferences::value( 'ObjectCreationIDList' );
04763                 if ( !$createdObjectIDList ||
04764                      !in_array( $object->ID, unserialize( $createdObjectIDList ) ) )
04765                 {
04766                     return array();
04767                 }
04768             }
04769             else if ( $object->attribute( 'owner_id' ) != $userID &&
04770                       $object->ID != $userID )
04771             {
04772                 return array();
04773             }
04774         }
04775 
04776         if ( isset( $policy['ParentGroup'] ) )
04777         {
04778             if ( $object === false )
04779                 $object = $this->attribute( 'object' );
04780 
04781             $access = $object->checkGroupLimitationAccess( $policy['ParentGroup'], $userID );
04782             if ( $access !== 'allowed' )
04783             {
04784                 return array();
04785             }
04786         }
04787 
04788         if ( isset( $policy['Class'] ) )
04789         {
04790             $canCreateClassIDListPart = $policy['Class'];
04791             $hasClassIDLimitation = true;
04792         }
04793 
04794         if ( isset( $policy['User_Section'] ) )
04795         {
04796             if ( $object === false )
04797                 $object = $this->attribute( 'object' );
04798 
04799             if ( !in_array( $object->attribute( 'section_id' ), $policy['User_Section']  ) )
04800             {
04801                 return array();
04802             }
04803         }
04804 
04805         if ( isset( $policy['User_Subtree'] ) )
04806         {
04807             $allowed = false;
04808             if ( $object === false )
04809                 $object = $this->attribute( 'object' );
04810 
04811             $assignedNodes = $object->attribute( 'assigned_nodes' );
04812             foreach ( $assignedNodes as $assignedNode )
04813             {
04814                 $path = $assignedNode->attribute( 'path_string' );
04815                 foreach ( $policy['User_Subtree'] as $subtreeString )
04816                 {
04817                     if ( strstr( $path, $subtreeString ) )
04818                     {
04819                         $allowed = true;
04820                         break;
04821                     }
04822                 }
04823             }
04824             if( !$allowed )
04825             {
04826                 return array();
04827             }
04828         }
04829 
04830         if ( isset( $policy['Section'] ) )
04831         {
04832             if ( $object === false )
04833                 $object = $this->attribute( 'object' );
04834 
04835             if ( !in_array( $object->attribute( 'section_id' ), $policy['Section']  ) )
04836             {
04837                 return array();
04838             }
04839         }
04840 
04841         if ( isset( $policy['ParentClass'] ) )
04842         {
04843             if ( $object === false )
04844                 $object = $this->attribute( 'object' );
04845 
04846             if ( !in_array( $object->attribute( 'contentclass_id' ), $policy['ParentClass']  ) )
04847             {
04848                 return array();
04849             }
04850         }
04851 
04852         if ( isset( $policy['ParentDepth'] ) && is_array( $policy['ParentDepth'] ) )
04853         {
04854             $NodeDepth = $this->attribute( 'depth' );
04855             if ( !in_array( '*', $policy['ParentDepth'] ) && !in_array( $NodeDepth, $policy['ParentDepth'] ) )
04856             {
04857                 return array();
04858             }
04859         }
04860 
04861         if ( isset( $policy['Assigned'] ) )
04862         {
04863             if ( $object === false )
04864                 $object = $this->attribute( 'object' );
04865 
04866             if ( $object->attribute( 'owner_id' ) != $userID )
04867             {
04868                 return array();
04869             }
04870         }
04871 
04872         $allowedNode = false;
04873         if ( isset( $policy['Node'] ) )
04874         {
04875             $allowed = false;
04876             foreach( $policy['Node'] as $nodeID )
04877             {
04878                 $mainNodeID = $this->attribute( 'main_node_id' );
04879                 $node = eZContentObjectTreeNode::fetch( $nodeID, false, false );
04880                 if ( $mainNodeID == $node['main_node_id'] )
04881                 {
04882                     $allowed = true;
04883                     $allowedNode = true;
04884                     break;
04885                 }
04886             }
04887             if ( !$allowed && !isset( $policy['Subtree'] ) )
04888             {
04889                 return array();
04890             }
04891         }
04892 
04893         if ( isset( $policy['Subtree'] ) )
04894         {
04895             $allowed = false;
04896             if ( $object === false )
04897                 $object = $this->attribute( 'object' );
04898             $assignedNodes = $object->attribute( 'assigned_nodes' );
04899             foreach ( $assignedNodes as $assignedNode )
04900             {
04901                 $path = $assignedNode->attribute( 'path_string' );
04902                 foreach ( $policy['Subtree'] as $subtreeString )
04903                 {
04904                     if ( strstr( $path, $subtreeString ) )
04905                     {
04906                         $allowed = true;
04907                         break;
04908                     }
04909                 }
04910             }
04911             if ( !$allowed && !$allowedNode )
04912             {
04913                 return array();
04914             }
04915         }
04916 
04917         if ( isset( $policy['Language'] ) )
04918         {
04919             if ( $allowedLanguageCodes )
04920             {
04921                 $allowedLanguageCodes = array_intersect( $allowedLanguageCodes, $policy['Language'] );
04922             }
04923             else
04924             {
04925                 $allowedLanguageCodes = $policy['Language'];
04926             }
04927         }
04928 
04929         if ( $hasClassIDLimitation )
04930         {
04931             return array( 'classes' => $canCreateClassIDListPart, 'language_codes' => $allowedLanguageCodes );
04932         }
04933         return array( 'classes' => '*', 'language_codes' => $allowedLanguageCodes );
04934     }
04935 
04936     // This code is automatically generated from templates/classlistfrompolicy.ctpl
04937     // code-template::auto-generated:END class-list-from-policy
04938 
04939     // code-template::create-block: can-instantiate-class-list, group-filter, object-policy-list, name-create, object-creation, object-sql-creation, current-subtree-limitation
04940     // code-template::auto-generated:START can-instantiate-class-list
04941     // This code is automatically generated from templates/classcreatelist.ctpl
04942     // DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD
04943 
04944     /**
04945      * Checks if provided policy array has a limitation on current subtree
04946      * @param array $policy
04947      * @return bool
04948      */
04949     protected function hasCurrentSubtreeLimitation( array $policy )
04950     {
04951         // No Subtree nor Node limitation, so we consider that it is potentially OK to create content under current subtree
04952         if ( !isset( $policy['Subtree'] ) && !isset( $policy['Node'] ) )
04953         {
04954             return true;
04955         }
04956 
04957         // First check subtree limitations
04958         if ( isset( $policy['Subtree'] ) )
04959         {
04960             foreach ( $policy['Subtree'] as $subtreeString )
04961             {
04962                 if ( strpos( $this->attribute( 'path_string' ), $subtreeString ) !== false )
04963                 {
04964                     return true;
04965                 }
04966             }
04967         }
04968 
04969         // Then check node limitations
04970         if ( isset( $policy['Node'] ) )
04971         {
04972             foreach ( $policy['Node'] as $subtreeString )
04973             {
04974                 if ( strpos( $this->attribute( 'path_string' ), $subtreeString ) !== false )
04975                 {
04976                     return true;
04977                 }
04978             }
04979         }
04980 
04981         return false;
04982     }
04983 
04984     /*!
04985      Finds all classes that the current user can create objects from and returns.
04986      It is also possible to filter the list event more with \a $includeFilter and \a $groupList.
04987 
04988      \param $asObject If \c true then it return eZContentClass objects, if not it will
04989                       be an associative array with \c name and \c id keys.
04990      \param $includeFilter If \c true then it will include only from class groups defined in
04991                            \a $groupList, if not it will exclude those groups.
04992      \param $groupList An array with class group IDs that should be used in filtering, use
04993                        \c false if you do not wish to filter at all.
04994      \param $fetchID A unique name for the current fetch, this must be supplied when filtering is
04995                      used if you want caching to work.
04996     */
04997     function canCreateClassList( $asObject = false, $includeFilter = true, $groupList = false, $fetchID = false )
04998     {
04999         $ini = eZINI::instance();
05000         $groupArray = array();
05001         $languageCodeList = eZContentLanguage::fetchLocaleList();
05002         $allowedLanguages = array( '*' => array() );
05003 
05004         $user = eZUser::currentUser();
05005         $accessResult = $user->hasAccessTo( 'content' , 'create' );
05006         $accessWord = $accessResult['accessWord'];
05007 
05008         $classIDArray = array();
05009         $classList = array();
05010         $fetchAll = false;
05011         if ( $accessWord == 'yes' )
05012         {
05013             $fetchAll = true;
05014             $allowedLanguages['*'] = $languageCodeList;
05015         }
05016         else if ( $accessWord == 'no' )
05017         {
05018             // Cannot create any objects, return empty list.
05019             return $classList;
05020         }
05021         else
05022         {
05023             $policies = $accessResult['policies'];
05024             foreach ( $policies as $policyKey => $policy )
05025             {
05026                 $policyArray = $this->classListFromPolicy( $policy, $languageCodeList );
05027                 if ( empty( $policyArray ) )
05028                 {
05029                     continue;
05030                 }
05031                 $classIDArrayPart = $policyArray['classes'];
05032                 $languageCodeArrayPart = $policyArray['language_codes'];
05033                 // No class limitation for this policy AND no previous limitation(s)
05034                 if ( $classIDArrayPart == '*' && empty( $classIDArray ) )
05035                 {
05036                     $fetchAll = true;
05037                     $allowedLanguages['*'] = array_unique( array_merge( $allowedLanguages['*'], $languageCodeArrayPart ) );
05038                 }
05039                 else if ( is_array( $classIDArrayPart ) && $this->hasCurrentSubtreeLimitation( $policy ) )
05040                 {
05041                     $fetchAll = false;
05042                     foreach( $classIDArrayPart as $class )
05043                     {
05044                         if ( isset( $allowedLanguages[$class] ) )
05045                         {
05046                             $allowedLanguages[$class] = array_unique( array_merge( $allowedLanguages[$class], $languageCodeArrayPart ) );
05047                         }
05048                         else
05049                         {
05050                             $allowedLanguages[$class] = $languageCodeArrayPart;
05051                         }
05052                     }
05053                     $classIDArray = array_merge( $classIDArray, array_diff( $classIDArrayPart, $classIDArray ) );
05054                 }
05055             }
05056         }
05057 
05058         $db = eZDB::instance();
05059 
05060         $filterTableSQL = '';
05061         $filterSQL = '';
05062         // Create extra SQL statements for the class group filters.
05063         if ( is_array( $groupList ) )
05064         {
05065             if ( count( $groupList ) == 0 )
05066             {
05067                 return $classList;
05068             }
05069 
05070             $filterTableSQL = ', ezcontentclass_classgroup ccg';
05071             $filterSQL = ( " AND" .
05072                            "      cc.id = ccg.contentclass_id AND" .
05073                            "      " );
05074             $filterSQL .= $db->generateSQLINStatement( $groupList, 'ccg.group_id', !$includeFilter, true, 'int' );
05075         }
05076 
05077         $classNameFilter = eZContentClassName::sqlFilter( 'cc' );
05078 
05079         if ( $fetchAll )
05080         {
05081             // If $asObject is true we fetch all fields in class
05082             $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
05083             $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
05084                                      "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
05085                                      "WHERE cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
05086                                      "ORDER BY $classNameFilter[nameField] ASC" );
05087             $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
05088         }
05089         else
05090         {
05091             // If the constrained class list is empty we are not allowed to create any class
05092             if ( count( $classIDArray ) == 0 )
05093             {
05094                 return $classList;
05095             }
05096 
05097             $classIDCondition = $db->generateSQLINStatement( $classIDArray, 'cc.id' );
05098             // If $asObject is true we fetch all fields in class
05099             $fields = $asObject ? "cc.*, $classNameFilter[nameField]" : "cc.id, $classNameFilter[nameField]";
05100             $rows = $db->arrayQuery( "SELECT DISTINCT $fields " .
05101                                      "FROM ezcontentclass cc$filterTableSQL, $classNameFilter[from] " .
05102                                      "WHERE $classIDCondition AND" .
05103                                      "      cc.version = " . eZContentClass::VERSION_STATUS_DEFINED . " $filterSQL AND $classNameFilter[where] " .
05104                                      "ORDER BY $classNameFilter[nameField] ASC" );
05105             $classList = eZPersistentObject::handleRows( $rows, 'eZContentClass', $asObject );
05106         }
05107 
05108         if ( $asObject )
05109         {
05110             foreach ( $classList as $key => $class )
05111             {
05112                 $id = $class->attribute( 'id' );
05113                 if ( isset( $allowedLanguages[$id] ) )
05114                 {
05115                     $languageCodes = array_unique( array_merge( $allowedLanguages['*'], $allowedLanguages[$id] ) );
05116                 }
05117                 else
05118                 {
05119                     $languageCodes = $allowedLanguages['*'];
05120                 }
05121                 $classList[$key]->setCanInstantiateLanguages( $languageCodes );
05122             }
05123         }
05124 
05125         eZDebugSetting::writeDebug( 'kernel-content-class', $classList, "class list fetched from db" );
05126         return $classList;
05127     }
05128 
05129     // This code is automatically generated from templates/classcreatelist.ctpl
05130     // code-template::auto-generated:END can-instantiate-class-list
05131 
05132     static function makeObjectsArray( $array , $with_contentobject = true )
05133     {
05134         $retNodes = array();
05135         if ( !is_array( $array ) )
05136             return $retNodes;
05137 
05138         foreach ( $array as $node )
05139         {
05140             unset( $object );
05141 
05142             if( $node['node_id'] == 1 )
05143             {
05144                 if( !array_key_exists( 'name', $node ) || !$node['name'] )
05145                     $node['name'] = ezpI18n::tr( 'kernel/content', 'Top Level Nodes' );
05146             }
05147 
05148             $object = new eZContentObjectTreeNode( $node );
05149             // If the name is not set it will be fetched later on when
05150             // getName()/attribute( 'name' ) is accessed.
05151             if ( isset( $node['name'] ) )
05152             {
05153                 $object->setName( $node['name'] );
05154             }
05155 
05156             if ( isset( $node['class_serialized_name_list'] ) )
05157             {
05158                 $node['class_name'] = eZContentClass::nameFromSerializedString( $node['class_serialized_name_list'] );
05159                 $object->ClassName = $node['class_name'];
05160             }
05161             if ( isset( $node['class_identifier'] ) )
05162                 $object->ClassIdentifier = $node['class_identifier'];
05163 
05164             if ( isset( $node['is_container'] ) )
05165                 $object->ClassIsContainer = $node['is_container'];
05166 
05167             if ( $with_contentobject )
05168             {
05169                 if ( isset( $node['class_name'] ) )
05170                 {
05171                     unset( $node['remote_id'] );
05172                     $contentObject = new eZContentObject( $node );
05173 
05174                     $permissions = array();
05175                     $contentObject->setPermissions( $permissions );
05176                     $contentObject->setClassName( $node['class_name'] );
05177                     if ( isset( $node['class_identifier'] ) )
05178                         $contentObject->ClassIdentifier = $node['class_identifier'];
05179 
05180                 }
05181                 else
05182                 {
05183                     $contentObject = new eZContentObject( array());
05184                     if ( isset( $node['name'] ) )
05185                          $contentObject->setCachedName( $node['name'] );
05186                 }
05187                 if ( isset( $node['real_translation'] ) && $node['real_translation'] != '' )
05188                 {
05189                     $object->CurrentLanguage = $node['real_translation'];
05190                     $contentObject->CurrentLanguage = $node['real_translation'];
05191                 }
05192                 if ( $node['node_id'] == 1 )
05193                 {
05194                     $contentObject->ClassName = 'Folder';
05195                     $contentObject->ClassIdentifier = 'folder';
05196                     $contentObject->ClassID = 1;
05197                     $contentObject->SectionID = 1;
05198                 }
05199 
05200                 $object->setContentObject( $contentObject );
05201             }
05202             $retNodes[] = $object;
05203         }
05204         return $retNodes;
05205     }
05206 
05207     /*!
05208      Get parent node id by node id
05209      \param $nodeID the node id you want parent node id for.
05210      */
05211     static function getParentNodeId( $nodeID )
05212     {
05213         if ( !isset( $nodeID ) )
05214             return null;
05215         $db = eZDB::instance();
05216         $nodeID =(int) $nodeID;
05217         $parentArr = $db->arrayQuery( "SELECT
05218                                               parent_node_id
05219                                        FROM
05220                                               ezcontentobject_tree
05221                                        WHERE
05222                                               node_id = $nodeID");
05223         return $parentArr[0]['parent_node_id'];
05224     }
05225 
05226     /**
05227      * Get parent node id's by content object id's.
05228      *
05229      * @static
05230      * @since Version 4.1
05231      * @param int|array $objectIDs
05232      * @param bool $groupByObjectId groups parent node ids by object id they belong to.
05233      * @param bool $onlyMainNode limits result to parent node id of main node.
05234      *
05235      * @return array Returns array of parent node id's
05236      */
05237     static function getParentNodeIdListByContentObjectID( $objectIDs, $groupByObjectId = false, $onlyMainNode = false )
05238     {
05239         if ( !$objectIDs )
05240             return null;
05241         if ( !is_array( $objectIDs ) )
05242             $objectIDs = array( $objectIDs );
05243 
05244         $db = eZDB::instance();
05245         $query = 'SELECT
05246                     parent_node_id, contentobject_id
05247                   FROM
05248                     ezcontentobject_tree
05249                   WHERE ' . $db->generateSQLINStatement( $objectIDs, 'contentobject_id', false, false, 'int' );
05250 
05251         if ( $onlyMainNode )
05252         {
05253             $query .= ' AND node_id = main_node_id';
05254         }
05255 
05256         $list = $db->arrayQuery( $query );
05257         $parentNodeIDs = array();
05258         if ( $groupByObjectId )
05259         {
05260             foreach( $list as $item )
05261             {
05262                 $objectID = $item['contentobject_id'];
05263                 if ( !isset( $parentNodeIDs[$objectID] ) )
05264                 {
05265                     $parentNodeIDs[$objectID] = array();
05266                 }
05267                 $parentNodeIDs[$objectID][] = $item['parent_node_id'];
05268             }
05269         }
05270         else
05271         {
05272             foreach( $list as $item )
05273             {
05274                 $parentNodeIDs[] = $item['parent_node_id'];
05275             }
05276         }
05277         return $parentNodeIDs;
05278     }
05279 
05280     /*!
05281      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05282      the calls within a db transaction; thus within db->begin and db->commit.
05283      */
05284     static function deleteNodeWhereParent( $node, $id )
05285     {
05286         eZContentObjectTreeNode::removeNode( eZContentObjectTreeNode::findNode( $node, $id ) );
05287 
05288     }
05289 
05290     static function findNode( $parentNode, $id, $asObject = false, $remoteID = false )
05291     {
05292         if ( !isset( $parentNode) || $parentNode == NULL  )
05293         {
05294             $parentNode = 2;
05295         }
05296         $parentNode =(int) $parentNode;
05297         $db = eZDB::instance();
05298         if( $asObject )
05299         {
05300             if ( $remoteID )
05301             {
05302                 $objectIDFilter = 'ezcontentobject.remote_id = ' . (string) $id;
05303             }
05304             else
05305             {
05306                 $objectIDFilter = 'contentobject_id = ' . (int) $id;
05307             }
05308 
05309             $query="SELECT ezcontentobject.*,
05310                        ezcontentobject_tree.*,
05311                        ezcontentclass.serialized_name_list as class_serialized_name_list,
05312                        ezcontentclass.identifier as class_identifier,
05313                        ezcontentclass.is_container as is_container
05314                 FROM ezcontentobject_tree,
05315                      ezcontentobject,
05316                      ezcontentclass
05317                 WHERE parent_node_id = $parentNode AND
05318                       $objectIDFilter AND
05319                       ezcontentobject_tree.contentobject_id=ezcontentobject.id AND
05320                       ezcontentclass.version=0  AND
05321                       ezcontentclass.id = ezcontentobject.contentclass_id ";
05322 
05323             $nodeListArray = $db->arrayQuery( $query );
05324             $retNodeArray = eZContentObjectTreeNode::makeObjectsArray( $nodeListArray );
05325 
05326             if ( count( $retNodeArray ) > 0 )
05327             {
05328                 return $retNodeArray[0];
05329             }
05330             else
05331             {
05332                 return null;
05333             }
05334         }
05335         else
05336         {
05337             $id = (int) $id;
05338             $getNodeQuery = "SELECT node_id
05339                            FROM ezcontentobject_tree
05340                            WHERE
05341                                 parent_node_id=$parentNode AND
05342                                 contentobject_id = $id ";
05343             $nodeArr = $db->arrayQuery( $getNodeQuery );
05344             if ( isset( $nodeArr[0] ) )
05345             {
05346                 return $nodeArr[0]['node_id'];
05347             }
05348             else
05349             {
05350                 return false;
05351             }
05352         }
05353     }
05354 
05355     function getName( $language = false )
05356     {
05357         // If the name is not set yet we fetch it from the object table
05358         if ( $this->Name === null || $language !== false )
05359         {
05360             if ( $this->CurrentLanguage || $language !== false )
05361             {
05362                 $sql = "SELECT name FROM ezcontentobject_name WHERE contentobject_id=" . (int) $this->ContentObjectID . " AND content_version=" . (int)$this->attribute( 'contentobject_version' ) . " AND real_translation='" . ( $language !== false ? $language : $this->CurrentLanguage ) . "'";
05363             }
05364             else
05365             {
05366                 $sql = "SELECT name FROM ezcontentobject WHERE id=" . (int) $this->ContentObjectID;
05367             }
05368             $db = eZDB::instance();
05369             $rows = $db->arrayQuery( $sql );
05370             if ( count( $rows ) > 0 )
05371             {
05372                 if ( $language !== false )
05373                 {
05374                     return $rows[0]['name'];
05375                 }
05376                 $this->Name = $rows[0]['name'];
05377             }
05378             else
05379             {
05380                 if ( $language !== false )
05381                 {
05382                     return false;
05383                 }
05384                 $this->Name = false;
05385             }
05386         }
05387         return $this->Name;
05388     }
05389 
05390     function setName( $name )
05391     {
05392         $this->Name = $name;
05393     }
05394 
05395     /*!
05396      \static
05397      Creates propper nodeassigment from contentNodeDOMNode specification
05398 
05399      \param contentobjecttreenode DOMNode
05400      \param contentobject.
05401      \param version
05402      \param isMain
05403      \param options
05404 
05405      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05406      the calls within a db transaction; thus within db->begin and db->commit.
05407     */
05408     static function unserialize( $contentNodeDOMNode, $contentObject, $version, $isMain, &$nodeList, &$options, $handlerType = 'ezcontentobject' )
05409     {
05410         $parentNodeID = -1;
05411 
05412         $remoteID = $contentNodeDOMNode->getAttribute( 'remote-id' );
05413         $parentNodeRemoteID = $contentNodeDOMNode->getAttribute( 'parent-node-remote-id' );
05414         $node = eZContentObjectTreeNode::fetchByRemoteID( $remoteID );
05415         if ( is_object( $node ) )
05416         {
05417             $description = "Node with remote ID $remoteID already exists.";
05418 
05419             $choosenAction = eZPackageHandler::errorChoosenAction( eZContentObject::PACKAGE_ERROR_EXISTS,
05420                                                                    $options, $description, $handlerType, false );
05421 
05422             switch( $choosenAction )
05423             {
05424                 // In case user have choosen "Keep existing object and create new"
05425                 case eZContentObject::PACKAGE_NEW:
05426                 {
05427                     $newRemoteID = eZRemoteIdUtility::generate( 'node' );
05428                     $node->setAttribute( 'remote_id', $newRemoteID );
05429                     $node->store();
05430                     $nodeInfo = array( 'contentobject_id' =>  $node->attribute( 'contentobject_id' ),
05431                                        'contentobject_version' => $node->attribute( 'contentobject_version' ),
05432                                        'parent_remote_id' => $remoteID );
05433                     $nodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(),
05434                                                                        null,
05435                                                                        $nodeInfo );
05436                     if ( is_object( $nodeAssignment ) )
05437                     {
05438                         $nodeAssignment->setAttribute( 'parent_remote_id', $newRemoteID );
05439                         $nodeAssignment->store();
05440                     }
05441                 } break;
05442 
05443                 // When running non-interactively with ezpm.php
05444                 case eZPackage::NON_INTERACTIVE:
05445                 case eZContentObject::PACKAGE_UPDATE:
05446                 {
05447                     // Update existing node settigns.
05448                     if ( !$parentNodeRemoteID )
05449                     {
05450                         // when top node of subtree export, only update node sort field and sort order
05451                         $node->setAttribute( 'sort_field', eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ) );
05452                         $node->setAttribute( 'sort_order', $contentNodeDOMNode->getAttribute( 'sort-order' ) );
05453                         $node->store();
05454                         return true;
05455                     }
05456                 } break;
05457 
05458                 default:
05459                 {
05460                     // This error may occur only if data integrity is broken
05461                     $options['error'] = array( 'error_code' => eZContentObject::PACKAGE_ERROR_NODE_EXISTS,
05462                                                'element_id' => $remoteID,
05463                                                'description' => $description );
05464                     return false;
05465                 } break;
05466             }
05467         }
05468 
05469         if ( $parentNodeRemoteID )
05470         {
05471             $parentNode = eZContentObjectTreeNode::fetchByRemoteID( $parentNodeRemoteID );
05472             if ( $parentNode !== null )
05473             {
05474                 $parentNodeID = $parentNode->attribute( 'node_id' );
05475             }
05476         }
05477         else
05478         {
05479             if ( isset( $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id'] ) )
05480             {
05481                 $parentNodeID = $options['top_nodes_map'][$contentNodeDOMNode->getAttribute( 'node-id' )]['new_node_id'];
05482             }
05483             else if ( isset( $options['top_nodes_map']['*'] ) )
05484             {
05485                 $parentNodeID = $options['top_nodes_map']['*'];
05486             }
05487             else
05488             {
05489                 eZDebug::writeError( 'New parent node not set ' . $contentNodeDOMNode->getAttribute( 'name' ), __METHOD__ );
05490             }
05491         }
05492 
05493         $isMain = ( $isMain && $contentNodeDOMNode->getAttribute( 'is-main-node' ) );
05494 
05495         $nodeInfo = array( 'contentobject_id' => $contentObject->attribute( 'id' ),
05496                            'contentobject_version' => $version,
05497                            'is_main' => $isMain,
05498                            'parent_node' => $parentNodeID,
05499                            'parent_remote_id' => $remoteID,     // meaning processed node remoteID (not parent)
05500                            'sort_field' => eZContentObjectTreeNode::sortFieldID( $contentNodeDOMNode->getAttribute( 'sort-field' ) ),
05501                            'sort_order' => $contentNodeDOMNode->getAttribute( 'sort-order' ) );
05502 
05503         if ( $parentNodeID == -1 && $parentNodeRemoteID )
05504         {
05505             if ( !isset( $options['suspended-nodes'] ) )
05506             {
05507                 $options['suspended-nodes'] = array();
05508             }
05509 
05510             $options['suspended-nodes'][$parentNodeRemoteID] = array( 'nodeinfo' => $nodeInfo,
05511                                                                       'priority' => $contentNodeDOMNode->getAttribute( 'priority' ) );
05512             return true;
05513         }
05514 
05515         $existNodeAssignment = eZPersistentObject::fetchObject( eZNodeAssignment::definition(),
05516                                                    null,
05517                                                    $nodeInfo );
05518         $nodeInfo['priority'] = $contentNodeDOMNode->getAttribute( 'priority' );
05519         if( !is_object( $existNodeAssignment ) )
05520         {
05521             $nodeAssignment = eZNodeAssignment::create( $nodeInfo );
05522             $nodeList[] = $nodeInfo;
05523             $nodeAssignment->store();
05524         }
05525 
05526         return true;
05527     }
05528 
05529     /*!
05530      Serialize ContentObjectTreeNode
05531 
05532      \params $options
05533      \params contentNodeIDArray
05534      \params topNodeIDArray
05535     */
05536     function serialize( $options, $contentNodeIDArray, $topNodeIDArray )
05537     {
05538         if ( $options['node_assignment'] == 'main' &&
05539              $this->attribute( 'main_node_id' ) != $this->attribute( 'node_id' ) )
05540         {
05541             return false;
05542         }
05543         if ( ! in_array( $this->attribute( 'node_id' ), array_keys( $contentNodeIDArray ) ) )
05544         {
05545             return false;
05546         }
05547 
05548         $dom = new DOMDocument( '1.0', 'utf-8' );
05549 
05550         $nodeAssignmentNode = $dom->createElement( 'node-assignment' );
05551         if ( $this->attribute( 'main_node_id' ) == $this->attribute( 'node_id' ) )
05552         {
05553             $nodeAssignmentNode->setAttribute( 'is-main-node', 1 );
05554         }
05555         if( !in_array( $this->attribute( 'node_id'), $topNodeIDArray ) )
05556         {
05557             $parentNode = $this->attribute( 'parent' );
05558             $nodeAssignmentNode->setAttribute( 'parent-node-remote-id', $parentNode->attribute( 'remote_id' ) );
05559         }
05560         $nodeAssignmentNode->setAttribute( 'name', $this->attribute( 'name' ) );
05561         $nodeAssignmentNode->setAttribute( 'node-id', $this->attribute( 'node_id' ) );
05562         $nodeAssignmentNode->setAttribute( 'remote-id', $this->attribute( 'remote_id' ) );
05563         $nodeAssignmentNode->setAttribute( 'sort-field', eZContentObjectTreeNode::sortFieldName( $this->attribute( 'sort_field' ) ) );
05564         $nodeAssignmentNode->setAttribute( 'sort-order', $this->attribute( 'sort_order' ) );
05565         $nodeAssignmentNode->setAttribute( 'priority', $this->attribute( 'priority' ) );
05566         return $nodeAssignmentNode;
05567     }
05568 
05569     /*!
05570      Update and store modified_subnode value for this node and all super nodes.
05571      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05572      the calls within a db transaction; thus within db->begin and db->commit.
05573     */
05574     function updateAndStoreModified()
05575     {
05576         $pathString = trim( $this->attribute( 'path_string' ), '/' );
05577 
05578         // during publishing, a temporary path is generated. The update query shouldn't be executed as it doesn't affect anything
05579         if ( $pathString == 'TEMPPATH' )
05580             return;
05581         $pathArray = explode( '/', $pathString );
05582 
05583         if ( count( $pathArray ) > 0 )
05584         {
05585             $db = eZDB::instance();
05586             $db->query( 'UPDATE ezcontentobject_tree SET modified_subnode=' . time() .
05587                         ' WHERE ' . $db->generateSQLINStatement( $pathArray, 'node_id', false, true, 'int' ) );
05588         }
05589     }
05590 
05591     /*!
05592      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05593      the calls within a db transaction; thus within db->begin and db->commit.
05594      */
05595     function store( $fieldFilters = null )
05596     {
05597         $db = eZDB::instance();
05598 
05599         $db->begin();
05600         eZPersistentObject::store( $fieldFilters );
05601         $this->updateAndStoreModified();
05602         $db->commit();
05603     }
05604 
05605     function object()
05606     {
05607         if ( $this->hasContentObject() )
05608         {
05609             return $this->ContentObject;
05610         }
05611         $contentobject_id = $this->attribute( 'contentobject_id' );
05612         $obj = eZContentObject::fetch( $contentobject_id );
05613         $obj->setCurrentLanguage( $this->CurrentLanguage );
05614         $this->ContentObject = $obj;
05615         return $obj;
05616     }
05617 
05618     function hasContentObject()
05619     {
05620         if ( isset( $this->ContentObject ) && $this->ContentObject instanceof eZContentObject )
05621             return true;
05622         else
05623             return false;
05624     }
05625 
05626     /*!
05627      Sets the current content object for this node.
05628     */
05629     function setContentObject( $object )
05630     {
05631         $this->ContentObject = $object;
05632     }
05633 
05634     /*!
05635      \return the creator of the version published in the node.
05636     */
05637     function creator()
05638     {
05639         $db = eZDB::instance();
05640         $query = "SELECT creator_id
05641                   FROM ezcontentobject_version
05642                   WHERE
05643                         contentobject_id = '$this->ContentObjectID' AND
05644                         version = '$this->ContentObjectVersion' ";
05645 
05646         $creatorArray = $db->arrayQuery( $query );
05647         return eZContentObject::fetch( $creatorArray[0]['creator_id'] );
05648     }
05649 
05650     function contentObjectVersionObject( $asObject = true )
05651     {
05652         $version = eZContentObjectVersion::fetchVersion( $this->ContentObjectVersion, $this->ContentObjectID, $asObject );
05653         if ( $this->CurrentLanguage != false )
05654         {
05655             $version->CurrentLanguage = $this->CurrentLanguage;
05656         }
05657         return $version;
05658     }
05659 
05660     function urlAlias()
05661     {
05662         $useURLAlias =& $GLOBALS['eZContentObjectTreeNodeUseURLAlias'];
05663         $ini = eZINI::instance();
05664         $cleanURL = '';
05665         if ( !isset( $useURLAlias ) )
05666         {
05667             $useURLAlias = $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled';
05668         }
05669         if ( $useURLAlias )
05670         {
05671             $path = $this->pathWithNames();
05672             if ( $ini->hasVariable( 'SiteAccessSettings', 'PathPrefix' ) &&
05673                  $ini->variable( 'SiteAccessSettings', 'PathPrefix' ) != '' )
05674             {
05675                 $prepend = $ini->variable( 'SiteAccessSettings', 'PathPrefix' );
05676                 $pathIdenStr = substr( $prepend, strlen( $prepend ) -1 ) == '/'
05677                                 ? $path . '/'
05678                                 : $path;
05679                 if ( strncasecmp( $pathIdenStr, $prepend, strlen( $prepend ) ) == 0 )
05680                     $cleanURL = eZURLAliasML::cleanURL( substr( $path, strlen( $prepend ) ) );
05681                 else
05682                     $cleanURL = eZURLAliasML::cleanURL( $path );
05683             }
05684             else
05685             {
05686                 $cleanURL = eZURLAliasML::cleanURL( $path );
05687             }
05688         }
05689         else
05690         {
05691             $cleanURL = eZURLAliasML::cleanURL( 'content/view/full/' . $this->NodeID );
05692         }
05693 
05694         return $cleanURL;
05695     }
05696 
05697     function url()
05698     {
05699         $ini = eZINI::instance();
05700         if ( $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled' )
05701         {
05702             return $this->urlAlias();
05703         }
05704         return 'content/view/full/' . $this->NodeID;
05705     }
05706 
05707 
05708     /*!
05709      \return the cached value of the class identifier if it exists, if not it's fetched dynamically
05710     */
05711     public function classIdentifier()
05712     {
05713         if ( $this->ClassIdentifier === null )
05714         {
05715             $object = $this->object();
05716             $this->ClassIdentifier = $object->contentClassIdentifier();
05717         }
05718 
05719         return $this->ClassIdentifier;
05720     }
05721 
05722     /*!
05723      \return the cached value of the class name if it exists, if not it's fetched dynamically
05724     */
05725     public function className()
05726     {
05727         if ( $this->ClassName === null )
05728         {
05729             $object = $this->object();
05730             $class = $object->contentClass();
05731             $this->ClassName = $class->attribute( 'name' );
05732         }
05733 
05734         return $this->ClassName;
05735     }
05736 
05737     /*!
05738      \return the cached value of the class is_container flag if it exists, if not it's fetched dynamically
05739     */
05740     public function classIsContainer()
05741     {
05742         if ( $this->ClassIsContainer === null )
05743         {
05744             $object = $this->object();
05745             $class = $object->contentClass();
05746             $this->ClassIsContainer = $class->attribute( 'is_container' );
05747         }
05748         return $this->ClassIsContainer;
05749     }
05750 
05751     /*!
05752     \return combined string representation of both "is_hidden" and "is_invisible" attributes
05753     Used in the node view templates.
05754     FIXME: this method probably should be removed in the future.
05755     */
05756     function hiddenInvisibleString()
05757     {
05758         return ( $this->IsHidden ? 'H' : '-' ) . '/' . ( $this->IsInvisible ? 'X' : '-' );
05759     }
05760 
05761     /*!
05762     \return combined string representation of both "is_hidden" and "is_invisible" attributes
05763     Used in the limitation handling templates.
05764     */
05765     function hiddenStatusString()
05766     {
05767         if( $this->IsHidden )
05768         {
05769             return ezpI18n::tr( 'kernel/content', 'Hidden' );
05770         }
05771         else if( $this->IsInvisible )
05772         {
05773             return ezpI18n::tr( 'kernel/content', 'Hidden by superior' );
05774         }
05775         return ezpI18n::tr( 'kernel/content', 'Visible' );
05776     }
05777 
05778     /*!
05779      \a static
05780 
05781      \param $node            Root node of the subtree
05782      \param $modifyRootNode  Whether to modify the root node (true/false)
05783 
05784      Hide algorithm:
05785      if ( root node of the subtree is visible )
05786      {
05787         1) Mark root node as hidden and invisible
05788         2) Recursively mark child nodes as invisible except for ones which have been previously marked as invisible
05789      }
05790      else
05791      {
05792         Mark root node as hidden
05793      }
05794 
05795      In some cases we don't want to touch the root node when (un)hiding a subtree, for example
05796      after content/move or content/copy.
05797      That's why $modifyRootNode argument is used.
05798 
05799      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05800      the calls within a db transaction; thus within db->begin and db->commit.
05801     */
05802     static function hideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true )
05803     {
05804         $nodeID = $node->attribute( 'node_id' );
05805         $time = time();
05806         $db = eZDB::instance();
05807 
05808         if ( eZAudit::isAuditEnabled() )
05809         {
05810             // Set audit params.
05811             $objectID = $node->attribute( 'contentobject_id' );
05812             $objectName = $node->attribute( 'name' );
05813             eZAudit::writeAudit( 'content-hide', array( 'Node ID' => $nodeID,
05814                                                         'Object ID' => $objectID,
05815                                                         'Content Name' => $objectName,
05816                                                         'Time' => $time,
05817                                                         'Comment' => 'Node has been hidden: eZContentObjectTreeNode::hideSubTree()' ) );
05818         }
05819 
05820         $db->begin();
05821 
05822         if ( !$node->attribute( 'is_invisible' ) ) // if root node is visible
05823         {
05824             // 1) Mark root node as hidden and invisible.
05825             if ( $modifyRootNode )
05826                 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, is_invisible=1, modified_subnode=$time WHERE node_id=$nodeID" );
05827 
05828             // 2) Recursively mark child nodes as invisible, except for ones which have been previously marked as invisible.
05829             $nodePath = $node->attribute( 'path_string' );
05830             $db->query( "UPDATE ezcontentobject_tree SET is_invisible=1, modified_subnode=$time WHERE is_invisible=0 AND path_string LIKE '$nodePath%'" );
05831         }
05832         else
05833         {
05834             // Mark root node as hidden
05835             if ( $modifyRootNode )
05836                 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=1, modified_subnode=$time WHERE node_id=$nodeID" );
05837         }
05838 
05839         $node->updateAndStoreModified();
05840 
05841         $db->commit();
05842 
05843         eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode );
05844     }
05845 
05846     /*!
05847      \a static
05848 
05849      \param $node            Root node of the subtree
05850      \param $modifyRootNode  Whether to modify the root node (true/false)
05851 
05852      Unhide algorithm:
05853      if ( parent node is visible )
05854      {
05855         1) Mark root node as not hidden and visible.
05856         2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children).
05857      }
05858      else
05859      {
05860         Mark root node as not hidden.
05861      }
05862 
05863      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
05864      the calls within a db transaction; thus within db->begin and db->commit.
05865     */
05866     static function unhideSubTree( eZContentObjectTreeNode $node, $modifyRootNode = true )
05867     {
05868         $nodeID = $node->attribute( 'node_id' );
05869         $nodePath = $node->attribute( 'path_string' );
05870         $nodeInvisible = $node->attribute( 'is_invisible' );
05871         $parentNode = $node->attribute( 'parent' );
05872         if ( !$parentNode instanceof eZContentObjectTreeNode )
05873         {
05874             eZDebug::writeError( "Parent of Node #$nodeId doesn't exist or inaccesible.", __METHOD__ );
05875             return;
05876         }
05877 
05878         $time = time();
05879 
05880         if ( eZAudit::isAuditEnabled() )
05881         {
05882             // Set audit params.
05883             $objectID = $node->attribute( 'contentobject_id' );
05884             $objectName = $node->attribute( 'name' );
05885 
05886             eZAudit::writeAudit( 'content-hide', array( 'Node ID' => $nodeID,
05887                                                         'Object ID' => $objectID,
05888                                                         'Content Name' => $objectName,
05889                                                         'Time' => $time,
05890                                                         'Comment' => 'Node has been unhidden: eZContentObjectTreeNode::unhideSubTree()' ) );
05891         }
05892 
05893         $db = eZDB::instance();
05894 
05895         $db->begin();
05896 
05897         if ( ! $parentNode->attribute( 'is_invisible' ) ) // if parent node is visible
05898         {
05899             // 1) Mark root node as not hidden and visible.
05900             if ( $modifyRootNode )
05901                 $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" );
05902 
05903             // 2) Recursively mark child nodes as visible (except for nodes previosly marked as hidden, and all their children).
05904 
05905             // 2.1) $hiddenChildren = Fetch all hidden children for the root node
05906             $hiddenChildren = $db->arrayQuery( "SELECT path_string FROM ezcontentobject_tree " .
05907                                                 "WHERE node_id <> $nodeID AND is_hidden=1 AND path_string LIKE '$nodePath%'" );
05908             $skipSubtreesString = '';
05909             foreach ( $hiddenChildren as $i )
05910                 $skipSubtreesString .= " AND path_string NOT LIKE '" . $i['path_string'] . "%'";
05911 
05912             // 2.2) Mark those children as visible which are not under nodes in $hiddenChildren
05913             $db->query( "UPDATE ezcontentobject_tree SET is_invisible=0, modified_subnode=$time WHERE path_string LIKE '$nodePath%' $skipSubtreesString" );
05914         }
05915         else
05916         {
05917             // Mark root node as not hidden.
05918             if ( $modifyRootNode )
05919                 $db->query( "UPDATE ezcontentobject_tree SET is_hidden=0, modified_subnode=$time WHERE node_id=$nodeID" );
05920         }
05921 
05922         $node->updateAndStoreModified();
05923 
05924         $db->commit();
05925 
05926         eZContentObjectTreeNode::clearViewCacheForSubtree( $node, $modifyRootNode );
05927     }
05928 
05929     /*!
05930      \a static
05931      Depending on the new parent node visibility, recompute "is_invisible" attribute for the given node and its children.
05932      (used after content/move or content/copy)
05933     */
05934     static function updateNodeVisibility( $node, $parentNode, $recursive = true )
05935     {
05936         if ( !$node )
05937         {
05938             eZDebug::writeWarning( 'No such node to update visibility for.' );
05939             return;
05940         }
05941 
05942         if ( !$parentNode )
05943         {
05944             eZDebug::writeWarning( 'No parent node found when updating node visibility' );
05945             return;
05946         }
05947 
05948         if ( $node->attribute( 'is_hidden' ) == 0 &&
05949              $parentNode->attribute( 'is_invisible' ) != $node->attribute( 'is_invisible' ) )
05950         {
05951             $parentNodeIsVisible = $parentNode->attribute( 'is_invisible' );
05952             $nodeID                 = $node->attribute( 'node_id' );
05953             $db                     = eZDB::instance();
05954             $db->begin();
05955             $db->query( "UPDATE ezcontentobject_tree SET is_invisible=$parentNodeIsVisible WHERE node_id=$nodeID" );
05956 
05957             if ( $recursive )
05958             {
05959                 // update visibility for children of the node
05960                 if( $parentNodeIsVisible )
05961                     eZContentObjectTreeNode::hideSubTree( $node, $modifyRootNode = false );
05962                 else
05963                     eZContentObjectTreeNode::unhideSubTree( $node, $modifyRootNode = false );
05964             }
05965             $db->commit();
05966         }
05967     }
05968 
05969     /*!
05970      \a static
05971      \return true on success, false otherwise
05972     */
05973     static function clearViewCacheForSubtree( eZContentObjectTreeNode $node, $clearForRootNode = true )
05974     {
05975         // Max nodes to fetch at a time
05976         static $limit = 50;
05977 
05978         if ( $clearForRootNode )
05979         {
05980             $objectID = $node->attribute( 'contentobject_id' );
05981             eZContentCacheManager::clearContentCacheIfNeeded( $objectID );
05982         }
05983 
05984         $offset = 0;
05985         $params = array( 'AsObject' => false,
05986                          'Depth' => false,
05987                          'Limitation' => array() ); // Empty array means no permission checking
05988         $subtreeCount = $node->subTreeCount( $params );
05989         while ( $offset < $subtreeCount )
05990         {
05991             $params['Offset'] = $offset;
05992             $params['Limit'] = $limit;
05993 
05994             $subtreeChunk = $node->subTree( $params );
05995             $nNodesInChunk = count( $subtreeChunk );
05996             $offset += $nNodesInChunk;
05997             if ( $nNodesInChunk == 0 )
05998                 break;
05999 
06000             $objectIDList = array();
06001             foreach ( $subtreeChunk as $curNode )
06002                 $objectIDList[] = $curNode['contentobject_id'];
06003             $objectIDList = array_unique( $objectIDList );
06004             unset( $subtreeChunk );
06005 
06006             foreach ( $objectIDList as $objectID )
06007                 eZContentCacheManager::clearContentCacheIfNeeded( $objectID );
06008         }
06009 
06010         return true;
06011     }
06012 
06013     static function setVersionByObjectID( $objectID, $newVersion )
06014     {
06015         $db = eZDB::instance();
06016         $db->query( "UPDATE ezcontentobject_tree SET contentobject_version='$newVersion' WHERE contentobject_id='$objectID'" );
06017     }
06018 
06019     function currentLanguage()
06020     {
06021         return $this->CurrentLanguage;
06022     }
06023 
06024     function setCurrentLanguage( $languageCode )
06025     {
06026         $this->CurrentLanguage = $languageCode;
06027         if ( $this->hasContentObject() )
06028         {
06029             $this->ContentObject->setCurrentLanguage( $languageCode );
06030         }
06031         $this->Name = null;
06032     }
06033 
06034     /*!
06035      \static
06036     */
06037     static function parentDepthLimitationList()
06038     {
06039         $maxLevel = 0;
06040         $ini = eZINI::instance();
06041         if ( $ini->hasVariable( 'RoleSettings', 'MaxParentDepthLimitation' ) )
06042             $maxLevel = $ini->variable( 'RoleSettings', 'MaxParentDepthLimitation' );
06043 
06044         $depthArray = array();
06045         for ( $i = 1; $i <= $maxLevel; $i++ )
06046         {
06047             $depthArray[] = array( 'name' => $i, 'id' => $i );
06048         }
06049 
06050         return $depthArray;
06051     }
06052 
06053     /*
06054       Returns available classes as Js array.
06055       Checks if the node is container, if yes emptyStr will be returned.
06056     */
06057     function availableClassesJsArray()
06058     {
06059         return eZContentObjectTreeNode::availableClassListJsArray( array( 'node' => $this ) );
06060     }
06061 
06062     /*
06063       Returns available classes as Js array.
06064       Checks for ini settings.
06065     */
06066     static function availableClassListJsArray( $parameters = false )
06067     {
06068         $iniMenu = eZINI::instance( 'contentstructuremenu.ini' );
06069         $falseValue = "''";
06070 
06071         if ( $iniMenu->hasVariable( 'TreeMenu', 'CreateHereMenu' ) )
06072         {
06073             $createHereMenu = $iniMenu->variable( 'TreeMenu', 'CreateHereMenu' );
06074         }
06075         else
06076         {
06077             $createHereMenu = 'simplified';
06078         }
06079 
06080         if ( $createHereMenu != 'simplified' and $createHereMenu != 'full' )
06081             return $falseValue;
06082 
06083         $ini = eZINI::instance( 'content.ini' );
06084         list( $usersClassGroupID, $setupClassGroupID ) = $ini->variableMulti( 'ClassGroupIDs', array( 'Users', 'Setup' ) );
06085         $userRootNode = $ini->variable( 'NodeSettings', 'UserRootNode' );
06086         $groupIDs = false;
06087         $filterType = 'include';
06088 
06089         if ( !is_array( $parameters ) )
06090             return $falseValue;
06091 
06092         $node = isset( $parameters['node'] ) ? $parameters['node'] : false;
06093         if ( is_object( $node ) )
06094         {
06095             if ( $createHereMenu == 'full' and !$node->canCreate() )
06096                 return $falseValue;
06097 
06098             $obj = $node->object();
06099             $contentClass = $obj->attribute( 'content_class' );
06100             if ( !$contentClass->attribute( 'is_container' ) )
06101             {
06102                 return $falseValue;
06103             }
06104 
06105             $pathArray = $node->pathArray();
06106         }
06107         else
06108         {
06109             // If current object is not container we should not return class list, should not display "create here" menu.
06110             if ( isset( $parameters['is_container'] ) and !$parameters['is_container'] )
06111                 return $falseValue;
06112 
06113             // Check if current user can create under this node
06114             if ( $createHereMenu == 'full' and isset( $parameters['node_id'] ) )
06115             {
06116                 $node = eZContentObjectTreeNode::fetch( $parameters['node_id'] );
06117                 if ( is_object( $node ) and !$node->canCreate() )
06118                     return $falseValue;
06119             }
06120             $pathString = isset( $parameters['path_string'] ) ? $parameters['path_string'] : false;
06121             if ( !$pathString )
06122                 return $falseValue;
06123 
06124             $pathItems = explode( '/', $pathString );
06125             $pathArray = array();
06126             foreach ( $pathItems as $pathItem )
06127             {
06128                 if ( $pathItem != '' )
06129                     $pathArray[] = (int) $pathItem;
06130             }
06131         }
06132 
06133         if ( in_array( $userRootNode, $pathArray ) )
06134         {
06135             $groupIDs = array( $usersClassGroupID );
06136         }
06137         else
06138         {
06139             $groupIDs = array( $usersClassGroupID, $setupClassGroupID );
06140             $filterType = 'exclude';
06141         }
06142 
06143         if ( $createHereMenu == 'simplified' )
06144         {
06145             $classes = eZContentClass::fetchAllClasses( false, $filterType == 'include', $groupIDs );
06146             return eZContentObjectTreeNode::getClassesJsArray( false, $filterType == 'include', $groupIDs, false, $classes );
06147         }
06148 
06149         return eZContentObjectTreeNode::getClassesJsArray( $node, $filterType == 'include', $groupIDs );
06150     }
06151 
06152     /*
06153       Returns available classes as Js array.
06154       \note building js array.
06155     */
06156     static function getClassesJsArray( $node = false, $includeFilter = true, $groupList = false, $fetchID = false, $classes = false )
06157     {
06158         $falseValue = "''";
06159         // If $classes is false we should check $node and fetch class list
06160         if ( $classes === false )
06161         {
06162             // If $node is object we should fetch available classes from node, from ezcontentclass otherwise
06163             $classes = ( $node instanceOf eZContentObjectTreeNode )
06164                         ? $node->canCreateClassList( false, $includeFilter, $groupList, $fetchID )
06165                         : eZContentClass::canInstantiateClassList( false, $includeFilter, $groupList, $fetchID );
06166         }
06167         if ( !is_array( $classes ) )
06168             return $falseValue;
06169 
06170         // Create javascript array
06171         $classList = array();
06172         foreach ( $classes as $class )
06173         {
06174             if ( $class instanceOf eZContentClass )
06175             {
06176                 $classID = $class->attribute( 'id' );
06177                 $className = $class->attribute( 'name' );
06178             }
06179             elseif ( is_array( $class ) )
06180             {
06181                 $classID = $class['id'];
06182                 $className = $class['name'];
06183             }
06184             $classList[] = array(
06185                 'classID' => (int) $classID,
06186                 'name'    => $className
06187             );
06188         }
06189 
06190         if ( $classList )
06191             return json_encode( $classList );
06192 
06193         return $falseValue;
06194     }
06195 
06196 
06197     /// The current language for the node
06198     public $CurrentLanguage = false;
06199 
06200     /// Name of the node
06201     public $Name;
06202 
06203     /// Contains the cached value of the class identifier
06204     public $ClassIdentifier = null;
06205     public $ClassName = null;
06206     protected $ClassIsContainer = null;
06207 }
06208 
06209 ?>