<?php

defined('C5_EXECUTE') or die("Access Denied.");

/**
 * Contains the page type object.
 *
 * @package    Pages
 * @category   Concrete
 * @author     Andrew Embler <andrew@concrete5.org>
 * @copyright  Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
 * @license    http://www.concrete5.org/license/     MIT License
 *
 */

/**
 * The CollectionType or PageType object represents reusable types of pages that can
 * be added to a Concrete site.
 *
 * @package    Pages
 * @author     Andrew Embler <andrew@concrete5.org>
 * @category   Concrete
 * @copyright  Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
 * @license    http://www.concrete5.org/license/     MIT License
 *
 */
class Concrete5_Model_CollectionType extends ConcreteObject
{

    public $ctID;
    public $addCTUArray = array();
    public $addCTGArray = array();
    public $akIDArray = array();
    public $composerAKIDArray = array();

    /**
     * @description returns a collection type object for the given CollectionType handle
     * @param string $ctHandle
     * @return CollectionType
     */
    public static function getByHandle($ctHandle)
    {
        $db = Loader::db();
        $q = "SELECT PageTypes.ctID, ComposerTypes.ctID as ctIDc, ctHandle, ctIsInternal, ctName, ctIcon, pkgID, ctComposerPublishPageMethod, ctComposerPublishPageTypeID, ctComposerPublishPageParentID from PageTypes left join ComposerTypes on PageTypes.ctID = ComposerTypes.ctID where ctHandle = ?";
        $r = $db->query($q, array($ctHandle));
        if ($r) {
            $row = $r->fetchRow();
            $r->free();
            if (is_array($row)) {
                $ct = new CollectionType;
                $ct->setPropertiesFromArray($row);
                if ($row['ctIDc']) {
                    $ct->ctIncludeInComposer = true;
                }
            }
        }
        return $ct;
    }

    public function setPropertiesFromArray($row)
    {
        if (!$row['ctIcon']) {
            $row['ctIcon'] = FILENAME_COLLECTION_TYPE_DEFAULT_ICON;
        }
        parent::setPropertiesFromArray($row);
    }

    public function getMasterTemplate()
    {
        $db = Loader::db();
        $cID = $db->getOne(
                  "select p.cID from Pages p inner join CollectionVersions cv on p.cID = cv.cID where cv.ctID = ? and cIsTemplate = 1",
                  array($this->ctID));
        return Page::getByID($cID, "RECENT");
    }

    /**
     * Get a collection type by its ID.
     *
     * @param $ctID
     * @return bool|CollectionType
     * @todo refactor to return null on not found.
     */
    public static function getByID($ctID)
    {
        $ctID = intval($ctID);
        $ct = CacheLocal::getEntry('collection_type_by_id', $ctID);
        if (is_object($ct)) {
            return $ct;
        } else {
            if ($ct === -1) {
                return false;
            }
        }

        $db = Loader::db();
        $q = "SELECT PageTypes.ctID, ComposerTypes.ctID as ctIDc, ctHandle, ctIsInternal, ctName, ctIcon, pkgID, ctComposerPublishPageMethod, ctComposerPublishPageTypeID, ctComposerPublishPageParentID from PageTypes left join ComposerTypes on PageTypes.ctID = ComposerTypes.ctID where PageTypes.ctID = ?";
        $r = $db->query($q, array($ctID));
        if ($r) {
            $row = $r->fetchRow();
            $r->free();
            if (is_array($row)) {
                $ct = new CollectionType;
                $ct->setPropertiesFromArray($row);
                if ($row['ctIDc']) {
                    $ct->ctIncludeInComposer = true;
                }
            }
        }
        CacheLocal::set('collection_type_by_id', $ctID, $ct);
        return $ct;
    }

