<?php

/**
 * Project: 
 * Contenido Content Management System
 * 
 * Description: 
 * Class for handling CMS Type Teaser
 * Teaser is able to teaser all articles in a category. It is also possible to set a list of articles
 * which were displayed as manual teaser. This function is not category dependant. There are also
 * several more properties like sort defintions, character limits for teaser text and a teaser headline
 * 
 * Requirements: 
 * @con_php_req 5.0
 * 
 *
 * @package    Contenido Content Types
 * @version    1.0.5
 * @author     Timo Trautmann
 * @copyright  four for business AG <www.4fb.de>
 * @license    http://www.contenido.org/license/LIZENZ.txt
 * @link       http://www.4fb.de
 * @link       http://www.contenido.org
 * @since      file available since contenido release 4.8.12
 * 
 * {@internal 
 *   created 2009-04-08
 *   modified 2009-04-14 - added possibility to expand template select by client or system setting using type 'cms_teaser'
 *   modofied 2009-04-21 - added seperate handling for xhtml
 *   modified 2009-05-04 - added sort order sequence
 *   modified 2009-10-01 - Dominik Ziegler, fixed session bug in link
 *   modified 2009-10-12 - Dominik Ziegler, fixed online/offline articles, dynamic teaser generation and translation implemented
 *   modified 2009-10-16 - Dominik Ziegler, added manual date support
 *   modified 2010-01-21 - Dominik Ziegler, strip tags from manual teaser date
 *
 *   $Id$:
 * }}
 * 
 */
if (!defined('CON_FRAMEWORK')) {
    die('Illegal call');
}

cInclude('includes', 'functions.con.php');
cInclude('includes', 'functions.api.images.php');

/**
 * Class handles content type teaser, its editmode and viewmode. All properties of teaser content type 
 * were stored as xml document in database as content in {prefix}_content
 *
 */
class Cms_Teaser {

    /**
     * Contenido configuration array
     *
     * @var array
     * @access private
     */
    private $aCfg;

    /**
     * Current id of content type CMS_TEASER[3] -> 3
     *
     * @var integer
     * @access private
     */
    private $iId;

    /**
     * Contenido database object
     *
     * @var object
     * @access private
     */
    private $oDb;

    /**
     * Idartlang of article, which is currently in edit- or viewmode
     *
     * @var integer
     * @access private
     */
    private $iIdArtLang;

    /**
     * List of fieldnames in frontend (properties) which the teaser has
     * and which were also described in the config xml document
     *
     * @var array
     * @access private
     */
    private $aTeaserData;

    /**
     * String contains value of stored content in database
     * in this case this is the config xml document which is 
     * later parsed and its settings were stored in $aSettings
     *
     * @var string
     * @access private
     */
    private $sContent;

    /**
     * Array which contains current teaser settings
     *
     * @var array
     * @access private
     */
    private $aSettings;

    /**
     * Array which contains all avariable CMS_Types and its ids
     * in current Contenido isntallation (described as hash [idtype => cmstypename])
     *
     * @var array
     * @access private
     */
    private $aCMSTypes;

    /**
     * current Contenido client id
     *
     * @var integer
     * @access private
     */
    private $iClient;

    /**
     * current Contenido language id
     *
     * @var integer
     * @access private
     */
    private $iLang;

    /**
     * Contenido Session object
     *
     * @var object
     * @access private
     */
    private $oSess;

    /**
     * Contenido configuration array for currently active client
     *
     * @var array
     * @access private
     */
    private $aCfgClient;

    /**
     * print XHTML
     *
     * @var string
     * @access private
     */
    private $sUseXHTML;

    /**
     * Placeholders for labels in frontend.
     * Important: This must be a static array!
     * @var		array
     * @access	private
     */
    private static $aTranslations = array("MORE" => "mehr");

