<?php
/**
 * Implementation of blog administration controller for entries
 *
 * @package koubel homepage
 * @author Miroslav Kubelik (koubel@volny.cz)
 * @copyright Copyright (c) 2007-2009 Miroslav Kubelik
 * @license http://opensource.org/licenses/gpl-license.php GNU GPL
 */

/**
 * Serves administration actions for blog entries
 *
 * @package koubel homepage
 * @author Miroslav Kubelik (koubel@volny.cz)
 * @copyright Copyright (c) 2007-2009 Miroslav Kubelik
 * @license http://opensource.org/licenses/gpl-license.php GNU GPL
 */
class Blog_Admin_EntryController extends Zend_Controller_Action
{
    /** count of entries in a list */
    const ENTRIES_PER_PAGE = 15;

    /**
     * List of entries.
     */
    public function indexAction()
    {
        $article = new Article();
        $this->view->articles = $article->getAll(
            $this->_helper->Paginator->getPageNumber(),
            self::ENTRIES_PER_PAGE
        );
        $this->view->paginator = $article->getPaginator();

        if ($this->_helper->flashMessenger->hasMessages()) {
            $this->view->messages=$this->_helper->flashMessenger->getMessages();
        }
    }

    /**
     * Views a dialog for addition of a new article.
     * It is used as target for the forward or redirect from the another actions, hence
     * it can set a form data from an old request, which will not serve in another actions.
     */
    public function addAction()
    {
        $author=new Author();
        $this->view->authors=$author->getAll();

        $category=new Category();
        $this->view->allCategories=$category->getAll();

        //article categories are empty by default
        $articleCategories=array();

        if ($this->_helper->flashMessenger->hasMessages()) {
            $this->view->messages=$this->_helper->flashMessenger->getMessages();
        }

        //in POST are old data, it occures when current submitted data are invalid, so
        //we print old data in form, user can corrects these.
        if (isset($_POST["title"])) {
            $data=$_POST;
            if (isset($data["articleCategories"])) {
                //set article categories from the database by previous submitted category values
                $articleCategories=$category->findByArrId( $data["articleCategories"]);
                unset($data["articleCategories"]);
            }
            $this->view->formData=$data;
        } else {
            //some data are predefined
            if (!isset($this->view->formData)) $this->view->formData=array();
            $this->view->formData["format"]=Dao_Article::FORMAT_HTML;
        }

        $this->view->articleCategories=$articleCategories;
    }


    /**
     * Action, which is as aim for the reciving of the add new article entry dialog data.
     * It tries to save a new article entry into the database, if something fails,
     * forwards to the addAction. If additions was OK, redirects
     * to the addAction with some flash message.
     */
    public function addToDbAction()
    {
        // Don't auto render
        $this->_helper->viewRenderer->setNoRender();
        $data=$_POST;

        $inputFilter=self::getEntryFormFilter();
        $inputFilter->setData($data);

        if ($inputFilter->isValid()) {
            $authorId=$inputFilter->author_id;
            $title = $inputFilter->getUnescaped('title');
            $titleUrl = Utils::friendlyUrl($inputFilter->getUnescaped('title'));
            $postedDate = new Zend_Date($inputFilter->posted,'dd-MM-yyyy HH:mm:ss',"en_US");
            $text = $inputFilter->getUnescaped('text');
            $format = $inputFilter->format;

            $article = new Article();

            try {
                $article->setAuthorId($authorId);
                $article->setTitle(ucfirst($title));
                $article->setTitleUrl($titleUrl);
                $article->setPosted($postedDate->toString('yyyy-MM-dd HH:mm:ss'));
                $article->setText($text);
                $article->setFormat($format);
                //get plain text
                $article->setPlainText(self::getPlainArticleText($article));

                //insert into database
                $article->insert();

                //add into the categories
                $article->addToCategoriesbyArrId($data["articleCategories"]);

            } catch (Zend_Db_Exception $e) {
                $message = "Cannot save record, database error: ".$e->getMessage();
                $this->view->messages = array($message);
                $this->_forward("add");
                return;
            }

            $newEntryUrl = $this->_helper->url->url(
                array(
                    "controller" => "entry",
                    "action" => "edit",
                    "itemId" => $article->getId()
                ),
                "blogAdminItemAction"
            );

            $message="Record <a href=\"".$newEntryUrl."\">$title</a> saved";
            $this->_helper->flashMessenger->addMessage($message);

            //send trackbacks, only when some trackbacks url are presented
            if (strlen($inputFilter->trackbackURLs)) {
                $this->sendTrackBacks($article,
                    explode(" ",$inputFilter->trackbackURLs)
                );
            }

            $this->_helper->redirector->setGoto("add");
            $this->_helper->redirector->redirectAndExit();

        } else {
            //invalid data
            $messages[]="Data are invalid:";
            $messages=array_merge($messages,Utils::getMessagesOneArray($inputFilter));
            $this->view->messages=$messages;
            $this->_forward("add");
            return;
        }
    }