    public function delete()
    {
        $db = Loader::db();
        $template_cID = $db->getOne(
                           "SELECT Pages.cID FROM Pages inner join CollectionVersions cv on Pages.cID = cv.cID WHERE cIsTemplate = 1 and cv.ctID = ?",
                           array($this->ctID));

        if ($template_cID) {
            $template = Page::getByID($template_cID);
            if ($template->getCollectionID() > 1) {
                $template->delete();
            }
        }

        $db->query("DELETE FROM PageTypes WHERE ctID = ?", array($this->ctID));
        $db->query("DELETE FROM PageTypeAttributes WHERE ctID = ?", array($this->ctID));
        $db->query("DELETE FROM ComposerTypes WHERE ctID = ?", array($this->ctID));
        $db->query("DELETE FROM ComposerContentLayout WHERE ctID = ?", array($this->ctID));
    }

    public function getComposerPageTypes()
    {
        $db = Loader::db();
        $ctArray = array();
        $r = $db->Execute('select ctID from ComposerTypes order by ctID asc');
        while ($row = $r->FetchRow()) {
            $ct = CollectionType::getByID($row['ctID']);
            if (is_object($ct)) {
                $ctArray[] = $ct;
            }
        }
        return $ctArray;
    }

    public static function getListByPackage($pkg)
    {
        $db = Loader::db();
        $list = array();
        $r = $db->Execute(
                'select ctID from PageTypes where pkgID = ? order by ctName asc',
                array($pkg->getPackageID()));
        while ($row = $r->FetchRow()) {
            $list[] = CollectionType::getByID($row['ctID']);
        }
        $r->Close();
        return $list;
    }

    public function importComposerSettings(SimpleXMLElement $sx)
    {
        $db = Loader::db();
        if ($sx['method'] == 'PAGE_TYPE') {
            $ctID = ContentImporter::getValue($sx['pagetype']);
            $cta = CollectionType::getByID($ctID);
            $this->saveComposerPublishTargetPageType($cta);
        } else {
            if ($sx['method'] == 'PARENT') {
                $cID = ContentImporter::getValue($sx['parent']);
                $c = Page::getByID($cID);
                $this->saveComposerPublishTargetPage($c);
            } else {
                $this->saveComposerPublishTargetAll();
            }
        }

        if (isset($sx->items)) {
            foreach ($sx->items->children() as $node) {
                $displayOrder = $db->GetOne(
                                   'select max(displayOrder) as displayOrder from ComposerContentLayout where ctID = ?',
                                   array($this->ctID));
                if ($displayOrder !== false) {
                    if ($displayOrder > 0) {
                        $displayOrder++;
                    } else {
                        $displayOrder = 1;
                    }
                } else {
                    $displayOrder = 0;
                }

                if ($node->getName() == 'attributekey') {
                    $ak = CollectionAttributeKey::getByHandle((string)$node['handle']);
                    $v = array($ak->getAttributeKeyID(), $displayOrder, $this->ctID);
                    $db->Execute('insert into ComposerContentLayout (akID, displayOrder, ctID) values (?, ?, ?)', $v);
                }
                if ($node->getName() == 'block') {
                    $mcID = $this->getMasterCollectionID();
                    $bID = $db->GetOne(
                              'select Blocks.bID from CollectionVersionBlocks inner join Blocks on CollectionVersionBlocks.bID = Blocks.bID where cID = ? and Blocks.bName = ?',
                              array(
                                  $mcID,
                                  (string)$node['name']
                              ));
                    $v = array($bID, $displayOrder, (string)$node['composer-template'], $this->ctID);
                    $db->Execute(
                       'insert into ComposerContentLayout (bID, displayOrder, ccFilename, ctID) values (?, ?, ?, ?)',
                       $v);
                }
            }
        }
    }