    /**
     * Constructor of class inits some important class variables and
     * gets some Contenido global vars, so this class has no need to
     * use ugly and buggy global commands
     *
     * @param string $sContent - xml document from database containing teaser settings
     * @param integer $iNumberOfCms - CMS_TEASER[4] => 4
     * @param integer $iIdArtLang - Idartlang of current article
     * @param array $sEditLink - sEditlink for editbuttons, not currently used
     * @param array $aCfg - Contenido configuration array
     * @param array $oDB - Contenido database object (not used because we need own object (else problems by cross query in same object))
     * @param string $sContenidoLang - Contenido Backend language string
     * @param integer $iClient - Contenido client id
     * @param integer $iLang - Contenido frontend language id
     * @param array $aCfgClient - Contenido Client configuration array
     *
     * @access public
     */
    function __construct($sContent, $iNumberOfCms, $iIdArtLang, $sEditLink, $aCfg, $oDB, $sContenidoLang, $iClient, $iLang, $aCfgClient, $oSess) {
        //set arguments to class variables directly
        $this->aCfg = $aCfg;
        $this->iId = $iNumberOfCms;
        $this->iIdArtLang = $iIdArtLang;
        $this->sContent = urldecode($sContent);
        $this->iClient = $iClient;
        $this->iLang = $iLang;
        $this->aCfgClient = $aCfgClient;
        $this->oSess = $oSess;

        if (!array_key_exists("generate_xhtml", $aCfg)) {
            $this->sUseXHTML = getEffectiveSetting("generator", "xhtml", 'false');
        } else {
            $this->sUseXHTML = $aCfg['generate_xhtml'];
        }

        //init other variables with default values
        $this->aCMSTypes = null;
        $this->aSettings = array();
        $this->oDb = new DB_ConLite();

        //define class array which contains all names of teaser properties. They were also base for generating dynamic javascripts for
        //retrival this properties out of html forms and retriving their values to screen
        $this->aTeaserData = array('teaser_title', 'teaser_category', 'teaser_count', 'teaser_style', 'teaser_manual',
            'teaser_start', 'teaser_source_head', 'teaser_source_head_count', 'teaser_source_text',
            'teaser_source_text_count', 'teaser_source_image', 'teaser_source_image_count', 'teaser_filter',
            'teaser_sort', 'teaser_sort_order', 'teaser_character_limit', 'teaser_image_width',
            'teaser_image_height', 'teaser_manual_art', 'teaser_image_crop', 'teaser_source_date',
            'teaser_source_date_count');

        //if form is submitted there is a need to store current teaser settings
        //notice: there is also a need, that teaser_id is the same (case: more than ohne cms teaser is used on the same page
        if (isset($_POST['teaser_action']) && $_POST['teaser_action'] == 'store' &&
                isset($_POST['teaser_id']) && (int) $_POST['teaser_id'] == $this->iId) {
            $this->storeTeaser();
        }

        //in sContent XML Document is stored, which contains teaser settings, call function which parses this document and store
        //properties as easy accessible array into $aSettings
        if (trim($this->sContent) != '') {
            $this->readSettings();
        }

        $this->setDefaultValues();
    }

    /**
     * Returns all translation strings for mi18n.
     *
     * @param	array	$aTranslationStrings	Array with translation strings
     * @return	array	Translation strings
     */
    static public function addModuleTranslations($aTranslationStrings) {
        foreach (self::$aTranslations as $sValue) {
            $aTranslationStrings[] = $sValue;
        }

        return $aTranslationStrings;
    }

    /**
     * Functen parses XML Document which contains teaser settings
     * and store properties as array into $aSettings
     *
     * @access private
     */
    private function readSettings() {
        //use XMLReader for parsing XML document
        $oXmlReader = new XMLReader();
        $oXmlReader->XML($this->sContent);
        //store nodename in order to retrive property name when content of node is read
        $sLastNode = '';
        //in xml document there is a block <art> in this artids for manual teaser were stored
        //if we were in this block store all values to an art array
        //this variable defines if we were in this block or not
        $bPutInArtArray = false;
        //array for previous described manual art array
        $this->aSettings['teaser_manual_art'] = array();

        while ($oXmlReader->read()) {
            switch ($oXmlReader->nodeType) {
                //read property name (ignore root node or block of manual arts for teaser)
                case XMLReader::ELEMENT:
                    if ($oXmlReader->name != 'teaser' && $oXmlReader->name != 'manual_art' && $oXmlReader->name != 'art') {
                        $sLastNode = 'teaser_' . $oXmlReader->name;
                        $this->aSettings[$sLastNode] = '';
                    }
                    //if we reach <art> node store all subnotes to artnode array
                    if ($oXmlReader->name == 'art') {
                        $bPutInArtArray = true;
                    }
                    break;

                //in case of a textnode we have previous propertyname and corrsponding value -> store into aSettings
                //if we were in <art> mode store to corresponding array
                case XMLReader::TEXT:
                    if ($bPutInArtArray == true) {
                        $bPutInArtArray = false;
                        array_push($this->aSettings['teaser_manual_art'], Contenido_Security::unfilter($oXmlReader->value));
                    } else {
                        $this->aSettings[$sLastNode] = Contenido_Security::unfilter($oXmlReader->value);
                    }
                    break;
            }
        }
    }