    /**
     * Prepares and returns input filter for the blog entry dialog.
     * @return Zend_Filter_Input
     */
    public static function getEntryFormFilter()
    {
        $filters = array(
            'title'   => 'StringTrim',
            'posted' => 'StringTrim',
            'text' => 'StringTrim',
            'trackbackURLs' => 'StringTrim'
        );

        $validators = array(
            'author_id'   => array('NotEmpty','Digits'),
            'title'   => 'NotEmpty',
            'format' => new Zend_Validate_InArray(Article::getAvailFormats()),
            'posted' => array('NotEmpty',new DateTimeValid()),
            'text' => 'NotEmpty',
            'trackbackURLs' => array(new MoreUrlValid(),'allowEmpty' => true)
        );

        $input = new Zend_Filter_Input($filters, $validators);
        return $input;
    }

    /**
     * Search for a article entry - view for a search form
     */
    public function searchAction()
    {
    }

    /**
     * Action, which is aim for a search article entry dialog.
     * When input data are invalid, forwards to the {@link searchAction()}.
     */
    public function searchResultAction()
    {
        if ($this->_hasParam("title")) {
            $data=$this->getRequest()->getPost();
            $filters = array(
                'title'   => 'StringTrim',
                'posted' => 'StringTrim',
                'text' => 'StringTrim',
                'from' => 'StringTrim',
                'to' => 'StringTrim',
            );

            $validators = array();

            $inputFilter = new Zend_Filter_Input($filters,$validators);

            $inputFilter->setData($data);

            if ($inputFilter->isValid()) {
                $params=array("title"=> $inputFilter->getUnescaped('title'),
                    "posted"=> $inputFilter->posted,
                    "text"=> $inputFilter->getUnescaped('text'),
                    "from"=> $inputFilter->from,
                    "to"=> $inputFilter->to,
                );
            } else {
                //invalid data
                $messages[]="Data are invalid:";
                $messages=array_merge($messages,Utils::getMessagesOneArray($inputFilter));
                $this->view->messages=$messages;
                $this->_forward("search");
                return;
            }
        }

        //search object
        $search=Search::factorySearch("SearchBlogArticle");
        //set the pagination
        $search->setPageParams(
            $this->_helper->Paginator->getPageNumber(),
            self::ENTRIES_PER_PAGE
        );

        //set search parameters if these are in the request, otherwise these are loaded from
        //a session in the search factory
        if (isset($params)) {
            $search->setParams($params);
        }

        $article=new Article();
        //found items
        $this->view->foundItems = $article->getFound($search);
        $this->view->paginator = $search->getPaginator();

        $this->_helper->viewRenderer->setRender('index');

        //save search params to session if we have more then one page
        if (count($this->view->paginator)>1) {
            $search->saveToSession();
        }
    }

