| <?php |
| /** |
| * File containing the ezcFeedRss1 class. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Feed |
| * @version //autogentag// |
| * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @filesource |
| */ |
| |
| /** |
| * Class providing parsing and generating of RSS1 feeds. |
| * |
| * Specifications: |
| * {@link http://web.resource.org/rss/1.0/spec RSS1 Specifications} |
| * |
| * @package Feed |
| * @version //autogentag// |
| */ |
| class ezcFeedRss1 extends ezcFeedProcessor implements ezcFeedParser |
| { |
| /** |
| * Defines the feed type of this processor. |
| */ |
| const FEED_TYPE = 'rss1'; |
| |
| /** |
| * Defines the feed content type of this processor. |
| */ |
| const CONTENT_TYPE = 'application/rss+xml'; |
| |
| /** |
| * Defines the namespace for RSS1 (RDF) feeds. |
| */ |
| const NAMESPACE_URI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; |
| |
| /** |
| * Creates a new RSS1 processor. |
| * |
| * @param ezcFeed $container The feed data container used when generating |
| */ |
| public function __construct( ezcFeed $container ) |
| { |
| $this->feedContainer = $container; |
| $this->feedType = self::FEED_TYPE; |
| $this->contentType = self::CONTENT_TYPE; |
| } |
| |
| /** |
| * Returns an XML string from the feed information contained in this |
| * processor. |
| * |
| * @return string |
| */ |
| public function generate() |
| { |
| $this->xml = new DOMDocument( '1.0', 'utf-8' ); |
| $this->xml->formatOutput = 1; |
| $this->createRootElement( '2.0' ); |
| |
| $this->generateChannel(); |
| $this->generateFeedModules( $this->channel ); |
| $this->generateItems(); |
| $this->generateImage(); |
| $this->generateTextInput(); |
| |
| return $this->xml->saveXML(); |
| } |
| |
| /** |
| * Returns true if the parser can parse the provided XML document object, |
| * false otherwise. |
| * |
| * @param DOMDocument $xml The XML document object to check for parseability |
| * @return bool |
| */ |
| public static function canParse( DOMDocument $xml ) |
| { |
| if ( strpos( $xml->documentElement->tagName, 'RDF' ) === false ) |
| { |
| return false; |
| } |
| |
| $namespaceUri = $xml->documentElement->lookupNamespaceURI( null ); |
| |
| // RSS 0.90 |
| if ( $namespaceUri === "http://channel.netscape.com/rdf/simple/0.9/" ) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Parses the provided XML document object and returns an ezcFeed object |
| * from it. |
| * |
| * @throws ezcFeedParseErrorException |
| * If an error was encountered during parsing. |
| * |
| * @param DOMDocument $xml The XML document object to parse |
| * @return ezcFeed |
| */ |
| public function parse( DOMDocument $xml ) |
| { |
| $feed = new ezcFeed( self::FEED_TYPE ); |
| $rssChildren = $xml->documentElement->childNodes; |
| $channel = null; |
| |
| $this->usedPrefixes = $this->fetchUsedPrefixes( $xml ); |
| |
| foreach ( $rssChildren as $rssChild ) |
| { |
| if ( $rssChild->nodeType === XML_ELEMENT_NODE |
| && $rssChild->tagName === 'channel' ) |
| { |
| $channel = $rssChild; |
| } |
| } |
| |
| if ( $channel === null ) |
| { |
| throw new ezcFeedParseErrorException( null, "No channel tag" ); |
| } |
| |
| if ( $channel->hasAttributeNS( self::NAMESPACE_URI, 'about' ) ) |
| { |
| $feed->id = $channel->getAttributeNS( self::NAMESPACE_URI, 'about' ); |
| } |
| |
| foreach ( $channel->childNodes as $channelChild ) |
| { |
| if ( $channelChild->nodeType == XML_ELEMENT_NODE ) |
| { |
| $tagName = $channelChild->tagName; |
| |
| switch ( $tagName ) |
| { |
| case 'title': |
| case 'link': |
| case 'description': |
| $feed->$tagName = $channelChild->textContent; |
| break; |
| |
| case 'items': |
| $seq = $channelChild->getElementsByTagNameNS( self::NAMESPACE_URI, 'Seq' ); |
| if ( $seq->length === 0 ) |
| { |
| break; |
| } |
| |
| $lis = $seq->item( 0 )->getElementsByTagNameNS( self::NAMESPACE_URI, 'li' ); |
| |
| foreach ( $lis as $el ) |
| { |
| $resource = $el->getAttribute( 'resource' ); |
| if ( empty( $resource ) ) |
| { |
| // some RSS1 (RDF) feeds specify the "resource" attribute as "rdf:resource" |
| // see issue #13109 |
| $resource = $el->getAttributeNS( self::NAMESPACE_URI, 'resource' ); |
| } |
| |
| $item = $this->getNodeByAttributeNS( $xml->documentElement, 'item', self::NAMESPACE_URI, 'about', $resource ); |
| if ( $item instanceof DOMElement ) |
| { |
| $element = $feed->add( 'item' ); |
| $this->parseItem( $feed, $element, $item ); |
| } |
| } |
| break; |
| |
| case 'image': |
| $resource = $channelChild->getAttributeNS( self::NAMESPACE_URI, 'resource' ); |
| |
| $image = $this->getNodeByAttributeNS( $xml->documentElement, 'image', self::NAMESPACE_URI, 'about', $resource ); |
| $this->parseImage( $feed, $image ); |
| break; |
| |
| case 'textinput': |
| $resource = $channelChild->getAttributeNS( self::NAMESPACE_URI, 'resource' ); |
| |
| $textInput = $this->getNodeByAttributeNS( $xml->documentElement, 'textinput', self::NAMESPACE_URI, 'about', $resource ); |
| $this->parseTextInput( $feed, $textInput ); |
| break; |
| |
| default: |
| // check if it's part of a known module/namespace |
| $this->parseModules( $feed, $channelChild, $tagName ); |
| break; |
| } |
| } |
| } |
| |
| if ( $channel->hasAttribute( 'xml:lang' ) ) |
| { |
| $feed->language = $channel->getAttribute( 'xml:lang' ); |
| } |
| |
| return $feed; |
| } |
| |
| /** |
| * Creates a root node for the XML document being generated. |
| * |
| * @param string $version The RSS version for the root node |
| */ |
| private function createRootElement( $version ) |
| { |
| $rss = $this->xml->createElementNS( self::NAMESPACE_URI, 'rdf:RDF' ); |
| $this->addAttribute( $rss, 'xmlns', 'http://purl.org/rss/1.0/' ); |
| |
| $this->channel = $channelTag = $this->xml->createElement( 'channel' ); |
| $rss->appendChild( $channelTag ); |
| $this->root = $this->xml->appendChild( $rss ); |
| } |
| |
| /** |
| * Adds the required feed elements to the XML document being generated. |
| */ |
| private function generateChannel() |
| { |
| $data = $this->id; |
| |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/@about" ); |
| } |
| |
| $aboutAttr = $this->xml->createAttribute( 'rdf:about' ); |
| $aboutVal = $this->xml->createTextNode( $data ); |
| $aboutAttr->appendChild( $aboutVal ); |
| $this->channel->appendChild( $aboutAttr ); |
| |
| $elements = array( 'title', 'link', 'description' ); |
| foreach ( $elements as $element ) |
| { |
| $data = $this->$element; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/{$element}" ); |
| } |
| |
| switch ( $element ) |
| { |
| case 'link': |
| $this->generateMetaData( $this->channel, $element, $data ); |
| break; |
| |
| case 'title': |
| case 'description': |
| $this->generateMetaData( $this->channel, $element, $data ); |
| break; |
| } |
| } |
| |
| if ( !is_null( $this->language ) ) |
| { |
| $this->addAttribute( $this->channel, 'xml:lang', $this->language ); |
| } |
| |
| $items = $this->item; |
| if ( count( $items ) === 0 ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/item" ); |
| } |
| |
| $itemsTag = $this->xml->createElement( 'items' ); |
| $this->channel->appendChild( $itemsTag ); |
| $seqTag = $this->xml->createElement( 'rdf:Seq' ); |
| $itemsTag->appendChild( $seqTag ); |
| |
| foreach ( $items as $item ) |
| { |
| $about = $item->id; |
| $liTag = $this->xml->createElement( 'rdf:li' ); |
| $resourceAttr = $this->xml->createAttribute( 'resource' ); |
| $resourceVal = $this->xml->createTextNode( $about ); |
| $resourceAttr->appendChild( $resourceVal ); |
| $liTag->appendChild( $resourceAttr ); |
| $seqTag->appendChild( $liTag ); |
| } |
| |
| $image = $this->image; |
| if ( $image !== null ) |
| { |
| $imageTag = $this->xml->createElement( 'image' ); |
| |
| $about = $image->about; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/image/@about" ); |
| } |
| |
| $resourceAttr = $this->xml->createAttribute( 'rdf:resource' ); |
| $resourceVal = $this->xml->createTextNode( $about ); |
| $resourceAttr->appendChild( $resourceVal ); |
| $imageTag->appendChild( $resourceAttr ); |
| |
| $this->channel->appendChild( $imageTag ); |
| } |
| |
| $textInput = $this->textInput; |
| if ( $textInput !== null ) |
| { |
| $textInputTag = $this->xml->createElement( 'textinput' ); |
| |
| $about = $textInput->about; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/textinput/@about" ); |
| } |
| |
| $resourceAttr = $this->xml->createAttribute( 'rdf:resource' ); |
| $resourceVal = $this->xml->createTextNode( $about ); |
| $resourceAttr->appendChild( $resourceVal ); |
| $textInputTag->appendChild( $resourceAttr ); |
| |
| $this->channel->appendChild( $textInputTag ); |
| } |
| } |
| |
| /** |
| * Adds the feed items to the XML document being generated. |
| */ |
| private function generateItems() |
| { |
| foreach ( $this->item as $element ) |
| { |
| $itemTag = $this->xml->createElement( 'item' ); |
| $this->root->appendChild( $itemTag ); |
| |
| $data = $element->id; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/item/@about" ); |
| } |
| |
| $aboutAttr = $this->xml->createAttribute( 'rdf:about' ); |
| $aboutVal = $this->xml->createTextNode( $data ); |
| $aboutAttr->appendChild( $aboutVal ); |
| $itemTag->appendChild( $aboutAttr ); |
| |
| $elements = array( 'title', 'link' ); |
| foreach ( $elements as $attribute ) |
| { |
| $data = $element->$attribute; |
| |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/item/{$attribute}" ); |
| } |
| |
| $this->generateMetaData( $itemTag, $attribute, $data ); |
| } |
| |
| $elements = array( 'description', 'language' ); |
| foreach ( $elements as $attribute ) |
| { |
| $data = $element->$attribute; |
| if ( !is_null( $data ) ) |
| { |
| switch ( $attribute ) |
| { |
| case 'description': |
| $this->generateMetaData( $itemTag, $attribute, $data ); |
| break; |
| |
| case 'language': |
| $this->addAttribute( $itemTag, 'xml:lang', $data ); |
| break; |
| } |
| } |
| } |
| |
| $this->generateItemModules( $element, $itemTag ); |
| } |
| } |
| |
| /** |
| * Adds the feed image to the XML document being generated. |
| */ |
| private function generateImage() |
| { |
| $image = $this->image; |
| if ( $image !== null ) |
| { |
| $imageTag = $this->xml->createElement( 'image' ); |
| $this->root->appendChild( $imageTag ); |
| |
| $data = $image->about; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/image/@about" ); |
| } |
| |
| $aboutAttr = $this->xml->createAttribute( 'rdf:about' ); |
| $aboutVal = $this->xml->createTextNode( $data ); |
| $aboutAttr->appendChild( $aboutVal ); |
| $imageTag->appendChild( $aboutAttr ); |
| |
| $elements = array( 'title', 'url', 'link' ); |
| foreach ( $elements as $attribute ) |
| { |
| $data = $image->$attribute; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/image/{$attribute}" ); |
| } |
| |
| $this->generateMetaData( $imageTag, $attribute, $data ); |
| } |
| } |
| } |
| |
| /** |
| * Adds the feed textinput to the XML document being generated. |
| */ |
| private function generateTextInput() |
| { |
| $textInput = $this->textInput; |
| if ( $textInput !== null ) |
| { |
| $textInputTag = $this->xml->createElement( 'textinput' ); |
| $this->root->appendChild( $textInputTag ); |
| |
| $data = $textInput->about; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/textinput/@about" ); |
| } |
| |
| $aboutAttr = $this->xml->createAttribute( 'rdf:about' ); |
| $aboutVal = $this->xml->createTextNode( $data ); |
| $aboutAttr->appendChild( $aboutVal ); |
| $textInputTag->appendChild( $aboutAttr ); |
| |
| $elements = array( 'title', 'description', 'name', 'link' ); |
| foreach ( $elements as $attribute ) |
| { |
| $data = $textInput->$attribute; |
| if ( is_null( $data ) ) |
| { |
| throw new ezcFeedRequiredMetaDataMissingException( "/{$this->root->nodeName}/textinput/{$attribute}" ); |
| } |
| |
| $this->generateMetaData( $textInputTag, $attribute, $data ); |
| } |
| } |
| } |
| |
| /** |
| * Parses the provided XML element object and stores it as a feed item in |
| * the provided ezcFeed object. |
| * |
| * @param ezcFeed $feed The feed object in which to store the parsed XML element as a feed item |
| * @param ezcFeedElement $element The feed element object that will contain the feed item |
| * @param DOMElement $xml The XML element object to parse |
| */ |
| private function parseItem( ezcFeed $feed, $element, DOMElement $xml ) |
| { |
| if ( $xml->hasAttributeNS( self::NAMESPACE_URI, 'about' ) ) |
| { |
| $element->id = $xml->getAttributeNS( self::NAMESPACE_URI, 'about' ); |
| } |
| |
| foreach ( $xml->childNodes as $itemChild ) |
| { |
| if ( $itemChild->nodeType == XML_ELEMENT_NODE ) |
| { |
| $tagName = $itemChild->tagName; |
| |
| switch ( $tagName ) |
| { |
| case 'title': |
| case 'link': |
| case 'description': |
| $element->$tagName = $itemChild->textContent; |
| break; |
| |
| default: |
| // check if it's part of a known module/namespace |
| $this->parseModules( $element, $itemChild, $tagName ); |
| break; |
| } |
| } |
| } |
| |
| if ( $xml->hasAttribute( 'xml:lang' ) ) |
| { |
| $element->language = $xml->getAttribute( 'xml:lang' ); |
| } |
| } |
| |
| /** |
| * Parses the provided XML element object and stores it as a feed image in |
| * the provided ezcFeed object. |
| * |
| * @param ezcFeed $feed The feed object in which to store the parsed XML element as a feed image |
| * @param DOMElement $xml The XML element object to parse |
| */ |
| private function parseImage( ezcFeed $feed, DOMElement $xml = null ) |
| { |
| $image = $feed->add( 'image' ); |
| if ( $xml !== null ) |
| { |
| foreach ( $xml->childNodes as $itemChild ) |
| { |
| if ( $itemChild->nodeType == XML_ELEMENT_NODE ) |
| { |
| $tagName = $itemChild->tagName; |
| switch ( $tagName ) |
| { |
| case 'title': |
| case 'link': |
| case 'url': |
| $image->$tagName = $itemChild->textContent; |
| break; |
| } |
| } |
| } |
| |
| if ( $xml->hasAttributeNS( self::NAMESPACE_URI, 'about' ) ) |
| { |
| $image->about = $xml->getAttributeNS( self::NAMESPACE_URI, 'about' ); |
| } |
| } |
| } |
| |
| /** |
| * Parses the provided XML element object and stores it as a feed textinput in |
| * the provided ezcFeed object. |
| * |
| * @param ezcFeed $feed The feed object in which to store the parsed XML element as a feed textinput |
| * @param DOMElement $xml The XML element object to parse |
| */ |
| private function parseTextInput( ezcFeed $feed, DOMElement $xml = null ) |
| { |
| $textInput = $feed->add( 'textInput' ); |
| if ( $xml !== null ) |
| { |
| foreach ( $xml->childNodes as $itemChild ) |
| { |
| if ( $itemChild->nodeType == XML_ELEMENT_NODE ) |
| { |
| $tagName = $itemChild->tagName; |
| switch ( $tagName ) |
| { |
| case 'title': |
| case 'description': |
| case 'name': |
| case 'link': |
| $textInput->$tagName = $itemChild->textContent; |
| break; |
| } |
| } |
| } |
| |
| if ( $xml->hasAttributeNS( self::NAMESPACE_URI, 'about' ) ) |
| { |
| $textInput->about = $xml->getAttributeNS( self::NAMESPACE_URI, 'about' ); |
| } |
| } |
| } |
| } |
| ?> |