    public static function exportList($xml)
    {
        $list = self::getList(false, true);
        $nxml = $xml->addChild('pagetypes');
        foreach ($list as $ct) {
            $type = $nxml->addChild('pagetype');
            $type->addAttribute('handle', $ct->getCollectionTypeHandle());
            $type->addAttribute('name', $ct->getCollectionTypeName());
            $type->addAttribute('internal', $ct->isCollectionTypeInternal());
            $type->addAttribute('icon', $ct->getCollectionTypeIcon());
            $type->addAttribute('package', $ct->getPackageHandle());
            if ($ct->isCollectionTypeIncludedInComposer()) {
                $composer = $type->addChild('composer');
                $composer->addAttribute('method', $ct->getCollectionTypeComposerPublishMethod());
                $parent = '';
                $pagetype = '';

                if ($ct->getCollectionTypeComposerPublishPageTypeID() > 0) {
                    $pagetype = ContentExporter::replacePageTypeWithPlaceHolder(
                                               $ct->getCollectionTypeComposerPublishPageTypeID());
                }

                if ($ct->getCollectionTypeComposerPublishPageParentID() > 0) {
                    $parent = ContentExporter::replacePageWithPlaceHolder(
                                             $ct->getCollectionTypeComposerPublishPageParentID());
                }

                $composer->addAttribute('pagetype', $pagetype);
                $composer->addAttribute('parent', $parent);

                $items = $ct->getComposerContentItems();
                if (count($items) > 0) {
                    $itemNode = $composer->addChild('items');
                    foreach ($items as $ci) {
                        $ci->export($itemNode, 'composer');
                    }
                }
            }
            $mcID = $ct->getMasterCollectionID();
            if ($mcID > 0) {
                $mc = Page::getByID($mcID);
                $mc->export($type);
            }
        }
    }

    public static function getList($limiterType = null, $includeInternal = false)
    {
        $db = Loader::db();

        // the purpose for this class? Well, we get an array of collection type objects,
        // we don't do a join because on big sites it's actually slower
        $mcIDs = $db->GetAll(
                    "select p.cID, cv.ctID from Pages p inner join CollectionVersions cv on p.cID = cv.cID where cIsTemplate = 1");
        $masterCollectionIDs = array();
        foreach ($mcIDs as $mc) {
            $masterCollectionIDs[$mc['ctID']] = $mc['cID'];
        }
        if ($includeInternal) {
            $internal = '1=1';
        } else {
            $internal = 'ctIsInternal = 0';
        }
        $q = "select ctID, ctHandle, ctIcon, ctName, ctIsInternal, pkgID from PageTypes where {$internal} order by ctName asc";
        $r = $db->query($q);
        $ctArray = array();
        if ($r) {
            while ($row = $r->fetchRow()) {
                $ct = new CollectionType;
                $row['mcID'] = $masterCollectionIDs[$row['ctID']];
                $ct->setPropertiesFromArray($row);

                if (is_array($limiterType)) {
                    // an array of collection type handles that we should check against
                    if (in_array($row['ctHandle'], $limiterType)) {
                        $ctArray[] = $ct;
                    }
                } else {
                    if (is_object($limiterType)) {
                        $ct->limit($limiterType);
                        $ctArray[] = $ct;
                    } else {
                        $ctArray[] = $ct;
                    }
                }
            }
            $r->free();
        }

        return $ctArray;
    }

    public static function add($data, $pkg = null)
    {
        $db = Loader::db();
        $pkgID = ($pkg == null) ? 0 : $pkg->getPackageID();
        $ctIcon = FILENAME_COLLECTION_TYPE_DEFAULT_ICON;
        if (isset($data['ctIcon'])) {
            $ctIcon = $data['ctIcon'];
        }
        $ctIsInternal = 0;
        if (isset($data['ctIsInternal']) && $data['ctIsInternal']) {
            $ctIsInternal = 1;
        }
        $v = array($data['ctHandle'], $data['ctName'], $ctIcon, $ctIsInternal, $pkgID);
        $q = "insert into PageTypes (ctHandle, ctName, ctIcon, ctIsInternal, pkgID) values (?, ?, ?, ?, ?)";
        $r = $db->prepare($q);
        $res = $db->execute($r, $v);
        if ($res) {
            $ctID = $db->Insert_ID();
            // metadata

            if (is_array($data['akID'])) {
                foreach ($data['akID'] as $ak) {
                    $v2 = array($ctID, $ak);
                    $db->query("insert into PageTypeAttributes (ctID, akID) values (?, ?)", $v2);
                }
            }

            // now that we've created the collection type, we create the master collection
            $dh = Loader::helper('date');
            $cDate = $dh->getSystemDateTime();
            $data['ctID'] = $ctID;
            $cobj = Collection::addCollection($data);
            $cID = $cobj->getCollectionID();

            $mcName = ($data['cName']) ? $data['cName'] : "MC: {$data['ctName']}";
            $mcDescription = $data['cDescription'] ? $data['cDescription'] : "Master Collection For {$data['ctName']}";
            $v2 = array($cID, 1, $pkgID);
            $q2 = "insert into Pages (cID, cIsTemplate, pkgID) values (?, ?, ?)";
            $r2 = $db->prepare($q2);
            $res2 = $db->execute($r2, $v2);
            if ($res2) {
                return CollectionType::getByID($ctID);
            }
        }
    }