    /**
     * Returns plain text for the article.
     * Uses pureHtml.php view script, which just calls textRenderer view helper
     * @return string article text without all tags (no wiki and html tags)
     */
    public static function getPlainArticleText(Article $article)
    {
        $viewRenderer=Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
        $view = $viewRenderer->view;
        //gets striped text, just plain txt used for search
        $view->article=$article;
        $text=$view->render("pureHtml.phtml");
        //must be called before html_entity_decode
        $text=WikiText_Utils::removeEntities($text);

        $text=html_entity_decode(strip_tags($text),ENT_QUOTES,"UTF-8");

        //more then 3 blank characters are just 2 - in trimmed text
        $text=preg_replace("/(\s){3,}/","$1$1",trim($text));
        return $text;
    }

    /**
     * Send all trackbacks for the article.
     *
     * For every trackback sets the flashMessanger message if trackback was successfull
     * or not.
     *
     * @param Article $article
     * @param array $urls array with the trackback urls, on these trackbacks will be send.
     */
    public function sendTrackBacks(Article $article,array $urls)
    {
        $articleUrlUnique = AppBaseUrl::getUri() . $this->_helper->url->url(
            array("entry" => $article->getTitleUrl()),
            "blogArticle");

        //trackback library object
        $trackBack=new TrackBack($articleUrlUnique);
        //set log
        $trackBack->setLog(HomeWebApp::getInstance()->getLog());
        $trackBack->setBlogName("Mirin blog");
        $trackBack->setExcerpt(substr(trim($article->getPlainText()),0,400));
        $trackBack->setTitle($article->getTitle());

        foreach($urls as $pingUrl) {
            $trackBack->setPingUrl($pingUrl);
            if ($trackBack->ping()) {
                $this->_helper->flashMessenger->addMessage("trackback to the $pingUrl was send successfully");
            } else {
                $this->_helper->flashMessenger->addMessage("trackback to the $pingUrl was rejected, message: "
                    . $trackBack->getErrorMessage());
            }
        }
    }

    /**
     * Views a dialog for an edit article page with a form.
     * It is used as target for the forward or redirect from the another actions, hence
     * it can set a form data from an old request, which will not serve in another actions.
     */
    public function editAction()
    {
        $article=new Article();
        $category=new Category();
        $author=new Author();

        //in POST are old data, it occures when current submitted data are invalid, so
        //we print old data in form, user can corrects these.
        if (isset($_POST["entryId"])) {
            $data=$_POST;
            if (isset($data["articleCategories"])) {
                //set article categories from the database by previous submitted category values
                $articleCategories=$category->findByArrId($data["articleCategories"]);
                unset($data["articleCategories"]);
            } else {
                $articleCategories=array();
            }
            $this->view->formData=$data;
            $this->view->formData["id"]=$data["entryId"];
        } else {
            //no old data, gets data from database
            $article->findById($this->_getParam("itemId"));
            $articleCategories=$article->getCategories();
            $this->view->formData=$article->getDaoRow(false)->toArray();

            //special format for a datetime
            $date=new Zend_Date($article->getPosted(),Zend_Date::ISO_8601,"en_US");
            $this->view->formData["posted"]=$date->toString('dd-MM-yyyy HH:mm:ss');
        }

        $this->view->authors = $author->getAll();

        $this->view->allCategories = $category->getAll();

        $this->view->articleCategories = $articleCategories;

        if ($this->_helper->flashMessenger->hasMessages()) {
            $this->view->messages = $this->_helper->flashMessenger->getMessages();
        }
    }