    /**
     * Function gets all submitted values for new teaser properties from 
     * $_POST array, generates new corresponding config XML Document and
     * stores it as content, using contenido conSaveContentEntry() function
     *
     * @access private
     */
    private function storeTeaser() {
        //create new xml document, its encoding and root node
        $oXmlDom = new DOMDocument('1.0', 'iso-8859-1');
        $oXmlDom->formatOutput = true;
        $oRootNode = $oXmlDom->createElement('teaser');
        $oXmlDom->appendChild($oRootNode);

        //$this->aTeaserData defines all teaser properties, so try to read them from %_POST
        foreach ($this->aTeaserData as $sParam) {
            //in case of article list for manual teaser do a special behaviour
            if ($sParam == 'teaser_manual_art') {
                $oParam = $oXmlDom->createElement(str_replace('teaser_', '', $sParam));
                //split all arts to array
                $aArts = explode(';', Contenido_Security::toString($_POST[$sParam]));
                //for each artid generate subnote in xml document and store its value
                foreach ($aArts as $iArt) {
                    $iArt = (int) $iArt;
                    if ($iArt > 0) {
                        $oArt = $oXmlDom->createElement('art', $iArt);
                        $oParam->appendChild($oArt);
                    }
                }
            } else {
                //generate xml node for current property and store its value
                $oParam = $oXmlDom->createElement(str_replace('teaser_', '', $sParam), Contenido_Security::filter($_POST[$sParam], $this->oDb));
            }

            $oXmlDom->firstChild->appendChild($oParam);
        }

        //serialize xml document and store new version in class variable and database
        conSaveContentEntry($this->iIdArtLang, 'CMS_TEASER', $this->iId, $oXmlDom->saveXML(), true);
        $this->sContent = $oXmlDom->saveXML();
    }

    /**
     * Function which generated a select box for setting number of articles which were 
     * displayed in teaser as a maximum. Only important in editmode
     *
     * @param string $sSelected - value of select box which is selected
     * @return html string of select box
     *
     * @access private
     */
    private function getCountSelect($sSelected) {
        $this->oDb = new DB_ConLite();

        $oHtmlSelect = new cHTMLSelectElement('teaser_count', "", 'teaser_count');

        //set please chose option element
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //generate a select box containing count 1 to 20 for maximum teaser count
        for ($i = 1; $i <= 20; $i++) {
            $oHtmlSelectOption = new cHTMLOptionElement($i, $i, false);
            $oHtmlSelect->addOptionElement($i, $oHtmlSelectOption);
        }

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        return $oHtmlSelect->render();
    }