    public function getPages()
    {
        // returns an array of pages of this type. Does not check permissions
        // since this can get pretty long it actually returns a limited amount of data;

        $pl = new PageList();
        $pl->filterByCollectionTypeID($this->getCollectionTypeID());
        $pl->ignorePermissions();
        $pl->ignoreAliases();
        $pages = $pl->get();

        return $pages;
    }

    public function resetComposerData()
    {
        $db = Loader::db();
        $db->query("delete from ComposerContentLayout where ctID = ?", array($this->getCollectionTypeID()));
        $db->Execute('delete from ComposerTypes where ctID = ?', array($this->getCollectionTypeID()));
    }

    public function saveComposerAttributeKeys($atids = array())
    {
        $db = Loader::db();
        // we remove those that aren't in the list already
        $ids = $atids;
        $ids[] = -1;
        $v = implode(',', $ids);
        $r = $db->Execute(
                "delete from ComposerContentLayout where akID not in ({$v}) and bID = 0 and ctID = ?",
                array($this->getCollectionTypeID()));

        // now we append the new items
        $displayOrder = $db->GetOne(
                           'select max(displayOrder) from ComposerContentLayout where ctID = ?',
                           array($this->getCollectionTypeID()));
        if ($displayOrder > 0) {
            $displayOrder++;
        } else {
            $displayOrder = 0;
        }

        $existingAKIDs = $db->GetCol(
                            'select akID from ComposerContentLayout where akID > 0 and ctID = ?',
                            array($this->getCollectionTypeID()));
        // this returns all akIDs currently available the composer content layout table for this composer type
        // if something is in the new array but not in the existing one we append

        if (is_array($atids)) {
            foreach ($atids as $ak) {
                if (!in_array($ak, $existingAKIDs)) {
                    $db->Replace(
                       'ComposerContentLayout',
                       array('ctID' => $this->ctID, 'akID' => $ak, 'displayOrder' => $displayOrder),
                       array('ctID', 'akID'),
                       true);
                    $displayOrder++;
                }
            }
        }
    }

    public function saveComposerPublishTargetPage($c)
    {
        $db = Loader::db();
        $db->Execute('delete from ComposerTypes where ctID = ?', array($this->getCollectionTypeID()));
        $db->Replace(
           'ComposerTypes',
           array(
               'ctID'                          => $this->ctID,
               'ctComposerPublishPageMethod'   => 'PARENT',
               'ctComposerPublishPageTypeID'   => 0,
               'ctComposerPublishPageParentID' => $c->getCollectionID()),
           array('ctID'),
           true);
    }

    public function saveComposerPublishTargetPageType($ct)
    {
        $db = Loader::db();
        $db->Execute('delete from ComposerTypes where ctID = ?', array($this->getCollectionTypeID()));
        $db->Replace(
           'ComposerTypes',
           array(
               'ctID'                          => $this->ctID,
               'ctComposerPublishPageMethod'   => 'PAGE_TYPE',
               'ctComposerPublishPageTypeID'   => $ct->getCollectionTypeID(),
               'ctComposerPublishPageParentID' => 0),
           array('ctID'),
           true);
    }

