<?php

	lt_include( PLOG_CLASS_PATH."plugins/atom/class/xml/atom/plogatomabstractservice.class.php" );
	lt_include( PLOG_CLASS_PATH."plugins/atom/class/template/atomoutputrenderer.class.php" );	
	lt_include( PLOG_CLASS_PATH."plugins/atom/class/net/atomrequestgenerator.class.php" );
	lt_include( PLOG_CLASS_PATH."class/net/requestgenerator.class.php" );
	lt_include( PLOG_CLASS_PATH."class/dao/articles.class.php" );
	lt_include( PLOG_CLASS_PATH."class/dao/articlecategories.class.php" );	
	lt_include( PLOG_CLASS_PATH."class/gallery/dao/galleryresources.class.php" );
	lt_include( PLOG_CLASS_PATH."class/data/mimetype.class.php" );
	lt_include( PLOG_CLASS_PATH."class/data/textfilter.class.php" );
	lt_include( PLOG_CLASS_PATH."class/config/config.class.php" );
	lt_include( PLOG_CLASS_PATH."class/file/file.class.php" );
	lt_include( PLOG_CLASS_PATH."class/template/cachecontrol.class.php" );
	
	/**
	 * Takes care of service.post requests, which basically means adding
	 * new entries to the site
	 */
	class PostService extends PlogAtomAbstractService 
	{
	
		function PostService( $request )
		{
			$this->PlogAtomAbstractService( $request );
		}
		
		/**
		 * service.post sends data in a POST request so here we go!
		 */
		function doPost()
		{
		    // first of all, get the Atom document from the request
		    $document = $this->_request->getAtomDocument();
		    $contents = $document->content;
		    
		    // according to the spect there can be more than one <content> tag but dunno why... we'll make
		    // things easier here for us but LET'S KEEP THIS IN MIND!
		    if( $contents[0]->mode == ATOM_CONTENT_MODE_BASE64 ) {
		      $result = $this->doBinaryObjectPost();
		    }
		    else {
		      $result = $this->doArticlePost();
		    }
			
			// clear the cache no matter what happened
			CacheControl::resetBlogCache( $this->_blogInfo->getId());
		    
		    return $result;
		}

		/**
		 * find the right article category that has been configured in the admin interface, or
		 * the first one that can be found if none has been configured
		 */
		function findArticleCategory()
		{
			$blogSettings = $this->_blogInfo->getSettings();
			$categoryId = $blogSettings->getValue( "plugin_atom_article_category_id" );
			$articleCategories = new ArticleCategories();
			$category = $articleCategories->getCategory( $categoryId );
			if( !$categoryId ) {
				// category id not defined yet (user hasn't update the plugin settings) or doesn't exist anymore so...
				// check if there was any category and if not, find one. 
				// which one? we don't know... just one :)
				$blogCategories = $articleCategories->getBlogCategories( $this->_blogInfo->getId());
				if( !$blogCategories ) {
		          		return false;
				}
				// just take the one that came first...
				$category = array_pop( $blogCategories );
				$categoryId = $category->getId();
			}

			return $categoryId;
		}

		/**
		 * posts an article to the blog
		 */
		function doArticlePost()
		{
			// check if the user has posting permissions
			if( !$this->userHasPermission( "add_post" )) {
		          AtomLogger::Log( "User does not have add_post permission" );
		          $response = new AtomErrorResponse( "You do not have enough permissions to perform this operation", HTTP_ERROR_500 );
		          $this->setResponse( $response );		       
		          return false;				
			}
			
		    // get the atom document and its contents so that we can fill in the gaps
		    // in the Article object
		    AtomLogger::Log( "POSTing an article to the site!" );
		    
		    $document = $this->_request->getAtomDocument();
		    $contents = $document->content;		
		
		    $text  = $contents[0]->text;
		    AtomLogger::Log( "contents = $text" );
		    $title = $document->title->text;
		    AtomLogger::Log( "title = $title" );
		    
		    // process all the possible links that there could be in the document, potentially
		    $text = $this->atomLinksToHtml( $document->link ).$text;
		    
		    // find the category id that we should use
		    $categoryId = $this->findArticleCategory();
		    if( !$categoryId ) {
			$response = new AtomErrorResponse( "Error loading categories for blog ".$this->_blogInfo->getId(),
												HTTP_ERROR_500 );
		        $this->setResponse( $response );
		    }
		    AtomLogger::Log( "Using category with id = ".$categoryId );
		    $categories = Array( $categoryId );
		    		    
		    // what we're doing here is add articles to the db based on what came from the atom
		    // request so that's what we're going to do now
		    $article = new Article( $title,   // the title of the article
		                            $text,   // text of the article
		                            $categories, // category where this is going to be assigned
		                            $this->_userInfo->getId(),    // user who is posting this
		                            $this->_blogInfo->getId(),    // blog id where we are posting this
		                            POST_STATUS_PUBLISHED,     // set status to published by default
		                            0       // no reads so far
		                           );
		    $article->setCommentsEnabled( true );
					
			// check if the document should be posted at a certain time, courtesy of the
			// <issued> tag
			if( $document->issued != "" ) {
				$t = new Timestamp();
				$t->setDate( $document->issued, DATE_FORMAT_ISO_EXTENDED );
				$article->setDateObject( $t );
				AtomLogger::Log( "Article will be published ".$t->getTimestamp());
			}
			
		    // check if it went fine and if not, quit  
		    $articles = new Articles();
		    $articleId = $articles->addArticle( $article );
		    if( !$articleId ) {
		          AtomLogger::Log( "there was an error adding the article to the database!" );
		          $response = new AtomErrorResponse( "Error adding article to the database", HTTP_ERROR_500 );
		          $this->setResponse( $response );
		          
		          return false;
		    }
		                            
		    // but if it actually did go fine, then the only thing we need to do is generate the correct response
		    $newArticle = $articles->getBlogArticle( $articleId, $this->_blogInfo->getId());
			$output = new AtomOutputRenderer( "article" );
			$output->setValue( "blog", $this->_blogInfo );
			$output->setValue( "user", $this->_userInfo );
			$output->setValue( "url", new AtomRequestGenerator( $this->getBlogInfo()));
			$output->setValue( "article", $newArticle );
			
			// set the response
			$response = new AtomResponse( $output->fetch(), Array( HTTP_OK_201 ));
			$this->setResponse( $response );		              
		      
			return true;
		
		}
		
		/**
		 * detects if the value of the 'href' attribute of a <link> tag is a local resource and if so,
		 * returns an array with all the information we need from it.
		 * Local resource tags look something like:
		 *
		 * tag:resource:212.54.30.74,2004-07-23:40
		 *
		 * where the format itself is:
		 *
		 * tag:resource:SERVERNAME,YYYY-MM-DD:RESOURCEID
		 *
		 * @param linkHref the value of the href attribute of a link
		 * @return An array with all the information we need or false if the link does not point to a
		 * local resource
		 */ 
		function isLocalResourceTag( $linkHref )
		{
		      $regexp = "/tag:resource:(.+),([0-9]{4})-([0-9]{2})-([0-9]{2}):([0-9]*)$/";
		  
		      if( !preg_match( $regexp, $linkHref, $matches ))
		          return false;
		          
		      $result["server"] = $matches[1];
		      $result["date"] = $matches[2].$matches[3].$matches[4];
		      $result["id"] = $matches[5];
		      
		      return $result;
		}
		
		/**
		 * converts atom <link /> tags to html. If the <id> tag is "tag:resource:XXX", then 
		 * it means that we're dealing with a local resource and that we should try to look it
		 * up in the database
		 *
		 * @param links An array coming from the DOM model of the Atom document
		 * @return An html string
		 * @private
		 */
		function atomLinksToHtml( $links ) 
		{
		    // if there are no links, then return nothing  
            if ( !is_array( $links )) {
                AtomLogger::Log( "No links to parse!" );
                return "";
            }
                
            $resources = new GalleryResources();                

			$result = "";
                
            // otherwise, go through them...
            foreach( $links as $link ) {
                AtomLogger::Log( "-- atomLinksToHtml: Parsing link with href = ".$link->href );
                $localResData = $this->isLocalResourceTag( $link->href );
                if( !$localResData ) {
                    // if it's an external resource, just add a link to it
                    AtomLogger::Log( "  it is an external link!" );
                    $result .= "<a href=\"".$link->href."\">".$link->title."</a><br />\n";
                }
                else {
                    // if it's a local resource, then we should load it from the database and see what it is...
                    AtomLogger::Log( "  It is an internal resource with id = ".$localResData["id"] );
                    $resource  = $resources->getResource( $localResData["id"] );
                    if( $resource->isImage()) {
                        AtomLogger::Log( "  It is an image!" );
                        //$url =& RequestGenerator::getRequestGenerator( $this->_blogInfo );
                        $url = $this->_blogInfo->getBlogRequestGenerator();
                        // check which type of resource preview we should attach and generate the right link
                        $settings = $this->_blogInfo->getSettings();
                        $previewType = $settings->getValue( "plugin_atom_resource_preview_type" );
                        if( $previewType == ATOM_EMBED_MEDIUM_PREVIEW )
                            $link = $url->resourceMediumSizePreviewLink( $resource );
                        elseif( $previewType == ATOM_EMBED_FULL_SIZE_VIEW )
                            $link = $url->resourceDownloadLink( $resource );
                        else
                            $link = $url->resourcePreviewLink( $resource );                            
                            
                        $result .= '<a class="nodecoration" href="'.$url->resourceLink( $resource ).'"> '.
                            '<img class="res_image" id="res_'.$resource->getId().'" src="'.$link.'" alt="'.
                            $resource->getFileName().'" /></a><br />';
                        AtomLogger::Log( "Done!" );
                    }
                    else {
                        $url = $this->_blogInfo->getBlogRequestGenerator();
                        AtomLogger::Log( "  It is something else!" );
                        $result = "<a href=\"".$url->resourceLink( $resource )."\">".$resource->getFileName()."</a><br />\n";
                    }
                }
            }
            
            return $result;
		}
		
		/**
		 * @private
		 * Handles POST requests to the service.edit service when the "mode" attribute is
		 * base64, because that means that it is a binary object. In pLog, this kind of objects
		 * are stored as a resource. The atom response will return a valid id but different from
		 * article ids.
		 */  
		function doBinaryObjectPost()
		{
			// check if the user has posting permissions
			if( !$this->userHasPermission( "add_resource" )) {
				AtomLogger::Log( "User does not have add_resource permission" );
				$response = new AtomErrorResponse( "You do not have enough permissions to perform this operation", HTTP_ERROR_500 );
				$this->setResponse( $response );		       
				return false;				
			}			

			// first of all, get the contents and save them to a file
			$document = $this->_request->getAtomDocument();
			$contents = $document->content;
			$content = $contents[0];

			// we figure out the extension based on their mime type
			$mimeTypes = new MimeType();
			$fileMimeType = $content->type;
			$fileExtension = $mimeTypes->getExtension( $fileMimeType );
			AtomLogger::Log( "doBinaryObjectPost: mime type = $fileMimeType" );
			AtomLogger::Log( "doBinaryObjectPost: file extension = $fileExtension " );

			// also, come up with a name based on the contents of the <summary>...</summary> tags, which
			// are unfortunately not mandatory. If there is no summary (for instance Nokia's lifeblog uses
			// the summary to send the 'caption' of the picture in the app), then we come up with an
			// extremely random name
			$summary = $document->title->text;
			if( $summary == "" ) {
				$fileName = md5(microtime()).".$fileExtension";
			}
			else {
				// get the name of the file from whatever the client sent
				$fileName = $summary;
				// and then, make sure it doesn't have any strange characters in it...
				$tf = new Textfilter();
				$fileName = $tf->urlize( $fileName ).".$fileExtension";
			}

			AtomLogger::Log( "doBinaryObjectPost: file name = ".$fileName );

			// next step is to save the file in some temporary place
			$config =& Config::getConfig();
			$tempFolder = $config->getTempFolder();
			$filePath = $tempFolder."/".$fileName;
			AtomLogger::Log( "doBinaryPostObject: saving data to $filePath" );
			$file = new File( $filePath );
			if( !$file->open( "w+" )) {
				// if we had an error trying to save the file, then we quit...
				$response = new AtomErrorResponse( "Error creating temporary file for binary post", HTTP_ERROR_500 );
				$this->setResponse( $response );

				return false;
			} 
			// but if everything went fine, then we go ahead and save the file
			if( !$file->write( $content->text )) {
				// if we had an error trying to save the file, then we quit...
				$response = new AtomErrorResponse( "Error saving data to temporary file", HTTP_ERROR_500 );
				$this->setResponse( $response );

				return false;
			}

			//
			// now that the file has been saved somewhere in disk and we know where it is, 
			// we can add it to the gallery
			//
			$resources = new GalleryResources();
			// find the right album id
			$albumId = $this->checkMoblogAlbum();
			$resourceId = $resources->addResourceFromDisk( $this->_blogInfo->getId(), $albumId, $summary, $filePath );

			if( !$resourceId ) {
				AtomLogger::Log( "There was an error saving the resource. Error code = $resourceId" );
				$response = new AtomErrorResponse( "Error saving resource", HTTP_ERROR_500 );
				$this->setResponse( $response );

				return false;
			}

			AtomLogger::Log( "Resource was saved with resource id = $resourceId" );
			$newResource = $resources->getResource( $resourceId );
			if( !$newResource ) {
				AtomLogger::Log( "There was an error loading the new resource" );
				$response = new AtomErrorResponse( "Error loading resource", HTTP_ERROR_500 );
				$this->setResponse( $response );
				return false;		    
			}
			$output = new AtomOutputRenderer( "resource" );
			$output->setValue( "blog", $this->_blogInfo );
			$output->setValue( "user", $this->_userInfo );
			$output->setValue( "url", new AtomRequestGenerator( $this->getBlogInfo()));
			$output->setValue( "resource", $newResource );

			// set the response
			$response = new AtomResponse( $output->fetch(), Array( HTTP_OK_200 ));
			$this->setResponse( $response );
		}
		
		/**
		 * @private
		 * All files that were uploaded via Atom, will be added to an album called Moblog, since the album cannot
		 * really be specified in the atom request
		 *
		 * @param blogId the blog id
		 */
		function checkMoblogAlbum()
		{
		      // check if the name of the album was saved in the blog settings and if not
		      // use the default one
		      $blogSettings = $this->_blogInfo->getSettings();
		      $albumId = $blogSettings->getValue( "plugin_atom_gallery_resource_album_id" );
		      lt_include( PLOG_CLASS_PATH."class/gallery/dao/galleryalbums.class.php" );
		      $albums = new GalleryAlbums();
		      $album = $albums->getAlbum( $albumId );
		      if( !$album ) {
				AtomLogger::Log( "checkMoblogAlbum: album does not exist! Creating a new one..." );
				$albumId = $this->getDefaultMoblogAlbum();
		      }
		      else {
				AtomLogger::Log( "checkMoblogAlbum: Album exists, using album id = $albumId" );
				$albumId = $album->getId();
		      }	
	
		      AtomLogger::Log( "using album with id $albumId" );
		      
		      return $albumId;
		} 

		/**
		 * @private
		 * Used by the method above, to create a default album if no moblogging album was configured
		 * in the admin interface or the one that was configured does not exist aymore
		 */
		function getDefaultMoblogAlbum()
		{
		      $albumName = ATOM_DEFAULT_ALBUM_NAME;
		      
		      // there is no method to load the albums by
		      $tf = new Textfilter();
		      $mangledAlbumName = $tf->urlize( $albumName );
		      AtomLogger::Log( "getDefaultMoblogAlbum: Locating album $mangledAlbumName" );
		      $album = $albums->getAlbumByName( $mangledAlbumName, $this->_blogInfo->getId());
		      
		      // if it doesn't exist, then we have to create it
		      if( !$album ) {
		          AtomLogger::Log( "getDefaultMoblogAlbum: Album does not exist, creating it..." );
		          $t = new Timestamp();
		          $album = new GalleryAlbum( $this->_blogInfo->getId(),   // blog (owner) id
		                                     $albumName,   // name
		                                     "",     // no description
		                                     0,   // no flags either
		                                     0,    // no parent id
		                                     $t->getTimestamp(),  // current timestamp,
		                                     Array(),   // no additional properties
		                                     true     // don't make this album private
		                                    ); 
		          $albumId = $albums->addAlbum( $album );
		      }
		      else {
		          $albumId = $album->getId();
		      }
		      
			return $albumId;
		}
	}
?>