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