    public function saveComposerPublishTargetAll()
    {
        $db = Loader::db();
        $db->Execute('delete from ComposerTypes where ctID = ?', array($this->getCollectionTypeID()));
        $db->Replace(
           'ComposerTypes',
           array(
               'ctID'                          => $this->ctID,
               'ctComposerPublishPageMethod'   => 'CHOOSE',
               'ctComposerPublishPageTypeID'   => 0,
               'ctComposerPublishPageParentID' => 0),
           array('ctID'),
           true);
    }

    public function saveComposerContentItemOrder($items)
    {
        $db = Loader::db();
        $displayOrder = 0;
        foreach ($items as $it) {
            $bID = $it->bID;
            if (!$bID) {
                $bID = 0;
            }
            $akID = $it->akID;
            if (!$akID) {
                $akID = 0;
            }
            $v = array($displayOrder, $bID, $akID, $this->getCollectionTypeID());
            $db->Execute(
               'update ComposerContentLayout set displayOrder = ? where bID = ? and akID = ? and ctID = ?',
               $v);
            $displayOrder++;
        }
    }

    public function update($data)
    {
        $db = Loader::db();

        $v = array($data['ctName'], $data['ctHandle'], $data['ctIcon'], $this->ctID);
        $r = $db->prepare("update PageTypes set ctName = ?, ctHandle = ?, ctIcon = ? where ctID = ?");
        $res = $db->execute($r, $v);

        // metadata
        $v2 = array($this->getCollectionTypeID());
        $db->query("delete from PageTypeAttributes where ctID = ?", $v2);

        if (is_array($data['akID'])) {
            foreach ($data['akID'] as $ak) {
                $v3 = array($this->getCollectionTypeID(), $ak);
                $db->query("insert into PageTypeAttributes (ctID, akID) values (?, ?)", $v3);
            }
        }

    }

    public function assignCollectionAttribute($ak)
    {
        // object
        $db = Loader::db();
        $db->query(
           "insert into PageTypeAttributes (ctID, akID) values (?, ?)",
           array($this->getCollectionTypeID(), $ak->getAttributeKeyID()));
    }

    public function populateAvailableAttributeKeys()
    {
        $db = Loader::db();
        $v = array($this->getCollectionTypeID());
        $q = 'SELECT pta.akID'
            . ' FROM PageTypeAttributes pta'
            . ' LEFT JOIN (AttributeSetKeys sk INNER JOIN AttributeSets s ON sk.asID = s.asID)'
            . ' ON pta.akID = sk.akID'
            . ' WHERE pta.ctID = ?'
            . ' ORDER BY (s.asID IS NULL), s.asDisplayOrder, sk.displayOrder';
        $r = $db->query($q, $v);
        if ($r) {
            $this->akIDArray = array();
            while ($row = $r->fetchRow()) {
                $this->akIDArray[] = $row['akID'];
            }
        }
    }

    public function getIcons()
    {
        $f = Loader::helper('file');
        Loader::model('file_list');
        Loader::model('file_set');
        $fileList = new FileList();
        $fs = FileSet::getByName('Page Type Icons');
        if (!$fs) {
            return $f->getDirectoryContents(DIR_FILES_COLLECTION_TYPE_ICONS);
        } else {
            $fileList->filterBySet($fs);
            $icons = $fileList->get(100);
            if (!count($icons)) {
                $icons = $f->getDirectoryContents(DIR_FILES_COLLECTION_TYPE_ICONS);
            }
            return $icons;
        }
    }

    public function getAvailableAttributeKeys()
    {
        if (count($this->akIDArray) == 0) {
            $this->populateAvailableAttributeKeys();
        }
        $objArray = array();
        foreach ($this->akIDArray as $akID) {
            $obj = CollectionAttributeKey::getByID($akID);
            if (is_object($obj)) {
                $objArray[] = $obj;
            }
        }
        return $objArray;
    }