    /**
     * Updates article entry in the database.
     * If something fails, forwards to the editBlogEntry. If the database update was OK, redirects
     * to the editEntry with some flash message.
     */
    public function updateDbAction()
    {
        // Don't auto render
        $this->_helper->viewRenderer->setNoRender();
        $data=$_POST;

        $inputFilter=self::getEntryFormFilter();
        $inputFilter->setData($data);

        if ($inputFilter->isValid()) {
            $authorId=$inputFilter->author_id;
            $title=$inputFilter->getUnescaped('title');
            $titleUrl=Utils::friendlyUrl($inputFilter->getUnescaped('title'));
            $postedDate=new Zend_Date($inputFilter->posted,'dd-MM-yyyy HH:mm:ss',"en_US");
            $text=$inputFilter->getUnescaped('text');
            $format=$inputFilter->format;

            $article=new Article();

            try {
                $article->setId($data['entryId']);
                $article->setAuthorId($authorId);
                $article->setTitle(ucfirst($title));
                $article->setTitleUrl($titleUrl);
                $article->setPosted($postedDate->toString('yyyy-MM-dd HH:mm:ss'));
                $article->setText($text);
                $article->setFormat($format);
                //get plain text
                $article->setPlainText(self::getPlainArticleText($article));

                $article->update();
                $article->updateCategories(isset($data["articleCategories"]) ? $data["articleCategories"] : array() );
            } catch (Zend_Db_Exception $e) {
                $message="Cannot update record, database error: ".$e->getMessage();
                $this->view->messages=array($message);
                $this->_forward("edit");
                return;
            }

            $editEntryUrl = $this->_helper->url->url(
                array(
                    "controller" => "entry",
                    "action" => "edit",
                    "itemId" => $article->getId()
                ),
                "blogAdminItemAction"
            );

            $message="Record was updated";

            //invalidate xhml article cache
            $article->invalidateCache();

            $this->_helper->flashMessenger->addMessage($message);
            $this->_helper->redirector->gotoUrlAndExit($editEntryUrl,array("prependBase"=>false));
            return;

        } else {
            //invalid data
            $messages[]="Data are invalid:";
            $messages=array_merge($messages,Utils::getMessagesOneArray($inputFilter));
            $this->view->messages=$messages;
            $this->_forward("edit");
            return;
        }
    }

    /**
     * Action, which is complete responsible for a removing entry from the database.
     * It views dialog for the confirmation, than if we realy want delete item and
     * serves database removing also.
     */
    public function deleteAction()
    {
        $article = new Article();
        $itemId = $this->_getParam("itemId");
        $article->findById($itemId);

        //"really want" delete flag
        $delflag = false;

        //confirmation was send
        if (isset($_GET['wantdel']))
        {
            //No - redirect entry list
            if ($_GET['wantdel']=="No") {
                $this->_helper->redirector->gotoUrlAndExit(
                    $this->_helper->url->url(
                        array(
                            "controller" => "entry",
                            "action" => "index"
                        ),
                        "blogAdmin"
                    ),
                    array("prependBase"=>false)
                );
                return;
            }
            //Yes - delete
            $delflag = $_GET['wantdel']=="Yes" ? true : false;
        }

        //if really want delete, make it
        if ($delflag) {
            //delete from database
            try {
                $article->delete();
            } catch (Zend_Db_Exception $e) {
                $message="Cannot delete record, database error: ".$e->getMessage();
                $this->_helper->flashMessenger->addMessage($message);
                $this->_helper->redirector->gotoUrlAndExit($this->_helper->url->url(
                    array(
                        "controller" => "entry",
                        "action" => "index"
                    ),
                    "blogAdmin"),
                    array("prependBase"=>false)
                );
                return;
            }

            //redirect after successful deleting
            $this->_helper->flashMessenger->addMessage("Entry with ID: ".$article->getId()
                . " title: ".$article->getTitle()." was deleted");
            $this->_helper->redirector->gotoUrlAndExit($this->_helper->url->url(
                array(
                    "controller" => "entry",
                    "action" => "index",
                ),
                "blogAdmin"),
                array("prependBase"=>false)
            );
            return;
        }

        $this->view->subject = "blog entry " . $article->getId() . ": "
            . $article->getTitle();
        $this->view->wasDeleted = $delflag;
        //delete url
        $this->view->deleteUrl = $this->_helper->url->url(
            array(
                "controller" => "entry",
                "action" => "delete",
                "itemId" => $article->getId()
            ),
            "blogAdminItemAction"
        );

        if ($this->_helper->flashMessenger->hasMessages()) {
            $this->view->messages=$this->_helper->flashMessenger->getMessages();
        }

        $this->_helper->viewRenderer->setRender("admin/deleteItem",null,true);
    }
}
