eZ Publish  [trunk]
ezrssexport.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZRSSExport class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package kernel
00009  */
00010 
00011 /*!
00012   \class eZRSSExport ezrssexport.php
00013   \brief Handles RSS Export in eZ Publish
00014 
00015   RSSExport is used to create RSS feeds from published content. See kernel/rss for more files.
00016 */
00017 
00018 class eZRSSExport extends eZPersistentObject
00019 {
00020     const STATUS_VALID = 1;
00021     const STATUS_DRAFT = 0;
00022 
00023     /*!
00024      Initializes a new RSSExport.
00025     */
00026     function eZRSSExport( $row )
00027     {
00028         $this->eZPersistentObject( $row );
00029     }
00030 
00031     static function definition()
00032     {
00033         return array( 'fields' => array( 'id' => array( 'name' => 'ID',
00034                                                         'datatype' => 'integer',
00035                                                         'default' => 0,
00036                                                         'required' => true ),
00037                                          'node_id' => array( 'name' => 'NodeID',
00038                                                              'datatype' => 'integer',
00039                                                              'default' => 0,
00040                                                              'required' => false ),
00041                                          'title' => array( 'name' => 'Title',
00042                                                            'datatype' => 'string',
00043                                                            'default' => ezpI18n::tr( 'kernel/rss', 'New RSS Export' ),
00044                                                            'required' => true ),
00045                                          'url' => array( 'name' => 'URL',
00046                                                          'datatype' => 'string',
00047                                                          'default' => '',
00048                                                          'required' => true ),
00049                                          'site_access' => array( 'name' => 'SiteAccess',
00050                                                                  'datatype' => 'string',
00051                                                                  'default' => '',
00052                                                                  'required' => true ),
00053                                          'modified' => array( 'name' => 'Modified',
00054                                                               'datatype' => 'integer',
00055                                                               'default' => 0,
00056                                                               'required' => true ),
00057                                          'modifier_id' => array( 'name' => 'ModifierID',
00058                                                               'datatype' => 'integer',
00059                                                               'default' => 0,
00060                                                               'required' => true,
00061                                                                  'foreign_class' => 'eZUser',
00062                                                                  'foreign_attribute' => 'contentobject_id',
00063                                                                  'multiplicity' => '1..*' ),
00064                                          'created' => array( 'name' => 'Created',
00065                                                               'datatype' => 'integer',
00066                                                               'default' => 0,
00067                                                               'required' => true ),
00068                                          'creator_id' => array( 'name' => 'CreatorID',
00069                                                               'datatype' => 'integer',
00070                                                               'default' => 0,
00071                                                               'required' => true,
00072                                                                 'foreign_class' => 'eZUser',
00073                                                                 'foreign_attribute' => 'contentobject_id',
00074                                                                 'multiplicity' => '1..*' ),
00075                                          'description' => array( 'name' => 'Description',
00076                                                                  'datatype' => 'string',
00077                                                                  'default' => '',
00078                                                                  'required' => false ),
00079                                          'image_id' => array( 'name' => 'ImageID',
00080                                                               'datatype' => 'integer',
00081                                                               'default' => 0,
00082                                                               'required' => false ),
00083                                          'rss_version' => array( 'name' => 'RSSVersion',
00084                                                                  'datatype' => 'string',
00085                                                                  'default' => 0,
00086                                                                  'required' => true ),
00087                                          'active' => array( 'name' => 'Active',
00088                                                             'datatype' => 'integer',
00089                                                             'default' => 0,
00090                                                             'required' => true ),
00091                                          'status' => array( 'name' => 'Status',
00092                                                             'datatype' => 'integer',
00093                                                             'default' => 0,
00094                                                             'required' => true ),
00095                                          'access_url' => array( 'name' => 'AccessURL',
00096                                                                 'datatype' => 'string',
00097                                                                 'default' => 'rss_feed',
00098                                                                 'required' => false ),
00099                                          'number_of_objects' => array( 'name' => 'NumberOfObjects',
00100                                                                        'datatype' => 'integer',
00101                                                                        'default' => 0,
00102                                                                        'required' => true ),
00103                                          'main_node_only' => array( 'name' => 'MainNodeOnly',
00104                                                                     'datatype' => 'integer',
00105                                                                     'default' => 0,
00106                                                                     'required' => true ) ),
00107                       'keys' => array( 'id', 'status' ),
00108                       'function_attributes' => array( 'item_list' => 'itemList',
00109                                                       'modifier' => 'modifier',
00110                                                       'rss-xml' => 'rssXml', // deprecated
00111                                                       'rss-xml-content' => 'rssXmlContent', // new attribute which uses the Feed component
00112                                                       'image_path' => 'imagePath',
00113                                                       'image_node' => 'imageNode' ),
00114                       'increment_key' => 'id',
00115                       'sort' => array( 'title' => 'asc' ),
00116                       'class_name' => 'eZRSSExport',
00117                       'name' => 'ezrss_export' );
00118 
00119     }
00120 
00121     /*!
00122      \static
00123      Creates a new RSS Export
00124      \param User ID
00125 
00126      \return the URL alias object
00127     */
00128     static function create( $user_id )
00129     {
00130         $config = eZINI::instance( 'site.ini' );
00131         $dateTime = time();
00132         $row = array( 'id' => null,
00133                       'node_id', '',
00134                       'title' => ezpI18n::tr( 'kernel/classes', 'New RSS Export' ),
00135                       'site_access' => '',
00136                       'modifier_id' => $user_id,
00137                       'modified' => $dateTime,
00138                       'creator_id' => $user_id,
00139                       'created' => $dateTime,
00140                       'status' => self::STATUS_DRAFT,
00141                       'url' => 'http://'. $config->variable( 'SiteSettings', 'SiteURL' ),
00142                       'description' => '',
00143                       'image_id' => 0,
00144                       'active' => 1,
00145                       'access_url' => '' );
00146         return new eZRSSExport( $row );
00147     }
00148 
00149     /*!
00150      Store Object to database
00151      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00152      the calls within a db transaction; thus within db->begin and db->commit.
00153     */
00154     function store( $storeAsValid = false )
00155     {
00156         $dateTime = time();
00157         $user = eZUser::currentUser();
00158         if (  $this->ID == null )
00159         {
00160             eZPersistentObject::store();
00161             return;
00162         }
00163 
00164         $db = eZDB::instance();
00165         $db->begin();
00166         if ( $storeAsValid )
00167         {
00168             $oldStatus = $this->attribute( 'status' );
00169             $this->setAttribute( 'status', eZRSSExport::STATUS_VALID );
00170         }
00171         $this->setAttribute( 'modified', $dateTime );
00172         $this->setAttribute( 'modifier_id', $user->attribute( "contentobject_id" ) );
00173         eZPersistentObject::store();
00174         $db->commit();
00175         if ( $storeAsValid )
00176         {
00177             $this->setAttribute( 'status', $oldStatus );
00178         }
00179     }
00180 
00181     /*!
00182      Remove the RSS Export.
00183      \note Transaction unsafe. If you call several transaction unsafe methods you must enclose
00184      the calls within a db transaction; thus within db->begin and db->commit.
00185     */
00186     function removeThis()
00187     {
00188         $exportItems = $this->fetchItems();
00189 
00190         $db = eZDB::instance();
00191         $db->begin();
00192         foreach ( $exportItems as $item )
00193         {
00194             $item->remove();
00195         }
00196         eZPersistentObject::remove();
00197         $db->commit();
00198     }
00199 
00200     /*!
00201      \static
00202       Fetches the RSS Export by ID.
00203 
00204      \param RSS Export ID
00205     */
00206     static function fetch( $id, $asObject = true, $status = eZRSSExport::STATUS_VALID )
00207     {
00208         return eZPersistentObject::fetchObject( eZRSSExport::definition(),
00209                                                 null,
00210                                                 array( "id" => $id,
00211                                                        'status' => $status ),
00212                                                 $asObject );
00213     }
00214 
00215     /*!
00216      \static
00217       Fetches the RSS Export by feed access url and is active.
00218 
00219      \param RSS Export access url
00220     */
00221     static function fetchByName( $access_url, $asObject = true )
00222     {
00223         return eZPersistentObject::fetchObject( eZRSSExport::definition(),
00224                                                 null,
00225                                                 array( 'access_url' => $access_url,
00226                                                        'active' => 1,
00227                                                        'status' => self::STATUS_VALID ),
00228                                                 $asObject );
00229     }
00230 
00231     /*!
00232      \static
00233       Fetches complete list of RSS Exports.
00234     */
00235     static function fetchList( $asObject = true )
00236     {
00237         return eZPersistentObject::fetchObjectList( eZRSSExport::definition(),
00238                                                     null, array( 'status' => self::STATUS_VALID ), null, null,
00239                                                     $asObject );
00240     }
00241 
00242     function itemList()
00243     {
00244         return $this->fetchItems();
00245     }
00246 
00247     function imageNode()
00248     {
00249         if ( isset( $this->ImageID ) and $this->ImageID )
00250         {
00251             return eZContentObjectTreeNode::fetch( $this->ImageID );
00252         }
00253         return null;
00254     }
00255 
00256     function imagePath()
00257     {
00258         if ( isset( $this->ImageID ) and $this->ImageID )
00259         {
00260             $objectNode = eZContentObjectTreeNode::fetch( $this->ImageID );
00261             if ( isset( $objectNode ) )
00262             {
00263                 $retValue = '';
00264                 $path_array = $objectNode->attribute( 'path_array' );
00265                 for ( $i = 0; $i < count( $path_array ); $i++ )
00266                 {
00267                     $treenode = eZContentObjectTreeNode::fetch( $path_array[$i], false, false );
00268 
00269                     if( $i != 0 )
00270                     {
00271                         $retValue .= '/';
00272                     }
00273 
00274                     $retValue .= array_key_exists( 'name', $treenode ) ? $treenode['name'] : '';
00275                 }
00276                 return $retValue;
00277             }
00278         }
00279         return null;
00280 
00281     }
00282 
00283     function modifier()
00284     {
00285         if ( isset( $this->ModifierID ) and $this->ModifierID )
00286         {
00287             return eZUser::fetch( $this->ModifierID );
00288         }
00289         return null;
00290     }
00291 
00292     /**
00293      * Generates an RSS feed document based on the rss_version attribute.
00294      *
00295      * @deprecated since 4.2
00296      * @return DomDocument XML document
00297      */
00298     function rssXml()
00299     {
00300         switch( $this->attribute( 'rss_version' ) )
00301         {
00302             case '1.0':
00303             {
00304                 return $this->fetchRSS1_0();
00305             } break;
00306 
00307             case '2.0':
00308             {
00309                 return $this->fetchRSS2_0();
00310             } break;
00311 
00312             default:
00313             {
00314                 return null;
00315             } break;
00316         }
00317 
00318         return null;
00319     }
00320 
00321     /**
00322      * Generates an RSS feed document based on the rss_version attribute.
00323      *
00324      * It uses the Feed component from eZ Components.
00325      *
00326      * Supported types: 'rss1', 'rss2', 'atom'.
00327      *
00328      * @since 4.2
00329      * @return string XML document as a string
00330      */
00331     function rssXmlContent()
00332     {
00333         try
00334         {
00335             switch ( $this->attribute( 'rss_version' ) )
00336             {
00337                 case '1.0':
00338                 {
00339                     return $this->generateFeed( 'rss1' );
00340                 } break;
00341 
00342                 case '2.0':
00343                 {
00344                     return $this->generateFeed( 'rss2' );
00345                 } break;
00346 
00347                 case 'ATOM':
00348                 {
00349                     return $this->generateFeed( 'atom' );
00350                 } break;
00351 
00352                 default:
00353                 {
00354                     return null;
00355                 } break;
00356             }
00357         }
00358         catch ( ezcFeedException $e )
00359         {
00360             return '<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang=""><title>The RSS feed you were trying to access contains some errors and cannot be generated: ' . $e->getMessage() . ' Please contact the webmaster.</title></feed>';
00361         }
00362 
00363         return null;
00364     }
00365 
00366     /*!
00367       Fetches RSS Items related to this RSS Export. The RSS Export Items contain information about which nodes to export information from
00368 
00369       \param RSSExport ID (optional). Uses current RSSExport's ID as default
00370 
00371       \return RSSExportItem list. null if no RSS Export items found
00372     */
00373     function fetchItems( $id = false, $status = eZRSSExport::STATUS_VALID )
00374     {
00375         if ( $id === false )
00376         {
00377             if ( isset( $this ) )
00378             {
00379                 $id = $this->ID;
00380                 $status = $this->Status;
00381             }
00382             else
00383             {
00384                 $itemList = null;
00385                 return $itemList;
00386             }
00387         }
00388         if ( $id !== null )
00389             $itemList = eZRSSExportItem::fetchFilteredList( array( 'rssexport_id' => $id, 'status' => $status ) );
00390         else
00391             $itemList = null;
00392         return $itemList;
00393     }
00394 
00395     function getObjectListFilter()
00396     {
00397         if ( $this->MainNodeOnly == 1 )
00398         {
00399             $this->MainNodeOnly = true;
00400         }
00401         else
00402         {
00403             $this->MainNodeOnly = false;
00404         }
00405 
00406         return array( 'number_of_objects' => intval($this->NumberOfObjects),
00407                       'main_node_only'    => $this->MainNodeOnly
00408                      );
00409     }
00410 
00411     /**
00412      * Get a RSS xml document based on the RSS 2.0 standard based on the RSS Export settings defined by this object
00413      *
00414      * @deprecated since 4.2
00415      * @return string RSS 2.0 XML document
00416      */
00417     function fetchRSS2_0()
00418     {
00419         $locale = eZLocale::instance();
00420 
00421         // Get URL Translation settings.
00422         $config = eZINI::instance();
00423         if ( $config->variable( 'URLTranslator', 'Translation' ) == 'enabled' )
00424         {
00425             $useURLAlias = true;
00426         }
00427         else
00428         {
00429             $useURLAlias = false;
00430         }
00431 
00432         if ( $this->attribute( 'url' ) == '' )
00433         {
00434             $baseItemURL = '';
00435             eZURI::transformURI( $baseItemURL, false, 'full' );
00436             $baseItemURL .= '/';
00437         }
00438         else
00439         {
00440             $baseItemURL = $this->attribute( 'url' ).'/'; //.$this->attribute( 'site_access' ).'/';
00441         }
00442 
00443         $doc = new DOMDocument( '1.0', 'utf-8' );
00444         $doc->formatOutput = true;
00445         $root = $doc->createElement( 'rss' );
00446         $root->setAttribute( 'version', '2.0' );
00447         $root->setAttribute( 'xmlns:atom', 'http://www.w3.org/2005/Atom' );
00448         $doc->appendChild( $root );
00449 
00450         $channel = $doc->createElement( 'channel' );
00451         $root->appendChild( $channel );
00452 
00453         $atomLink = $doc->createElement( 'atom:link' );
00454         $atomLink->setAttribute( 'href', $baseItemURL . "rss/feed/" . $this->attribute( 'access_url' ) );
00455         $atomLink->setAttribute( 'rel', 'self' );
00456         $atomLink->setAttribute( 'type', 'application/rss+xml' );
00457         $channel->appendChild( $atomLink );
00458 
00459         $channelTitle = $doc->createElement( 'title' );
00460         $channelTitle->appendChild( $doc->createTextNode( $this->attribute( 'title' ) ) );
00461         $channel->appendChild( $channelTitle );
00462 
00463         $channelLink = $doc->createElement( 'link' );
00464         $channelLink->appendChild( $doc->createTextNode( $this->attribute( 'url' ) ) );
00465         $channel->appendChild( $channelLink );
00466 
00467         $channelDescription = $doc->createElement( 'description' );
00468         $channelDescription->appendChild( $doc->createTextNode( $this->attribute( 'description' ) ) );
00469         $channel->appendChild( $channelDescription );
00470 
00471         $channelLanguage = $doc->createElement( 'language' );
00472         $channelLanguage->appendChild( $doc->createTextNode( $locale->httpLocaleCode() ) );
00473         $channel->appendChild( $channelLanguage );
00474 
00475         $imageURL = $this->fetchImageURL();
00476         if ( $imageURL !== false )
00477         {
00478             $image = $doc->createElement( 'image' );
00479 
00480             $imageUrlNode = $doc->createElement( 'url' );
00481             $imageUrlNode->appendChild( $doc->createTextNode( $imageURL ) );
00482             $image->appendChild( $imageUrlNode );
00483 
00484             $imageTitle = $doc->createElement( 'title' );
00485             $imageTitle->appendChild( $doc->createTextNode( $this->attribute( 'title' ) ) );
00486             $image->appendChild( $imageTitle );
00487 
00488             $imageLink = $doc->createElement( 'link' );
00489             $imageLink->appendChild( $doc->createTextNode( $this->attribute( 'url' ) ) );
00490             $image->appendChild( $imageLink );
00491 
00492             $channel->appendChild( $image );
00493         }
00494 
00495         $cond = array(
00496                     'rssexport_id'  => $this->ID,
00497                     'status'        => $this->Status
00498                     );
00499         $rssSources = eZRSSExportItem::fetchFilteredList( $cond );
00500 
00501         $nodeArray = eZRSSExportItem::fetchNodeList( $rssSources, $this->getObjectListFilter() );
00502 
00503         if ( is_array( $nodeArray ) && count( $nodeArray ) )
00504         {
00505             $attributeMappings = eZRSSExportItem::getAttributeMappings( $rssSources );
00506 
00507             foreach ( $nodeArray as $node )
00508             {
00509                 $object = $node->attribute( 'object' );
00510                 $dataMap = $object->dataMap();
00511                 if ( $useURLAlias === true )
00512                 {
00513                     $nodeURL = $this->urlEncodePath( $baseItemURL . $node->urlAlias() );
00514                 }
00515                 else
00516                 {
00517                     $nodeURL = $baseItemURL . 'content/view/full/' . $node->attribute( 'node_id' );
00518                 }
00519 
00520                 // keep track if there's any match
00521                 $doesMatch = false;
00522                 // start mapping the class attribute to the respective RSS field
00523                 foreach ( $attributeMappings as $attributeMapping )
00524                 {
00525                     // search for correct mapping by path
00526                     if ( $attributeMapping[0]->attribute( 'class_id' ) == $object->attribute( 'contentclass_id' ) and
00527                          in_array( $attributeMapping[0]->attribute( 'source_node_id' ), $node->attribute( 'path_array' ) ) )
00528                     {
00529                         // found it
00530                         $doesMatch = true;
00531                         // now fetch the attributes
00532                         $title =  $dataMap[$attributeMapping[0]->attribute( 'title' )];
00533                         $description =  $dataMap[$attributeMapping[0]->attribute( 'description' )];
00534                         // category is optional
00535                         $catAttributeIdentifier = $attributeMapping[0]->attribute( 'category' );
00536                         $category = $catAttributeIdentifier ? $dataMap[$catAttributeIdentifier] : false;
00537                         break;
00538                     }
00539                 }
00540 
00541                 if( !$doesMatch )
00542                 {
00543                     // no match
00544                     eZDebug::writeWarning( __METHOD__ . ': Cannot find matching RSS source node for content object in '.__FILE__.', Line '.__LINE__ );
00545                     $retValue = null;
00546                     return $retValue;
00547                 }
00548 
00549                 $item = $doc->createElement( 'item' );
00550 
00551                 // title RSS element with respective class attribute content
00552                 $titleContent =  $title->attribute( 'content' );
00553                 if ( $titleContent instanceof eZXMLText )
00554                 {
00555                     $outputHandler = $titleContent->attribute( 'output' );
00556                     $itemTitleText = $outputHandler->attribute( 'output_text' );
00557                 }
00558                 else
00559                 {
00560                     $itemTitleText = $titleContent;
00561                 }
00562 
00563                 $itemTitle = $doc->createElement( 'title' );
00564                 $itemTitle->appendChild( $doc->createTextNode( $itemTitleText ) );
00565                 $item->appendChild( $itemTitle );
00566 
00567                 $itemLink = $doc->createElement( 'link' );
00568                 $itemLink->appendChild( $doc->createTextNode( $nodeURL ) );
00569                 $item->appendChild( $itemLink );
00570 
00571                 $itemGuid = $doc->createElement( 'guid' );
00572                 $itemGuid->appendChild( $doc->createTextNode( $nodeURL ) );
00573                 $item->appendChild( $itemGuid );
00574 
00575                 // description RSS element with respective class attribute content
00576                 $descriptionContent =  $description->attribute( 'content' );
00577                 if ( $descriptionContent instanceof eZXMLText )
00578                 {
00579                     $outputHandler =  $descriptionContent->attribute( 'output' );
00580                     $itemDescriptionText = $outputHandler->attribute( 'output_text' );
00581                 }
00582                 else
00583                 {
00584                     $itemDescriptionText = $descriptionContent;
00585                 }
00586 
00587                 $itemDescription = $doc->createElement( 'description' );
00588                 $itemDescription->appendChild( $doc->createTextNode( $itemDescriptionText ) );
00589                 $item->appendChild( $itemDescription );
00590 
00591                 // category RSS element with respective class attribute content
00592                 if ( $category )
00593                 {
00594                     $categoryContent =  $category->attribute( 'content' );
00595                     if ( $categoryContent instanceof eZXMLText )
00596                     {
00597                         $outputHandler = $categoryContent->attribute( 'output' );
00598                         $itemCategoryText = $outputHandler->attribute( 'output_text' );
00599                     }
00600                     elseif ( $categoryContent instanceof eZKeyword )
00601                     {
00602                         $itemCategoryText = $categoryContent->keywordString();
00603                     }
00604                     else
00605                     {
00606                         $itemCategoryText = $categoryContent;
00607                     }
00608 
00609                     $itemCategory = $doc->createElement( 'category' );
00610                     $itemCategory->appendChild( $doc->createTextNode( $itemCategoryText ) );
00611                     $item->appendChild( $itemCategory );
00612                 }
00613 
00614                 $itemPubDate = $doc->createElement( 'pubDate' );
00615                 $itemPubDate->appendChild( $doc->createTextNode( gmdate( 'D, d M Y H:i:s', $object->attribute( 'published' ) ) .' GMT' ) );
00616 
00617                 $item->appendChild( $itemPubDate );
00618 
00619                 $channel->appendChild( $item );
00620             }
00621         }
00622 
00623         return $doc;
00624     }
00625 
00626     /**
00627      * Get a RSS xml document based on the RSS 1.0 standard based on the RSS Export settings defined by this object
00628      *
00629      * @deprecated since 4.2
00630      * @return DomDocument RSS 1.0 XML document
00631      */
00632     function fetchRSS1_0()
00633     {
00634         $imageURL = $this->fetchImageURL();
00635 
00636         // Get URL Translation settings.
00637         $config = eZINI::instance( 'site.ini' );
00638         if ( $config->variable( 'URLTranslator', 'Translation' ) == 'enabled' )
00639         {
00640             $useURLAlias = true;
00641         }
00642         else
00643         {
00644             $useURLAlias = false;
00645         }
00646 
00647         if ( $this->attribute( 'url' ) == '' )
00648         {
00649             $baseItemURL = '';
00650             eZURI::transformURI( $baseItemURL, false, 'full' );
00651             $baseItemURL .= '/';
00652         }
00653         else
00654         {
00655             $baseItemURL = $this->attribute( 'url' ).'/'; //.$this->attribute( 'site_access' ).'/';
00656         }
00657 
00658         $doc = new DOMDocument( '1.0', 'utf-8' );
00659         $root = $doc->createElementNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:RDF' );
00660         $doc->appendChild( $root );
00661 
00662         $channel = $doc->createElementNS( 'http://purl.org/rss/1.0/', 'channel' );
00663         $channel->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:about', $this->attribute( 'url' ) );
00664         $root->appendChild( $channel );
00665 
00666         $channelTitle = $doc->createElement( 'title' );
00667         $channelTitle->appendChild( $doc->createTextNode( $this->attribute( 'title' ) ) );
00668         $channel->appendChild( $channelTitle );
00669 
00670         $channelUrl = $doc->createElement( 'link' );
00671         $channelUrl->appendChild( $doc->createTextNode( $this->attribute( 'url' ) ) );
00672         $channel->appendChild( $channelUrl );
00673 
00674         $channelDescription = $doc->createElement( 'description' );
00675         $channelDescription->appendChild( $doc->createTextNode( $this->attribute( 'description' ) ) );
00676         $channel->appendChild( $channelDescription );
00677 
00678         if ( $imageURL !== false )
00679         {
00680             $channelImage = $doc->createElement( 'image' );
00681             $channelImage->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:resource', $imageURL );
00682             $channel->appendChild( $channelImage );
00683 
00684             $image = $doc->createElement( 'image' );
00685             $image->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:about', $imageURL );
00686 
00687             $imageTitle = $doc->createElement( 'title' );
00688             $imageTitle->appendChild( $doc->createTextNode( $this->attribute( 'title' ) ) );
00689             $image->appendChild( $imageTitle );
00690 
00691             $imageLink = $doc->createElement( 'link' );
00692             $imageLink->appendChild( $doc->createTextNode( $this->attribute( 'url' ) ) );
00693             $image->appendChild( $imageLink );
00694 
00695             $imageUrlNode = $doc->createElement( 'url' );
00696             $imageUrlNode->appendChild( $doc->createTextNode( $imageURL ) );
00697             $image->appendChild( $imageUrlNode );
00698 
00699             $root->appendChild( $image );
00700         }
00701 
00702         $items = $doc->createElement( 'items' );
00703         $channel->appendChild( $items );
00704 
00705         $rdfSeq = $doc->createElementNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:Seq' );
00706         $items->appendChild( $rdfSeq );
00707 
00708         $cond = array(
00709                     'rssexport_id'  => $this->ID,
00710                     'status'        => $this->Status
00711                     );
00712         $rssSources = eZRSSExportItem::fetchFilteredList( $cond );
00713 
00714         $nodeArray = eZRSSExportItem::fetchNodeList( $rssSources, $this->getObjectListFilter() );
00715 
00716         if ( is_array( $nodeArray ) && count( $nodeArray ) )
00717         {
00718             $attributeMappings = eZRSSExportItem::getAttributeMappings( $rssSources );
00719 
00720             foreach ( $nodeArray as $node )
00721             {
00722                 $object = $node->attribute( 'object' );
00723                 $dataMap = $object->dataMap();
00724                 if ( $useURLAlias === true )
00725                 {
00726                     $nodeURL = $this->urlEncodePath( $baseItemURL . $node->urlAlias() );
00727                 }
00728                 else
00729                 {
00730                     $nodeURL = $baseItemURL . 'content/view/full/' . $node->attribute( 'node_id' );
00731                 }
00732 
00733                 $rdfSeqLi = $doc->createElementNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:li' );
00734                 $rdfSeqLi->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:resource', $nodeURL );
00735                 $rdfSeq->appendChild( $rdfSeqLi );
00736 
00737                 // keep track if there's any match
00738                 $doesMatch = false;
00739                 // start mapping the class attribute to the respective RSS field
00740                 foreach ( $attributeMappings as $attributeMapping )
00741                 {
00742                     // search for correct mapping by path
00743                     if ( $attributeMapping[0]->attribute( 'class_id' ) == $object->attribute( 'contentclass_id' ) and
00744                          in_array( $attributeMapping[0]->attribute( 'source_node_id' ), $node->attribute( 'path_array' ) ) )
00745                     {
00746                         // found it
00747                         $doesMatch = true;
00748                         // now fetch the attributes
00749                         $title =  $dataMap[$attributeMapping[0]->attribute( 'title' )];
00750                         $description =  $dataMap[$attributeMapping[0]->attribute( 'description' )];
00751                         break;
00752                     }
00753                 }
00754 
00755                 if( !$doesMatch )
00756                 {
00757                     // no match
00758                     eZDebug::writeWarning( __METHOD__ . ': Cannot find matching RSS source node for content object in '.__FILE__.', Line '.__LINE__ );
00759                     $retValue = null;
00760                     return $retValue;
00761                 }
00762 
00763                 // title RSS element with respective class attribute content
00764                 $titleContent =  $title->attribute( 'content' );
00765                 if ( $titleContent instanceof eZXMLText )
00766                 {
00767                     $outputHandler =  $titleContent->attribute( 'output' );
00768                     $itemTitleText = $outputHandler->attribute( 'output_text' );
00769                 }
00770                 else
00771                 {
00772                     $itemTitleText = $titleContent;
00773                 }
00774 
00775                 $itemTitle = $doc->createElement( 'title' );
00776                 $itemTitle->appendChild( $doc->createTextNode( $itemTitleText ) );
00777 
00778                 // description RSS element with respective class attribute content
00779                 $descriptionContent =  $description->attribute( 'content' );
00780                 if ( $descriptionContent instanceof eZXMLText )
00781                 {
00782                     $outputHandler =  $descriptionContent->attribute( 'output' );
00783                     $itemDescriptionText = $outputHandler->attribute( 'output_text' );
00784                 }
00785                 else
00786                 {
00787                     $itemDescriptionText = $descriptionContent;
00788                 }
00789 
00790                 $itemDescription = $doc->createElement( 'description' );
00791                 $itemDescription->appendChild( $doc->createTextNode( $itemDescriptionText ) );
00792 
00793                 $itemLink = $doc->createElement( 'link' );
00794                 $itemLink->appendChild( $doc->createTextNode( $nodeURL ) );
00795 
00796                 $item = $doc->createElementNS( 'http://purl.org/rss/1.0/', 'item' );
00797                 $item->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:about', $nodeURL );
00798 
00799                 $item->appendChild( $itemTitle );
00800                 $item->appendChild( $itemLink );
00801                 $item->appendChild( $itemDescription );
00802 
00803                 $root->appendChild( $item );
00804             }
00805         }
00806 
00807         return $doc;
00808     }
00809 
00810     /**
00811      * Generates an RSS feed document with type $type and returns it as a string.
00812      *
00813      * It uses the Feed component from eZ Components.
00814      *
00815      * Supported types: 'rss1', 'rss2', 'atom'.
00816      *
00817      * @since 4.2
00818      * @param string $type One of 'rss1', 'rss2' and 'atom'
00819      * @return string XML document as a string
00820      */
00821     function generateFeed( $type )
00822     {
00823         $locale = eZLocale::instance();
00824 
00825         // Get URL Translation settings.
00826         $config = eZINI::instance();
00827         if ( $config->variable( 'URLTranslator', 'Translation' ) == 'enabled' )
00828         {
00829             $useURLAlias = true;
00830         }
00831         else
00832         {
00833             $useURLAlias = false;
00834         }
00835 
00836         if ( $this->attribute( 'url' ) == '' )
00837         {
00838             $baseItemURL = '';
00839             eZURI::transformURI( $baseItemURL, false, 'full' );
00840             $baseItemURL .= '/';
00841         }
00842         else
00843         {
00844             $baseItemURL = $this->attribute( 'url' ) . '/'; //.$this->attribute( 'site_access' ).'/';
00845         }
00846 
00847         $feed = new ezcFeed();
00848 
00849         $feed->title = htmlspecialchars(
00850             $this->attribute( 'title' ), ENT_NOQUOTES, 'UTF-8'
00851         );
00852 
00853         $link = $feed->add( 'link' );
00854         $link->href = htmlspecialchars( $baseItemURL, ENT_NOQUOTES, 'UTF-8' );
00855 
00856         $feed->description = htmlspecialchars(
00857             $this->attribute( 'description' ), ENT_NOQUOTES, 'UTF-8'
00858         );
00859         $feed->language = $locale->httpLocaleCode();
00860 
00861         // to add the <atom:link> element needed for RSS2
00862         $feed->id = htmlspecialchars(
00863             $baseItemURL . 'rss/feed/' . $this->attribute( 'access_url' ),
00864             ENT_NOQUOTES, 'UTF-8'
00865         );
00866 
00867         // required for ATOM
00868         $feed->updated = time();
00869         $author        = $feed->add( 'author' );
00870         $author->email = htmlspecialchars(
00871             $config->variable( 'MailSettings', 'AdminEmail' ),
00872             ENT_NOQUOTES, 'UTF-8'
00873         );
00874         $creatorObject = eZContentObject::fetch( $this->attribute( 'creator_id' ) );
00875         if ( $creatorObject instanceof eZContentObject )
00876         {
00877             $author->name = htmlspecialchars(
00878                 $creatorObject->attribute('name'), ENT_NOQUOTES, 'UTF-8'
00879             );
00880         }
00881 
00882         $imageURL = $this->fetchImageURL();
00883         if ( $imageURL !== false )
00884         {
00885             $imageURL = htmlspecialchars( $imageURL, ENT_NOQUOTES, 'UTF-8' );
00886             $image = $feed->add( 'image' );
00887 
00888             // Required for RSS1
00889             $image->about = $imageURL;
00890 
00891             $image->url = $imageURL;
00892             $image->title = htmlspecialchars(
00893                 $this->attribute( 'title' ), ENT_NOQUOTES, 'UTF-8'
00894             );
00895             $image->link = $link->href;
00896         }
00897 
00898         $cond = array(
00899                     'rssexport_id'  => $this->ID,
00900                     'status'        => $this->Status
00901                     );
00902         $rssSources = eZRSSExportItem::fetchFilteredList( $cond );
00903 
00904         $nodeArray = eZRSSExportItem::fetchNodeList( $rssSources, $this->getObjectListFilter() );
00905 
00906         if ( is_array( $nodeArray ) && count( $nodeArray ) )
00907         {
00908             $attributeMappings = eZRSSExportItem::getAttributeMappings( $rssSources );
00909 
00910             foreach ( $nodeArray as $node )
00911             {
00912                 if ( $node->attribute('is_hidden') && !eZContentObjectTreeNode::showInvisibleNodes() )
00913                 {
00914                     // if the node is hidden skip past it and don't add it to the RSS export
00915                     continue;
00916                 }
00917                 $object = $node->attribute( 'object' );
00918                 $dataMap = $object->dataMap();
00919                 if ( $useURLAlias === true )
00920                 {
00921                     $nodeURL = $this->urlEncodePath( $baseItemURL . $node->urlAlias() );
00922                 }
00923                 else
00924                 {
00925                     $nodeURL = $baseItemURL . 'content/view/full/' . $node->attribute( 'node_id' );
00926                 }
00927 
00928                 // keep track if there's any match
00929                 $doesMatch = false;
00930                 // start mapping the class attribute to the respective RSS field
00931                 foreach ( $attributeMappings as $attributeMapping )
00932                 {
00933                     // search for correct mapping by path
00934                     if ( $attributeMapping[0]->attribute( 'class_id' ) == $object->attribute( 'contentclass_id' ) and
00935                          in_array( $attributeMapping[0]->attribute( 'source_node_id' ), $node->attribute( 'path_array' ) ) )
00936                     {
00937                         // found it
00938                         $doesMatch = true;
00939                         // now fetch the attributes
00940                         $title =  $dataMap[$attributeMapping[0]->attribute( 'title' )];
00941                         // description is optional
00942                         $descAttributeIdentifier = $attributeMapping[0]->attribute( 'description' );
00943                         $description = $descAttributeIdentifier ? $dataMap[$descAttributeIdentifier] : false;
00944                         // category is optional
00945                         $catAttributeIdentifier = $attributeMapping[0]->attribute( 'category' );
00946                         $category = $catAttributeIdentifier ? $dataMap[$catAttributeIdentifier] : false;
00947                         // enclosure is optional
00948                         $enclosureAttributeIdentifier = $attributeMapping[0]->attribute( 'enclosure' );
00949                         $enclosure = $enclosureAttributeIdentifier ? $dataMap[$enclosureAttributeIdentifier] : false;
00950                         break;
00951                     }
00952                 }
00953 
00954                 if( !$doesMatch )
00955                 {
00956                     // no match
00957                     eZDebug::writeError( 'Cannot find matching RSS attributes for datamap on node: ' . $node->attribute( 'node_id' ), __METHOD__ );
00958                     return null;
00959                 }
00960 
00961                 // title RSS element with respective class attribute content
00962                 $titleContent =  $title->attribute( 'content' );
00963                 if ( $titleContent instanceof eZXMLText )
00964                 {
00965                     $outputHandler = $titleContent->attribute( 'output' );
00966                     $itemTitleText = $outputHandler->attribute( 'output_text' );
00967                 }
00968                 else
00969                 {
00970                     $itemTitleText = $titleContent;
00971                 }
00972 
00973                 $item = $feed->add( 'item' );
00974 
00975                 $item->title = htmlspecialchars( $itemTitleText, ENT_NOQUOTES, 'UTF-8' );
00976 
00977                 $link = $item->add( 'link' );
00978                 $link->href = htmlspecialchars( $nodeURL, ENT_NOQUOTES, 'UTF-8' );
00979 
00980                 switch ( $type )
00981                 {
00982                     case 'rss2':
00983                         $item->id = $object->attribute( 'remote_id' );
00984                         $item->id->isPermaLink = false;
00985                         break;
00986                     default:
00987                         $item->id = $nodeURL;
00988                 }
00989 
00990                 $itemCreatorObject = $node->attribute('creator');
00991                 if ( $itemCreatorObject instanceof eZContentObject )
00992                 {
00993                     $author = $item->add( 'author' );
00994                     $author->name = htmlspecialchars(
00995                         $itemCreatorObject->attribute('name'), ENT_NOQUOTES, 'UTF-8'
00996                     );
00997                     $author->email = $config->variable( 'MailSettings', 'AdminEmail' );
00998                 }
00999 
01000                 // description RSS element with respective class attribute content
01001                 if ( $description )
01002                 {
01003                     $descContent = $description->attribute( 'content' );
01004                     if ( $descContent instanceof eZXMLText )
01005                     {
01006                         $outputHandler =  $descContent->attribute( 'output' );
01007                         $itemDescriptionText = htmlspecialchars(
01008                             $outputHandler->attribute( 'output_text' ), ENT_NOQUOTES, 'UTF-8'
01009                         );
01010                     }
01011                     else if ( $descContent instanceof eZImageAliasHandler )
01012                     {
01013                         $itemImage   = $descContent->hasAttribute( 'rssitem' ) ? $descContent->attribute( 'rssitem' ) : $descContent->attribute( 'rss' );
01014                         $origImage   = $descContent->attribute( 'original' );
01015                         eZURI::transformURI( $itemImage['full_path'], true, 'full' );
01016                         eZURI::transformURI( $origImage['full_path'], true, 'full' );
01017                         $itemDescriptionText = '&lt;a href="' . htmlspecialchars( $origImage['full_path'] )
01018                                              . '"&gt;&lt;img alt="' . htmlspecialchars( $descContent->attribute( 'alternative_text' ) )
01019                                              . '" src="' . htmlspecialchars( $itemImage['full_path'] )
01020                                              . '" width="' . $itemImage['width']
01021                                              . '" height="' . $itemImage['height']
01022                                              . '" /&gt;&lt;/a&gt;';
01023                     }
01024                     else
01025                     {
01026                         $itemDescriptionText = htmlspecialchars(
01027                             $descContent, ENT_NOQUOTES, 'UTF-8'
01028                         );
01029                     }
01030                     $item->description = $itemDescriptionText;
01031                 }
01032 
01033                 // category RSS element with respective class attribute content
01034                 if ( $category )
01035                 {
01036                     $categoryContent =  $category->attribute( 'content' );
01037                     if ( $categoryContent instanceof eZXMLText )
01038                     {
01039                         $outputHandler = $categoryContent->attribute( 'output' );
01040                         $itemCategoryText = $outputHandler->attribute( 'output_text' );
01041                     }
01042                     elseif ( $categoryContent instanceof eZKeyword )
01043                     {
01044                         $itemCategoryText = $categoryContent->keywordString();
01045                     }
01046                     else
01047                     {
01048                         $itemCategoryText = $categoryContent;
01049                     }
01050 
01051                     if ( $itemCategoryText )
01052                     {
01053                         $cat = $item->add( 'category' );
01054                         $cat->term = htmlspecialchars(
01055                             $itemCategoryText, ENT_NOQUOTES, 'UTF-8'
01056                         );
01057                     }
01058                 }
01059 
01060                 // enclosure RSS element with respective class attribute content
01061                 if ( $enclosure )
01062                 {
01063                     $encItemURL       = false;
01064                     $enclosureContent = $enclosure->attribute( 'content' );
01065                     if ( $enclosureContent instanceof eZMedia )
01066                     {
01067                         $enc         = $item->add( 'enclosure' );
01068                         $enc->length = $enclosureContent->attribute('filesize');
01069                         $enc->type   = $enclosureContent->attribute('mime_type');
01070                         $encItemURL = 'content/download/' . $enclosure->attribute('contentobject_id')
01071                                     . '/' . $enclosureContent->attribute( 'contentobject_attribute_id' )
01072                                     . '/' . urlencode( $enclosureContent->attribute( 'original_filename' ) );
01073                         eZURI::transformURI( $encItemURL, false, 'full' );
01074                     }
01075                     else if ( $enclosureContent instanceof eZBinaryFile )
01076                     {
01077                         $enc         = $item->add( 'enclosure' );
01078                         $enc->length = $enclosureContent->attribute('filesize');
01079                         $enc->type   = $enclosureContent->attribute('mime_type');
01080                         $encItemURL = 'content/download/' . $enclosure->attribute('contentobject_id')
01081                                     . '/' . $enclosureContent->attribute( 'contentobject_attribute_id' )
01082                                     . '/version/' . $enclosureContent->attribute( 'version' )
01083                                     . '/file/' . urlencode( $enclosureContent->attribute( 'original_filename' ) );
01084                         eZURI::transformURI( $encItemURL, false, 'full' );
01085                     }
01086                     else if ( $enclosureContent instanceof eZImageAliasHandler )
01087                     {
01088                         $enc         = $item->add( 'enclosure' );
01089                         $origImage   = $enclosureContent->attribute( 'original' );
01090                         $enc->length = $origImage['filesize'];
01091                         $enc->type   = $origImage['mime_type'];
01092                         $encItemURL  = $origImage['full_path'];
01093                         eZURI::transformURI( $encItemURL, true, 'full' );
01094                     }
01095 
01096                     if ( $encItemURL )
01097                     {
01098                         $enc->url = htmlspecialchars( $encItemURL, ENT_NOQUOTES, 'UTF-8' );
01099                     }
01100                 }
01101 
01102                 $item->published = $object->attribute( 'published' );
01103                 $item->updated = $object->attribute( 'published' );
01104             }
01105         }
01106         return $feed->generate( $type );
01107     }
01108 
01109     /*!
01110      \private
01111 
01112      Fetch Image from current ezrss export object. If non exist, or invalid, return false
01113 
01114      \return valid image url
01115     */
01116     function fetchImageURL()
01117     {
01118 
01119         $imageNode =  $this->attribute( 'image_node' );
01120         if ( !$imageNode )
01121             return false;
01122 
01123         $imageObject =  $imageNode->attribute( 'object' );
01124         if ( !$imageObject )
01125             return false;
01126 
01127         $dataMap =  $imageObject->attribute( 'data_map' );
01128         if ( !$dataMap )
01129             return false;
01130 
01131         $imageAttribute =  $dataMap['image'];
01132         if ( !$imageAttribute )
01133             return false;
01134 
01135         $imageHandler =  $imageAttribute->attribute( 'content' );
01136         if ( !$imageHandler )
01137             return false;
01138 
01139         $imageAlias =  $imageHandler->imageAlias( 'rss' );
01140         if( !$imageAlias )
01141             return false;
01142 
01143         $url = eZSys::hostname() . eZSys::wwwDir() .'/'. $imageAlias['url'];
01144         $url = preg_replace( "#^(//)#", "/", $url );
01145 
01146         return 'http://'.$url;
01147     }
01148 
01149     /*!
01150      \private
01151 
01152      Performs rawurlencode() on the path part of the URL. The rest is not touched.
01153 
01154      \return partially encoded url
01155     */
01156     function urlEncodePath( $url )
01157     {
01158         // Raw encode the path part of the URL
01159         $urlComponents = parse_url( $url );
01160         $pathParts = explode( '/', $urlComponents['path'] );
01161         foreach ( $pathParts as $key => $pathPart )
01162         {
01163             $pathParts[$key] = rawurlencode( $pathPart );
01164         }
01165         $encodedPath = implode( '/', $pathParts );
01166 
01167         // Rebuild the URL again, like this: scheme://user:pass@host/path?query#fragment
01168         $encodedUrl = $urlComponents['scheme'] . '://';
01169 
01170         if ( isset( $urlComponents['user'] ) )
01171         {
01172             $encodedUrl .= $urlComponents['user'];
01173             if ( isset( $urlComponents['pass'] ) )
01174             {
01175                 $encodedUrl .= ':' . $urlComponents['pass'];
01176             }
01177             $encodedUrl .= '@';
01178         }
01179 
01180         $encodedUrl .= $urlComponents['host'];
01181         if ( isset( $urlComponents['port'] ) )
01182         {
01183             $encodedUrl .= ':' . $urlComponents['port'];
01184         }
01185         $encodedUrl .= $encodedPath;
01186 
01187         if ( isset( $urlComponents['query'] ) )
01188         {
01189             $encodedUrl .= '?' . $urlComponents['query'];
01190         }
01191 
01192         if ( isset( $urlComponents['fragment'] ) )
01193         {
01194             $encodedUrl .= '#' . $urlComponents['fragment'];
01195         }
01196 
01197         return $encodedUrl;
01198     }
01199 }
01200 ?>