    public function getComposerAttributeKeys()
    {
        $db = Loader::db();
        $akIDs = $db->GetCol('select akID from ComposerContentLayout where ctID = ? and akID > 0', array($this->ctID));
        $attribs = array();
        if (is_array($akIDs)) {
            foreach ($akIDs as $akID) {
                $obj = CollectionAttributeKey::getByID($akID);
                if (is_object($obj)) {
                    $attribs[] = $obj;
                }
            }
        }
        return $attribs;
    }

    public function getComposerContentItems()
    {
        $db = Loader::db();
        $r = $db->Execute(
                'select bID, akID, ccFilename from ComposerContentLayout where ctID = ? order by displayOrder asc',
                array($this->ctID));
        $items = array();
        while ($row = $r->FetchRow()) {
            if ($row['akID'] > 0) {
                $obj = CollectionAttributeKey::getByID($row['akID']);
                if (is_object($obj)) {
                    $items[] = $obj;
                }
            } else {
                if ($row['bID'] > 0) {
                    $b = Block::getByID($row['bID']);
                    if (is_object($b)) {
                        $items[] = $b;
                    }
                }
            }
        }
        return $items;
    }

    /**
     *Checks if given attribute key or attribute key id is assigned to this collection type
     */
    public function isAvailableCollectionTypeAttribute($akIDorObject)
    {
        if (is_object($akIDorObject)) {
            $akID = $akIDorObject->getAttributeKeyID();
        } else {
            $akID = $akIDorObject;
        }

        if (count($this->akIDArray) == 0) {
            $this->populateAvailableAttributeKeys();
        }
        return in_array($akID, $this->akIDArray);
    }

    public function canAddSubCollection($obj)
    {
        switch (strtolower(get_class($obj))) {
            case 'group':
                return in_array($obj->getGroupID(), $this->addCTGArray);
                break;
            case 'userinfo':
                return in_array($obj->getUserID(), $this->addCTUArray);
                break;
        }
    }

    /**
     * returns the complete html img tag for this collection type's icon
     */
    public function getCollectionTypeIconImage()
    {
        Loader::helper('concrete/file');
        $html = Loader::helper('html');
        $icon = $this->getCollectionTypeIcon();
        if (is_numeric($icon) && $icon) {
            $f = File::getByID($icon);
            $fv = $f->getApprovedVersion();
            $src = $fv->getRelativePath();
        } else { // retain support for legacy file type images
            $src = REL_DIR_FILES_COLLECTION_TYPE_ICONS . '/' . $icon;
        }
        $iconImg = '<img src="' . $src . '" height="' . COLLECTION_TYPE_ICON_HEIGHT . '" width="' . COLLECTION_TYPE_ICON_WIDTH . '" alt="' . $this->getCollectionTypeName() . '" title="' . $this->getCollectionTypeName() . '" />';

        return $iconImg;
    }

    public function getCollectionTypeID()
    {
        return $this->ctID;
    }

    public function getCollectionTypeName()
    {
        return $this->ctName;
    }

    public function getCollectionTypeHandle()
    {
        return $this->ctHandle;
    }

    public function isCollectionTypeIncludedInComposer()
    {
        return $this->ctIncludeInComposer;
    }

    public function isCollectionTypeInternal()
    {
        return $this->ctIsInternal;
    }

    public function getCollectionTypeComposerPublishMethod()
    {
        return $this->ctComposerPublishPageMethod;
    }

    public function getCollectionTypeComposerPublishPageParentID()
    {
        return $this->ctComposerPublishPageParentID;
    }

    public function getCollectionTypeComposerPublishPageTypeID()
    {
        return $this->ctComposerPublishPageTypeID;
    }

    public function getMasterCollectionID()
    {
        $db = Loader::db();
        $mcID = $db->GetOne(
                   "select p.cID from Pages p inner join CollectionVersions cv on p.cID = cv.cID where cv.ctID = ? and cIsTemplate = 1",
                   array($this->ctID));
        return $mcID;
    }

    public function getCollectionTypeIcon()
    {
        return $this->ctIcon;
    }

    public function getPackageID()
    {
        return $this->pkgID;
    }

    public function getPackageHandle()
    {
        return PackageList::getHandle($this->pkgID);
    }

}