    /**
     * Function which generated a select box for setting teaser style
     * currently two seperate teaser templates were supported
     *
     * @param string $sSelected - value of select box which is selected
     * @return html string of select box
     *
     * @access private
     */
    private function getStyleSelect($sSelected) {
        $oHtmlSelect = new cHTMLSelectElement('teaser_style', "", 'teaser_style');

        //set please chose option element
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //set other avariable options manually
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Block Style"), 'cms_teaser_style_block.html', false);
        $oHtmlSelect->addOptionElement(1, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Blog Style"), 'cms_teaser_style_blog.html', false);
        $oHtmlSelect->addOptionElement(2, $oHtmlSelectOption);

        $aAdditionalOptions = getEffectiveSettingsByType('cms_teaser');
        $i = 3;
        foreach ($aAdditionalOptions as $sLabel => $sTemplate) {
            $oHtmlSelectOption = new cHTMLOptionElement($sLabel, $sTemplate, false);
            $oHtmlSelect->addOptionElement($i, $oHtmlSelectOption);
            $i++;
        }

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        return $oHtmlSelect->render();
    }

    /**
     * Function which generated a select box for setting teaser 
     * sort order argument
     *
     * @param string $sSelected - value of select box which is selected
     * @return html string of select box
     *
     * @access private
     */
    private function getSortOrderSelect($sSelected) {
        $oHtmlSelect = new cHTMLSelectElement('teaser_sort_order', "", 'teaser_sort_order');

        //set please chose option element
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //set other avariable options manually
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Ascending"), 'asc', false);
        $oHtmlSelect->addOptionElement(1, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Descending"), 'desc', false);
        $oHtmlSelect->addOptionElement(2, $oHtmlSelectOption);

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        return $oHtmlSelect->render();
    }

    /**
     * Function which provides select option for cropping teaser images
     *
     * @param string $sSelected - value of select box which is selected
     * @return html string of select box
     *
     * @access private
     */
    private function getCropSelect($sSelected) {
        $oHtmlSelect = new cHTMLSelectElement('teaser_image_crop', "", 'teaser_image_crop');

        //set please chose option element
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //set other avariable options manually
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Scaled"), 'false', false);
        $oHtmlSelect->addOptionElement(1, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Cropped"), 'true', false);
        $oHtmlSelect->addOptionElement(2, $oHtmlSelectOption);

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        return $oHtmlSelect->render();
    }

    /**
     * Function which generated a select box for setting teaser 
     * sort argument
     *
     * @param string $sSelected - value of select box which is selected
     * @return html string of select box
     *
     * @access private
     */
    private function getSortSelect($sSelected) {
        $oHtmlSelect = new cHTMLSelectElement('teaser_sort', "", 'teaser_sort');

        //set please chose option element
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //set other avariable options manually
        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Sort Sequence"), 'sortsequence', false);
        $oHtmlSelect->addOptionElement(1, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Creationdate"), 'creationdate', false);
        $oHtmlSelect->addOptionElement(2, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Publisheddate"), 'publisheddate', false);
        $oHtmlSelect->addOptionElement(3, $oHtmlSelectOption);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Modificationdate"), 'modificationdate', false);
        $oHtmlSelect->addOptionElement(4, $oHtmlSelectOption);

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        return $oHtmlSelect->render();
    }

    /**
     * Function which gets all currenty avariable content types and their ids
     * from database and store it into class variable aCMSTypes. Because this
     * information is used multiple, this way causes a better performance to get
     * this information seperately
     *
     * @access private
     */
    private function initCmsTypes() {
        $this->aCMSTypes = array();

        $sSql = 'SELECT * from %s ORDER BY type';

        $this->oDb->query(sprintf($sSql, $this->aCfg['tab']['type']));

        while ($this->oDb->next_record()) {
            $this->aCMSTypes[$this->oDb->f('idtype')] = $this->oDb->f('type');
        }
    }

    /**
     * Teaser gets informations from other articles and their content typs
     * Function builds a select box in which coresponding cms type can be selected
     * after that a text box is rendered for setting id for this conent type to get
     * informations from. This function is used three times for source defintion of
     * headline text and teaserimage
     *
     * @param unknown_type $sSelectName - name of input elements
     * @param string $sSelected - value of select box which is selected
     * @param string $sValue - current value of text box
     * @return html string of select box
     *
     * @access private
     */
    private function getTypeSelect($sSelectName, $sSelected, $sValue) {
        //generate textbox for content type id
        $oHtmlInput = new cHTMLTextbox($sSelectName . '_count', $sValue, "", "", $sSelectName . '_count');

        //generate content type select
        $oHtmlSelect = new cHTMLSelectElement($sSelectName, "", $sSelectName);

        $oHtmlSelectOption = new cHTMLOptionElement(i18n("Please choose"), '', true);
        $oHtmlSelect->addOptionElement(0, $oHtmlSelectOption);

        //use $this->aCMSTypes as basis for this select box which contains all avariable content types in system
        foreach ($this->aCMSTypes as $sKey => $sValue) {
            $oHtmlSelectOption = new cHTMLOptionElement($sValue, $sValue, false);
            $oHtmlSelect->addOptionElement($sKey, $oHtmlSelectOption);
        }

        $oHtmlSelect->setStyle("width:147px;");

        //set default value
        $oHtmlSelect->setDefault($sSelected);

        $oHtmlInput->setStyle("width:50px;");

        return $oHtmlSelect->render() . $oHtmlInput->render();
    }

    /**
     * Function is called in editmode of contenido an returns teaser view and editbutton
     *
     * @return string - escaped html code for further use in contenido and sending to browser
     *
     * @access public
     */
    public function getAllWidgetEdit() {
        $this->initCmsTypes();

        $oTpl = new Template();
        /* Set some values into javascript for a better handling */
        $oTpl->set('s', 'CON_PATH', $this->aCfg['path']['contenido_fullhtml']);
        $oTpl->set('s', 'ID', $this->iId);
        $oTpl->set('s', 'IDARTLANG', $this->iIdArtLang);
        $oTpl->set('s', 'CONTENIDO', $_REQUEST['contenido']);
        //output fields for use in javascript
        $oTpl->set('s', 'FIELDS', "'" . implode("','", $this->aTeaserData) . "'");

        /* Start set a lot of translations */
        $oTpl->set('s', 'LABEL_TEASERSETTINGS', i18n("Teasersettings"));
        $oTpl->set('s', 'LABEL_TEASERTITLE', i18n("Teasertitle"));
        $oTpl->set('s', 'LABEL_START', i18n("Teaser Startarticle"));
        $oTpl->set('s', 'LABEL_TARGET', i18n("Sourcecategory"));
        $oTpl->set('s', 'LABEL_COUNT', i18n("Number of Articles"));
        $oTpl->set('s', 'LABEL_GENERAL', i18n("General Settings"));
        $oTpl->set('s', 'LABEL_STYLE', i18n("Teaser Style"));
        $oTpl->set('s', 'LABEL_ADVANCED', i18n("Advanced Teaser Settings"));
        $oTpl->set('s', 'LABEL_FILTER', i18n("Teaser Filter"));
        $oTpl->set('s', 'LABEL_SORT', i18n("Teaser Sort"));
        $oTpl->set('s', 'LABEL_SORT_ORDER', i18n("Sort order"));
        $oTpl->set('s', 'LABEL_SOURCEHEAD', i18n("Source Headline"));
        $oTpl->set('s', 'LABEL_SOURCE', i18n("Source Settings"));
        $oTpl->set('s', 'LABEL_SOURCETEXT', i18n("Source Text"));
        $oTpl->set('s', 'LABEL_SOURCEIMAGE', i18n("Source Image"));
        $oTpl->set('s', 'LABEL_SOURCEDATE', i18n("Source Date"));

        $oTpl->set('s', 'LABEL_CAT', i18n("Category"));
        $oTpl->set('s', 'LABEL_ART', i18n("Article"));

        $oTpl->set('s', 'GENERAL', i18n("General"));
        $oTpl->set('s', 'ADVANCED', i18n("Advanced"));
        $oTpl->set('s', 'MANUAL', i18n("Manual"));
        $oTpl->set('s', 'LABEL_EXISTING_ARTICLES', i18n("Included Articles"));
        $oTpl->set('s', 'LABEL_ADD_ARTICLE', i18n("Add Article"));
        $oTpl->set('s', 'LABEL_MANUAL', i18n("Manual Teaser Settings"));
        $oTpl->set('s', 'LABEL_USE_MANUAL', i18n("Manual Teaser"));


        $oTpl->set('s', 'SIZE_SETTINGS', i18n("Size Settings"));
        $oTpl->set('s', 'LABEL_CHARACTER_LIMIT', i18n("Characterlength"));
        $oTpl->set('s', 'LABEL_IMAGE_WIDTH', i18n("Imagewidth"));
        $oTpl->set('s', 'LABEL_IMAGE_HEIGHT', i18n("Imageheight"));
        $oTpl->set('s', 'LABEL_IMAGE_CROP', i18n("Image Scale"));
        /* End set a lot of translations */

        /* Start set values into configuration array and generate select boxes used previous defined values CASE CHECKBOXES */
        if ($this->aSettings['teaser_start'] == 'true') {
            $oTpl->set('s', 'START_CHECKED', 'checked');
        } else {
            $oTpl->set('s', 'START_CHECKED', '');
        }

        if ($this->aSettings['teaser_manual'] == 'true') {
            $oTpl->set('s', 'MANUAL_CHECKED', 'checked');
        } else {
            $oTpl->set('s', 'MANUAL_CHECKED', '');
        }
        /* Start set values into configuration array and generate select boxes used previous defined values CASE CHECKBOXES */

        /* Start set values into configuration array and generate select boxes used previous defined values */
        $sCatSelect = buildCategorySelect('teaser_category', $this->aSettings['teaser_category'], 0);

        $oTpl->set('s', 'TARGET_SELECT', $sCatSelect);
        $oTpl->set('s', 'CAT_SELECT', buildCategorySelect('teaser_cat', 0, 0));
        $oTpl->set('s', 'ART_SELECT', buildArticleSelect('teaser_art', 0, 0));
        $oTpl->set('s', 'COUNT_SELECT', $this->getCountSelect($this->aSettings['teaser_count']));
        $oTpl->set('s', 'STYLE_SELECT', $this->getStyleSelect($this->aSettings['teaser_style']));
        $oTpl->set('s', 'SOURCEHEAD_SELECT', $this->getTypeSelect('teaser_source_head', $this->aSettings['teaser_source_head'], $this->aSettings['teaser_source_head_count']));
        $oTpl->set('s', 'SOURCETEXT_SELECT', $this->getTypeSelect('teaser_source_text', $this->aSettings['teaser_source_text'], $this->aSettings['teaser_source_text_count']));
        $oTpl->set('s', 'SOURCEIAMGE_SELECT', $this->getTypeSelect('teaser_source_image', $this->aSettings['teaser_source_image'], $this->aSettings['teaser_source_image_count']));
        $oTpl->set('s', 'SOURCEDATE_SELECT', $this->getTypeSelect('teaser_source_date', $this->aSettings['teaser_source_date'], $this->aSettings['teaser_source_date_count']));
        $oTpl->set('s', 'TEASER_TITLE', $this->aSettings['teaser_title']);
        $oTpl->set('s', 'FILTER_VALUE', $this->aSettings['teaser_filter']);
        $oTpl->set('s', 'SORT_SELECT', $this->getSortSelect($this->aSettings['teaser_sort']));
        $oTpl->set('s', 'SORT_ORDER_SELECT', $this->getSortOrderSelect($this->aSettings['teaser_sort_order']));
        $oTpl->set('s', 'IMAGE_CROP_SELECT', $this->getCropSelect($this->aSettings['teaser_image_crop']));
        $oTpl->set('s', 'CHARACTER_LIMIT', $this->aSettings['teaser_character_limit']);
        $oTpl->set('s', 'IMAGE_WIDTH', $this->aSettings['teaser_image_width']);
        $oTpl->set('s', 'IMAGE_HEIGHT', $this->aSettings['teaser_image_height']);
        $oTpl->set('s', 'LABEL_ADD', i18n('Add'));

        $sOptions = '';
        if (is_array($this->aSettings['teaser_manual_art'])) {
            foreach ($this->aSettings['teaser_manual_art'] as $iIdArt) {
                $sOptions .= '<option value="' . $iIdArt . '">' . $this->getArtName($iIdArt) . '</option>' . "\n";
            }
        }
        $oTpl->set('s', 'MANUAL_OPTIONS', $sOptions);
        /* End set values into configuration array and generate select boxes used previous defined values */

        $sCode = $oTpl->generate($this->aCfg['path']['contenido'] . 'templates/standard/template.cms_teaser_edit.html', 1);

        //return $this->encodeForOutput($sCode);

        return $this->getAllWidgetView(true) . $this->encodeForOutput($sCode);
    }

    /**
     * In Contenido content type code is evaled by php. To make this possible,
     * this function prepares code for evaluation
     *
     * @param string $sCode - code to escape
     * @return string - escaped code
     *
     * @access private
     */
    private function encodeForOutput($sCode) {
        $sCode = (string) $sCode;

        $sCode = AddSlashes(AddSlashes($sCode));
        $sCode = str_replace("\\\'", "'", $sCode);
        $sCode = str_replace("\$", '\\\$', $sCode);

        return $sCode;
    }

    /**
     * Function sets some default values for teaser in case that there is no
     * value definied
     *
     * @access private
     */
    private function setDefaultValues() {
        //character limit is 120 by default
        if ((int) $this->aSettings['teaser_character_limit'] == 0) {
            $this->aSettings['teaser_character_limit'] = 120;
        }

        //teaser cont is 6 by default
        if ((int) $this->aSettings['teaser_count'] == 0) {
            $this->aSettings['teaser_count'] = 6;
        }

        //teasersort is creationdate by default
        if (strlen($this->aSettings['teaser_sort']) == 0) {
            $this->aSettings['teaser_sort'] = 'creationdate';
        }

        //teaser style is liststyle by default
        if (strlen($this->aSettings['teaser_style']) == 0) {
            $this->aSettings['teaser_style'] = 'cms_teaser_style_blog.html';
        }

        //teaser image width default
        if ((int) $this->aSettings['teaser_image_width'] == 0) {
            $this->aSettings['teaser_image_width'] = 100;
        }

        //teaser image height default
        if ((int) $this->aSettings['teaser_image_height'] == 0) {
            $this->aSettings['teaser_image_height'] = 75;
        }

        //cms type head default
        if (strlen($this->aSettings['teaser_source_head']) == 0) {
            $this->aSettings['teaser_source_head'] = 'CMS_HTMLHEAD';
        }

        //cms type text default
        if (strlen($this->aSettings['teaser_source_text']) == 0) {
            $this->aSettings['teaser_source_text'] = 'CMS_HTML';
        }

        //cms type image default
        if (strlen($this->aSettings['teaser_source_image']) == 0) {
            $this->aSettings['teaser_source_image'] = 'CMS_IMG';
        }

        //cms type date default
        if (strlen($this->aSettings['teaser_source_date']) == 0) {
            $this->aSettings['teaser_source_date'] = 'CMS_DATE';
        }

        //sort order of teaser articles
        if (strlen($this->aSettings['teaser_sort_order']) == 0) {
            $this->aSettings['teaser_sort_order'] = 'asc';
        }

        //teaser image crop option
        if (strlen($this->aSettings['teaser_image_crop']) == 0 || $this->aSettings['teaser_image_crop'] == 'false') {
            $this->aSettings['teaser_image_crop'] = 'false';
        }
    }

    /**
     * Function gets path to an image of base of idupload in contenido,
     * scales this image on basis of teaser settings and returns path to
     * scaled image. It is also possible to give path to image directly,
     * in this case set fourth parameter to true
     *
     * @param integer $iImage - idupl of image to use for teaser
     * @param integer $iMaxX - maximum image width
     * @param integer $iMaxY - maximum image height
     * @param boolean $bIsFile - in case of a direct file path retrival from database is not needed
     * @return string - <img> tag contains scaled image
     *
     * @access private
     */
    private function getImage($iImage, $iMaxX, $iMaxY, $bCropped, $bIsFile = false) {
        $sContent = '';

        if ($bCropped == 'true') {
            $bCropped = true;
        } else {
            $bCropped = false;
        }

        //check if there is a need to get image path
        if ($bIsFile == false) {
            //get path out of database
            $sSQL = 'SELECT * FROM ' . $this->aCfg['tab']['upl'] . ' WHERE idupl = ' . $iImage;

            $this->oDb->query($sSQL);
            if ($this->oDb->next_record()) {
                $sTeaserImage = $this->aCfgClient[$this->iClient]['path']['frontend'] . 'upload/' . $this->oDb->f("dirname") . $this->oDb->f("filename");
            }
        } else {
            $sTeaserImage = $iImage;
        }

        //scale image if exists and return it
        if (file_exists($sTeaserImage)) {
            //Scale Image using capiImgScale
            $sImgSrc = capiImgScale($sTeaserImage, $iMaxX, $iMaxY, $bCropped);

            if ($this->sUseXHTML == 'true') {
                $sLetter = ' /';
            } else {
                $sLetter = '';
            }

            //Put Image into the teasertext
            $sContent = '<img alt="" src="' . $sImgSrc . '" class="teaser_image"' . $sLetter . '>' . $sContent;
        }

        return $sContent;
    }

    /**
     * Function retrives name of an article by its id from database
     *
     * @param integer $iIdArt - Contenido article id
     * @return string - name of article
     *
     * @access private
     */
    private function getArtName($iIdArt) {
        $iIdArt = (int) $iIdArt;

        //get article name from database
        $sSql = 'SELECT * FROM ' . $this->aCfg['tab']['art_lang'] . ' WHERE idart = ' . $iIdArt;
        $this->oDb->query($sSql);
        if ($this->oDb->next_record()) {
            return $this->oDb->f('title');
        } else {
            return 'Unknown Article';
        }
    }

    /**
     * Teaser allows to get a list of ids in which article content is searched in
     * article like 1,2,5,6 the result with largest character count is returned
     *
     * @param object $oArticle - Contenido article object
     * @param string $sIdType - Name of Content type to extract informations from
     * @param integer $iIdType - list of ids to search in
     * @return string - largest result of content
     *
     * @access private
     */
    private function getArtContent(&$oArticle, $sIdType, $iIdType) {
        $sReturn = '';

        //split ids, if there is only one id, array has only one place filled, that is also ok
        $aIds = explode(',', $iIdType);
        foreach ($aIds as $iCurIdType) {
            $sTmp = $oArticle->getContent($sIdType, $iCurIdType);
            //check for largest result and replace when new value is larger
            if (strlen($sReturn) < strlen($sTmp)) {
                $sReturn = $sTmp;
            }
        }

        return $sReturn;
    }

    /**
     * When a HTML Code is given for a Teaser image try to find a image in this code and generate
     * Teaser image on that basis
     *
     * @param string $sContent - HTML string to search image in
     * @return img tag containing scaled image
     *
     * @access private
     */
    private function extractImage($sContent) {
        $sImage = '';

        //search an image tag
        $sRegEx = "/<img[^>]*?>.*?/i";

        $aMatch = array();
        preg_match($sRegEx, $sContent, $aMatch);

        //if found extract its src content
        $sRegEx = "/(src)(=)(['\"]?)([^\"']*)(['\"]?)/i";
        $aImg = array();
        preg_match($sRegEx, $aMatch[0], $aImg);

        //check if this image lies in upload folder
        $iPos = strrpos($aImg[4], $this->aCfgClient[$this->iClient]["upload"]);
        if (!is_bool($iPos)) {
            //if it is generate full internal path to image and scale it for display using class internal function getImage()
            $sFile = $this->aCfgClient[$this->iClient]['path']['frontend'] . $aImg[4];
            $sImage = $this->getImage($sFile, $this->aSettings['teaser_image_width'], $this->aSettings['teaser_image_height'], $this->aSettings['teaser_image_crop'], true);
        }

        return $sImage;
    }

    /**
     * In edit and view mode this function fills teaser template with informations from an 
     * Contenido article object
     *
     * @param object $oArticle - Contenido Article object
     * @param object $oTpl - Contenido Template object (as reference)
     * @return boolean - success state of this operation
     *
     * @access private
     */
    private function fillTeaserTemplateEntry($oArticle, &$oTpl) {
        global $cCurrentModule;

        //get necessary informations for teaser from articles use properties in a Settings for retirval
        $sTitle = $this->getArtContent($oArticle, $this->aSettings['teaser_source_head'], $this->aSettings['teaser_source_head_count']);
        $sText = $this->getArtContent($oArticle, $this->aSettings['teaser_source_text'], $this->aSettings['teaser_source_text_count']);
        $iImage = $this->getArtContent($oArticle, $this->aSettings['teaser_source_image'], $this->aSettings['teaser_source_image_count']);
        $sDate = $this->getArtContent($oArticle, $this->aSettings['teaser_source_date'], $this->aSettings['teaser_source_date_count']);
        $iIdArt = $oArticle->getField('idart');
        $iPublished = $oArticle->getField('published');
        $iOnline = $oArticle->getField('online');

        if ($iOnline == 1) {
            //teaserfilter defines strings which must be contained in text for display. 
            //if string is defined check if article contains this string and abort, if article does not contain this string
            if ($this->aSettings['teaser_filter'] != '') {
                $iPosText = strrpos(clHtmlEntityDecode($sText), $this->aSettings['teaser_filter']);
                $iPosHead = strrpos(clHtmlEntityDecode($sTitle), $this->aSettings['teaser_filter']);
                if (is_bool($iPosText) && !$iPosText && is_bool($iPosHead) && !$iPosHead) {
                    return false;
                }
            }

            //convert date to readable format
            if (preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $iPublished, $aResults)) {
                $iPublished = $aResults[3] . '.' . $aResults[2] . '.' . $aResults[1];
            }

            //strip tags in teaser text and cut it if it is to long
            $sTitle = trim(strip_tags($sTitle));
            $sText = trim(strip_tags($sText));
            if (strlen($sText) > $this->aSettings['teaser_character_limit']) {
                $sText = capiStrTrimAfterWord($sText, $this->aSettings['teaser_character_limit']) . '...';
            }

            //try to get a teaser image directly from cms_img or try to extract if a content type is given, wich contains html
            if ((int) $iImage > 0) {
                $sImage = $this->getImage($iImage, $this->aSettings['teaser_image_width'], $this->aSettings['teaser_image_height'], $this->aSettings['teaser_image_crop']);
                $oTpl->set('d', 'IMAGE', $sImage);
            } else if (strip_tags($iImage) != $iImage && strlen($iImage) > 0) {
                $sImage = $this->extractImage($iImage);
                if (strlen($sImage) > 0) {
                    $oTpl->set('d', 'IMAGE', $sImage);
                } else {
                    $oTpl->set('d', 'IMAGE', '');
                }
            } else {
                $oTpl->set('d', 'IMAGE', '');
            }

            // strip all tags from manual teaser date
            $sDate = strip_tags($sDate);

            //set generated values to teaser template
            $oTpl->set('d', 'TITLE', $sTitle);
            $oTpl->set('d', 'TEXT', $sText);

            $oTpl->set('d', 'IDART', $iIdArt);
            $oTpl->set('d', 'ART_URL', 'front_content.php?idart=' . $iIdArt);
            $oTpl->set('d', 'PUBLISHED', $iPublished);
            $oTpl->set('d', 'PUBLISHED_MANUAL', $sDate);

            if ($sDate != "") {
                $oTpl->set('d', 'PUBLISHED_COMBINED', $sDate);
            } else {
                $oTpl->set('d', 'PUBLISHED_COMBINED', $iPublished);
            }

            foreach (self::$aTranslations as $sKey => $sValue) {
                $oTpl->set('d', $sKey, mi18n($sValue));
            }

            $oTpl->next();
        }

        return true;
    }

    /**
     * Function is called in edit- and viewmode in order to generate teasercode for output
     *
     * @param boolean $bEditmode - in editmode skip encoding because it is done in getAllWidgetEdit()
     * @return html string of select box
     *
     * @access public
     */
    public function getAllWidgetOutput($bEditmode = false) {
        $oTpl = new Template();
        //set title of teaser
        $oTpl->set('s', 'TITLE', $this->aSettings['teaser_title']);

        //decide if it is a manual or category teaser
        if ($this->aSettings['teaser_manual'] == 'true' && count($this->aSettings['teaser_manual_art']) > 0) {
            $i = 0;
            //in manual case get all art to display and generate article objects manually
            foreach ($this->aSettings['teaser_manual_art'] as $iIdArt) {
                $oArticle = new Article($iIdArt, $this->iClient, $this->iLang);

                //try to fill teaser image
                if ($this->fillTeaserTemplateEntry($oArticle, $oTpl)) {
                    $i++;

                    //break render, if teaser limit is reached
                    if ($i == $this->aSettings['teaser_count'])
                        break;
                }
            }
        } else {
            //in case of autmatic teaser use class Contenido_Category_Articles for getting all arts in category
            $oConCatArt = new Contenido_Category_Articles($this->oDb, $this->aCfg, $this->iClient, $this->iLang);
            //decide to teaser articles or not
            if ($this->aSettings['teaser_start'] == 'true') {
                $aArticles = $oConCatArt->getArticlesInCategory($this->aSettings['teaser_category'], $this->aSettings['teaser_sort'], $this->aSettings['teaser_sort_order']);
            } else {
                $aArticles = $oConCatArt->getNonStartArticlesInCategory($this->aSettings['teaser_category'], $this->aSettings['teaser_sort'], $this->aSettings['teaser_sort_order']);
            }

            $i = 0;
            //iterate over all found articles
            foreach ($aArticles as $oArticle) {
                //try to fill teaser image
                if ($this->fillTeaserTemplateEntry($oArticle, $oTpl)) {
                    $i++;
                    //break render, if teaser limit is reached
                    if ($i == $this->aSettings['teaser_count'])
                        break;
                }
            }
        }

        $sCode = '';
        //generate teasertemplate
        if (file_exists($this->aCfgClient[$this->iClient]['path']['frontend'] . 'templates/' . $this->aSettings['teaser_style'])) {
            $sCode = $oTpl->generate($this->aCfgClient[$this->iClient]['path']['frontend'] . 'templates/' . $this->aSettings['teaser_style'], 1);
        }

        return $sCode;
        //use this to show xml document which contains teaser settings
        #return '<pre>'.clHtmlEntities($this->sContent).'</pre>';
    }

    /**
     * Dynamic filelist generator. 
     * This method is executed every time the filelist is displayed.
     * 
     * @return	string	output of the filelist
     */
    public function getAllWidgetView() {
        $sCode = '\";?><?php
					$oTeaser = new Cms_Teaser(\'%s\', %s, 0, "", $cfg, null, "", $client, $lang, $cfgClient, null);
					
					echo $oTeaser->getAllWidgetOutput();
				 ?><?php echo \"';

        $sCode = sprintf($sCode, $this->sContent, $this->iId);
        return $sCode;
    }

}

?>