Вход Регистрация
Файл: library/XenForo/Model/Template.php
Строк: 1466
<?php

/**
 * Model for templates
 *
 * @package XenForo_Templates
 */
class XenForo_Model_Template extends XenForo_Model
{
    
/**
     * Returns all templates customized in a style in alphabetical title order
     *
     * @param integer $styleId Style ID
     * @param boolean $basicData If true, gets basic data only
     *
     * @return array Format: [title] => (array) template
     */
    
public function getAllTemplatesInStyle($styleId$basicData false)
    {
        return 
$this->fetchAllKeyed('
            SELECT ' 
. ($basicData 'template_id, title, style_id, addon_id' '*') . '
            FROM xf_template
            WHERE style_id = ?
            ORDER BY CONVERT(title USING utf8)
        '
'title'$styleId);
    }

    
/**
     * Get the effective template list for a style. "Effective" means a merged/flattened
     * system where every valid template has a record.
     *
     * This only returns data appropriate for a list view (map id, template id, title).
     * Template_state is also calculated based on whether this template has been customized.
     * State options: default, custom, inherited.
     *
     * @param integer $styleId
     *
     * @return array Format: [] => (array) template list info
     */
    
public function getEffectiveTemplateListForStyle($styleId, array $conditions = array(), $fetchOptions = array())
    {
        
$whereClause $this->prepareTemplateConditions($conditions$fetchOptions);
        
$limitOptions $this->prepareLimitFetchOptions($fetchOptions);

        return 
$this->_getDb()->fetchAll($this->limitQueryResults('
            SELECT template_map.template_map_id,
                template_map.style_id AS map_style_id,
                template.template_id,
                template.title,
                addon.addon_id, addon.title AS addonTitle,
                IF(template.style_id = 0, '
default', IF(template.style_id = template_map.style_id, 'custom', 'inherited')) AS template_state,
                IF(template.style_id = template_map.style_id, 1, 0) AS canDelete
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template_map.template_id = template.template_id)
            LEFT JOIN xf_addon AS addon ON
                (addon.addon_id = template.addon_id)
            WHERE template_map.style_id = ?
                AND ' 
$whereClause '
            ORDER BY CONVERT(template_map.title USING utf8)
        '
$limitOptions['limit'], $limitOptions['offset']), $styleId);
    }

    
/**
     * Prepares conditions for searching templates. Often, this search will
     * be done on an effective template set (using the map). Some conditions
     * may require this.
     *
     * @param array $conditions
     * @param array $fetchOptions
     *
     * @return string SQL conditions
     */
    
public function prepareTemplateConditions(array $conditions, array &$fetchOptions)
    {
        
$db $this->_getDb();
        
$sqlConditions = array();

        if (!empty(
$conditions['title']))
        {
            if (
is_array($conditions['title']))
            {
                
$sqlConditions[] = 'CONVERT(template.title USING utf8) LIKE ' XenForo_Db::quoteLike($conditions['title'][0], $conditions['title'][1], $db);
            }
            else
            {
                
$sqlConditions[] = 'CONVERT(template.title USING utf8) LIKE ' XenForo_Db::quoteLike($conditions['title'], 'lr'$db);
            }
        }

        if (!empty(
$conditions['template']))
        {
            
$caseSensitive = (empty($conditions['template_case_sensitive']) ? '' 'BINARY ');

            if (
is_array($conditions['template']))
            {
                
$sqlConditions[] = 'template.template LIKE ' $caseSensitive XenForo_Db::quoteLike($conditions['template'][0], $conditions['phrase_text'][1], $db);
            }
            else
            {
                
$sqlConditions[] = 'template.template LIKE ' $caseSensitive XenForo_Db::quoteLike($conditions['template'], 'lr'$db);
            }
        }

        if (!empty(
$conditions['template_state']))
        {
            
$stateIf 'IF(template.style_id = 0, 'default', IF(template.style_id = template_map.style_id, 'custom', 'inherited'))';
            if (
is_array($conditions['template_state']))
            {
                
$sqlConditions[] = $stateIf ' IN (' $db->quote($conditions['template_state']) . ')';
            }
            else
            {
                
$sqlConditions[] = $stateIf ' = ' $db->quote($conditions['template_state']);
            }
        }

        return 
$this->getConditionsForClause($sqlConditions);
    }

    
/**
     * Gets all effective templates in a style. "Effective" means a merged/flattened system
     * where every valid template has a record.
     *
     * @param integer $styleId
     *
     * @return array Format: [] => (array) effective template info
     */
    
public function getAllEffectiveTemplatesInStyle($styleId)
    {
        return 
$this->_getDb()->fetchAll('
            SELECT template_map.template_map_id,
                template_map.style_id AS map_style_id,
                template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template_map.template_id = template.template_id)
            WHERE template_map.style_id = ?
            ORDER BY CONVERT(template_map.title USING utf8)
        '
$styleId);
    }

    
/**
     * Gets style ID/template ID pairs for all styles where the named template
     * is modified.
     *
     * @param string $templateTitle
     *
     * @return array Format: [style_id] => template_id
     */
    
public function getTemplateIdInStylesByTitle($templateTitle)
    {
        return 
$this->_getDb()->fetchPairs('
            SELECT style_id, template_id
            FROM xf_template
            WHERE title = ?
        '
$templateTitle);
    }

    
/**
     * Gets the effective template in a style by its title. This includes all
     * template information and the map ID.
     *
     * @param string $title
     * @param integer $styleId
     *
     * @return array|false Effective template info.
     */
    
public function getEffectiveTemplateByTitle($title$styleId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT template_map.template_map_id,
                template_map.style_id AS map_style_id,
                template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template.template_id = template_map.template_id)
            WHERE template_map.title = ? AND template_map.style_id = ?
        '
, array($title$styleId));
    }

    
/**
     * Gets effective templates in a style by their titles
     *
     * @param array $titles
     * @param integer $styleId
     *
     * @return array|false Effective template info
     */
    
public function getEffectiveTemplatesByTitles(array $titles$styleId)
    {
        if (empty(
$titles))
        {
            return array();
        }

        return 
$this->fetchAllKeyed('
            SELECT template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template.template_id = template_map.template_id)
            WHERE template_map.title IN(' 
$this->_getDb()->quote($titles) . ') AND template_map.style_id = ?
        '
'title'$styleId);
    }

    
/**
     * Gets the effective template based on a known map idea. Returns all template
     * information and the map ID.
     *
     * @param integer $templateMapId
     *
     * @return array|false Effective template info.
     */
    
public function getEffectiveTemplateByMapId($templateMapId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT template_map.template_map_id,
                template_map.style_id AS map_style_id,
                template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template.template_id = template_map.template_id)
            WHERE template_map.template_map_id = ?
        '
$templateMapId);
    }

    
/**
     * Gets multiple effective templates based on 1 or more map IDs. Returns all template
     * information and the map ID.
     *
     * @param integery|array $templateMapIds Either one map ID as a scalar or any array of map IDs
     *
     * @return array Format: [] => (array) effective template info
     */
    
public function getEffectiveTemplatesByMapIds($templateMapIds)
    {
        if (!
is_array($templateMapIds))
        {
            
$templateMapIds = array($templateMapIds);
        }

        if (!
$templateMapIds)
        {
            return array();
        }

        
$db $this->_getDb();

        return 
$db->fetchAll('
            SELECT template_map.template_map_id,
                template_map.style_id AS map_style_id,
                template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template.template_id = template_map.template_id)
            WHERE template_map.template_map_id IN (' 
$db->quote($templateMapIds) . ')
        '
);
    }

    
/**
     * Gets all the unique templates pointed to by a set of template map IDs.
     *
     * @param array $templateMapIds
     *
     * @return array
     */
    
public function getUniqueTemplatesByMapIds($templateMapIds)
    {
        if (!
is_array($templateMapIds))
        {
            
$templateMapIds = array($templateMapIds);
        }

        if (!
$templateMapIds)
        {
            return array();
        }

        
$db $this->_getDb();

        return 
$this->fetchAllKeyed('
            SELECT DISTINCT template.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_template AS template ON
                (template.template_id = template_map.template_id)
            WHERE template_map.template_map_id IN (' 
$db->quote($templateMapIds) . ')
        '
'template_id');
    }

    public function 
getMapIdsByTemplateTitles(array $titles)
    {
        if (!
$titles)
        {
            return array();
        }

        
$db $this->_getDb();
        return 
$db->fetchCol("
            SELECT template_map_id
            FROM xf_template_map
            WHERE title IN (" 
$db->quote($titles) . ")
        "
);
    }

    
/**
     * Returns the template specified by template_id
     *
     * @param integer $templateId Template ID
     *
     * @return array|false Template
     */
    
public function getTemplateById($templateId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT *
            FROM xf_template
            WHERE template_id = ?
        '
$templateId);
    }

    
/**
     * Returns the templates specified by template IDs
     *
     * @param array $templateIds
     *
     * @return array
     */
    
public function getTemplatesByIds(array $templateIds)
    {
        if (!
$templateIds)
        {
            return array();
        }

        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_template
                WHERE template_id IN(' 
$this->_getDb()->quote($templateIds) . ')
        '
'template_id');
    }

    
/**
     * Fetches a template from a particular style based on its title.
     * Note that if a version of the requested template does not exist
     * in the specified style, nothing will be returned.
     *
     * @param string Title
     * @param integer Style ID (defaults to master style)
     *
     * @return array
     */
    
public function getTemplateInStyleByTitle($title$styleId 0)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT *
            FROM xf_template
            WHERE title = ?
                AND style_id = ?
        '
, array($title$styleId));
    }

    
/**
     * Fetches templates from a particular style based on their titles.
     * Note that if a version of the requested template does not exist
     * in the specified style, nothing will be returned for it.
     *
     * @param array $titles List of titles
     * @param integer $styleId Style ID (defaults to master style)
     *
     * @return array Format: [title] => info
     */
    
public function getTemplatesInStyleByTitles(array $titles$styleId 0)
    {
        if (!
$titles)
        {
            return array();
        }

        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_template
            WHERE title IN (' 
$this->_getDb()->quote($titles) . ')
                AND style_id = ?
        '
'title'$styleId);
    }

    
/**
     * Gets all templates with a specific title
     *
     * @param array $titles
     *
     * @return array Format: [template_id] => info
     */
    
public function getTemplatesByTitles(array $titles)
    {
        if (!
$titles)
        {
            return array();
        }

        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_template
            WHERE title IN (' 
$this->_getDb()->quote($titles) . ')
        '
'template_id');
    }

    
/**
     * Gets all templates that are outdated (parent version edited more recently).
     * Does not include contents of template.
     *
     * @return array [template id] => template info, including master_version_string
     */
    
public function getOutdatedTemplates()
    {
        return 
$this->fetchAllKeyed('
            SELECT template.template_id, template.title, template.style_id,
                template.addon_id, template.version_string, template.last_edit_date,
                parent.style_id AS parent_style_id,
                parent.version_string AS parent_version_string,
                parent.last_edit_date AS parent_last_edit_date,
                IF(parent.last_edit_date > 0 AND parent.last_edit_date >= template.last_edit_date, 1, 0) AS outdated_by_date
            FROM xf_template AS template
            INNER JOIN xf_style AS style ON (style.style_id = template.style_id)
            INNER JOIN xf_template_map AS map ON (map.style_id = style.parent_id AND map.title = template.title)
            INNER JOIN xf_template AS parent ON (map.template_id = parent.template_id
                AND (
                    (parent.last_edit_date > 0 AND parent.last_edit_date >= template.last_edit_date)
                    OR parent.version_id > template.version_id
                )
            )
            WHERE template.style_id > 0
        '
'template_id');
    }

    public function 
getLatestTemplateHistoryForTemplate($title$styleId$priorTo null)
    {
        return 
$this->_getDb()->fetchRow("
            SELECT *
            FROM xf_template_history
            WHERE title = ? AND style_id = ?
                " 
. ($priorTo " AND edit_date <= " intval($priorTo) : '') . "
            ORDER BY edit_date DESC
            LIMIT 1
        "
, array($title$styleId));
    }

    public function 
getHistoryForTemplate($title$styleId)
    {
        return 
$this->fetchAllKeyed("
            SELECT *
            FROM xf_template_history
            WHERE title = ? AND style_id = ?
            ORDER BY edit_date DESC
        "
'template_history_id', array($title$styleId));
    }

    public function 
pruneEditHistory($cutOff null)
    {
        if (
$cutOff === null)
        {
            
$logLength XenForo_Application::get('options')->templateHistoryLength;
            if (!
$logLength)
            {
                return 
0;
            }

            
$cutOff XenForo_Application::$time 86400 $logLength;
        }

        
$db $this->_getDb();
        return 
$db->delete('xf_template_history''log_date < ' $db->quote($cutOff));
    }

    public function 
autoMergeTemplate(array $templateXenForo_Diff3 $diff)
    {
        
/** @var $styleModel XenForo_Model_Style */
        
$styleModel $this->getModelFromCache('XenForo_Model_Style');

        
$style $styleModel->getStyleById($template['style_id']);
        if (!
$style)
        {
            return 
false;
        }

        
$parentStyle $styleModel->getStyleById($style['parent_id'], true);
        if (!
$parentStyle)
        {
            return 
false;
        }

        if (
$parentStyle['style_id'])
        {
            
$parentTemplate $this->getEffectiveTemplateByTitle($template['title'], $parentStyle['style_id']);
        }
        else
        {
            
$parentTemplate $this->getTemplateInStyleByTitle($template['title'], 0);
        }
        if (!
$parentTemplate)
        {
            return 
false;
        }

        if (!
$parentTemplate['last_edit_date'] || $parentTemplate['last_edit_date'] < $template['last_edit_date'])
        {
            return 
false;
        }

        
$previousVersion $this->getLatestTemplateHistoryForTemplate(
            
$template['title'], $parentTemplate['style_id'], $template['last_edit_date']
        );
        if (!
$previousVersion)
        {
            return 
false;
        }

        if (!isset(
$template['template']))
        {
            
$template $this->getTemplateById($template['template_id']);
        }
        if (!
$template)
        {
            return 
false;
        }

        
$final $diff->mergeToFinal(
            
$template['template'], $previousVersion['template'], $parentTemplate['template']
        );
        if (
$final === false)
        {
            return 
false;
        }

        
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
        
$dw->setExistingData($template);
        
$dw->set('template'$final);
        
$dw->set('last_edit_date'XenForo_Application::$time);
        return 
$dw->save();
    }

    
/**
     * Returns all the templates that belong to the specified add-on.
     *
     * @param string $addOnId
     *
     * @return array Format: [title] => info
     */
    
public function getMasterTemplatesInAddOn($addOnId)
    {
        return 
$this->fetchAllKeyed('
            SELECT *
            FROM xf_template
            WHERE addon_id = ?
                AND style_id = 0
            ORDER BY CONVERT(title USING utf8)
        '
'title'$addOnId);
    }

    
/**
     * Gets the template map IDs of any templates that include the source
     * map IDs. For example, this would pass in the map ID of _header
     * and get the map ID of the PAGE_CONTAINER.
     *
     * @param integer|array $mapIds One map ID as a scalar or an array of many.
     *
     * @return array Array of map IDs
     */
    
public function getIncludingTemplateMapIds($mapIds)
    {
        if (!
is_array($mapIds))
        {
            
$mapIds = array($mapIds);
        }

        if (!
$mapIds)
        {
            return array();
        }

        
$db $this->_getDb();

        return 
$db->fetchCol('
            SELECT source_map_id
            FROM xf_template_include
            WHERE target_map_id IN (' 
$db->quote($mapIds) . ')
        '
);
    }

    
/**
     * Gets the template map information for all templates that are mapped
     * to the specified template ID.
     *
     * @param integer $templateId
     *
     * @return array Format: [] => (array) template map info
     */
    
public function getMappedTemplatesByTemplateId($templateId)
    {
        return 
$this->_getDb()->fetchAll('
            SELECT *
            FROM xf_template_map
            WHERE template_id = ?
        '
$templateId);
    }

    
/**
     * Gets mapped template information from the parent style of the named
     * template. If the named style is 0 (or invalid), returns false.
     *
     * @param string $title
     * @param integer $styleId
     *
     * @return array|false
     */
    
public function getParentMappedTemplateByTitle($title$styleId)
    {
        if (
$styleId == 0)
        {
            return 
false;
        }

        return 
$this->_getDb()->fetchRow('
            SELECT parent_template_map.*
            FROM xf_template_map AS template_map
            INNER JOIN xf_style AS style ON
                (template_map.style_id = style.style_id)
            INNER JOIN xf_template_map AS parent_template_map ON
                (parent_template_map.style_id = style.parent_id AND parent_template_map.title = template_map.title)
            WHERE template_map.title = ? AND template_map.style_id = ?
        '
, array($title$styleId));
    }

    
/**
     * Gets the list of all template map IDs that include the named phrase.
     *
     * @param string $phraseTitle
     *
     * @return array List of template map IDs
     */
    
public function getTemplateMapIdsThatIncludePhrase($phraseTitle)
    {
        return 
$this->_getDb()->fetchCol('
            SELECT template_map_id
            FROM xf_template_phrase
            WHERE phrase_title = ?
        '
$phraseTitle);
    }

    
/**
     * Returns the path to the template development directory, if it has been configured and exists
     *
     * @return string Path to templates directory
     */
    
public function getTemplateDevelopmentDirectory()
    {
        
$config XenForo_Application::get('config');
        if (!
$config->debug || !$config->development->directory)
        {
            return 
'';
        }

        return 
XenForo_Application::getInstance()->getRootDir()
            . 
'/' $config->development->directory '/file_output/templates';
    }

    
/**
     * Checks that the templates directory has been configured and exists
     *
     * @return boolean
     */
    
public function canImportTemplatesFromDevelopment()
    {
        
$dir $this->getTemplateDevelopmentDirectory();
        return (
$dir && is_dir($dir));
    }

    
/**
     * Deletes the templates that belong to the specified add-on.
     *
     * @param string $addOnId
     */
    
public function deleteTemplatesForAddOn($addOnId)
    {
        
$db $this->_getDb();

        
$titles $db->fetchPairs('
            SELECT template_id, title
            FROM xf_template
            WHERE style_id = 0
                AND addon_id = ?
        '
$addOnId);
        if (!
$titles)
        {
            return;
        }

        
$templateIds array_keys($titles);

        
$this->_deleteTemplates($templateIds);

        
$db->query('
            DELETE FROM xf_template_compiled
            WHERE style_id = 0
                AND title IN (' 
$db->quote($titles) . ')
        '
);
        if (
XenForo_Application::get('options')->templateFiles)
        {
            
XenForo_Template_FileHandler::delete($titles0null);
        }
        
XenForo_Template_Compiler::resetTemplateCache();
    }

    public function 
deleteTemplatesInStyle($styleId)
    {
        
$db $this->_getDb();

        
$titles $db->fetchPairs('
            SELECT template_id, title
            FROM xf_template
            WHERE style_id = ?
        '
$styleId);
        if (!
$titles)
        {
            return;
        }

        
$templateIds array_keys($titles);

        
$this->_deleteTemplates($templateIds);

        
$db->query('
            DELETE FROM xf_template_compiled
            WHERE style_id = ?
                AND title IN (' 
$db->quote($titles) . ')
        '
$styleId);
        if (
XenForo_Application::get('options')->templateFiles)
        {
            
XenForo_Template_FileHandler::delete($titles$styleIdnull);
        }

        
XenForo_Template_Compiler::resetTemplateCache();
    }

    protected function 
_deleteTemplates(array $templateIds)
    {
        
$db $this->_getDb();
        
$quotedIds $db->quote($templateIds);

        
$db->query('
            DELETE FROM xf_template_include
            WHERE source_map_id IN (
                SELECT template_map_id
                FROM xf_template AS template
                INNER JOIN xf_template_map AS template_map ON
                    (template.template_id = template_map.template_id)
                WHERE template.template_id IN ('
$quotedIds ')
            )
        '
);
        
$db->query('
            DELETE FROM xf_template_phrase
            WHERE template_map_id IN (
                SELECT template_map_id
                FROM xf_template AS template
                INNER JOIN xf_template_map AS template_map ON
                    (template.template_id = template_map.template_id)
                WHERE template.template_id IN ('
$quotedIds ')
            )
        '
);

        
$db->delete('xf_template'"template_id IN ($quotedIds)");
        
$db->delete('xf_template_map'"template_id IN ($quotedIds)");
        
$db->delete('xf_template_modification_log'"template_id IN ($quotedIds)");
    }

    
/**
     * Imports all templates from the templates directory into the database
     */
    
public function importTemplatesFromDevelopment()
    {
        
$db $this->_getDb();

        
$templateDir $this->getTemplateDevelopmentDirectory();
        if (!
$templateDir && !is_dir($templateDir))
        {
            throw new 
XenForo_Exception("Template development directory not enabled or doesn't exist");
        }

        
$files glob("$templateDir/*.html");
        if (!
$files)
        {
            throw new 
XenForo_Exception("Template development directory does not have any templates");
        }

        
$metaData XenForo_Helper_DevelopmentXml::readMetaDataFile($templateDir '/_metadata.xml');
        
$addOnTemplates $this->getMasterTemplatesInAddOn('XenForo');

        
XenForo_Db::beginTransaction($db);

        
$titles = array();
        foreach (
$files AS $templateFile)
        {
            
$filename basename($templateFile);
            if (
preg_match('/^(.+).html$/'$filename$match))
            {
                
$titles[] = $match[1];
            }
        }

        
$existingTemplates $this->getTemplatesInStyleByTitles($titles0);

        foreach (
$files AS $templateFile)
        {
            if (!
is_readable($templateFile))
            {
                throw new 
XenForo_Exception("Template file '$templateFile' not readable");
            }

            
$filename basename($templateFile);
            if (
preg_match('/^(.+).html$/'$filename$match))
            {
                
$templateName $match[1];
                
$data file_get_contents($templateFile);

                
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
                if (isset(
$existingTemplates[$templateName]))
                {
                    
$dw->setExistingData($existingTemplates[$templateName], true);
                }
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
                
$dw->bulkSet(array(
                    
'style_id' => 0,
                    
'title' => $templateName,
                    
'template' => $data,
                    
'addon_id' => 'XenForo',
                    
'version_id' => 0,
                    
'version_string' => ''
                
));
                if (isset(
$metaData[$templateName]))
                {
                    
$dw->bulkSet($metaData[$templateName]);
                }
                
$dw->save();

                unset(
$addOnTemplates[$templateName]);
            }
        }

        
// removed templates
        
foreach ($addOnTemplates AS $addOnTemplate)
        {
            
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
            
$dw->setExistingData($addOnTemplatetrue);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
            
$dw->delete();
        }

        
XenForo_Db::commit($db);
    }

    
/**
     * Imports the add-on templates XML.
     *
     * @param SimpleXMLElement $xml XML element pointing to the root of the data
     * @param string $addOnId Add-on to import for
     * @param integer $maxExecution Maximum run time in seconds
     * @param integer $offset Number of elements to skip
     *
     * @return boolean|integer True on completion; false if the XML isn't correct; integer otherwise with new offset value
     */
    
public function importTemplatesAddOnXml(SimpleXMLElement $xml$addOnId$maxExecution 0$offset 0)
    {
        
$db $this->_getDb();

        
XenForo_Db::beginTransaction($db);

        
$startTime microtime(true);

        
$templates XenForo_Helper_DevelopmentXml::fixPhpBug50670($xml->template);

        
$titles = array();
        
$current 0;
        foreach (
$templates AS $template)
        {
            
$current++;
            if (
$current <= $offset)
            {
                continue;
            }

            
$titles[] = (string)$template['title'];
        }

        
$existingTemplates $this->getTemplatesInStyleByTitles($titles0);

        
$current 0;
        
$restartOffset false;
        foreach (
$templates AS $template)
        {
            
$current++;
            if (
$current <= $offset)
            {
                continue;
            }

            
$templateName = (string)$template['title'];

            
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
            if (isset(
$existingTemplates[$templateName]))
            {
                
$dw->setExistingData($existingTemplates[$templateName], true);
            }
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
            try
            {
                
$dw->bulkSet(array(
                    
'style_id' => 0,
                    
'title' => $templateName,
                    
'template' => XenForo_Helper_DevelopmentXml::processSimpleXmlCdata($template),
                    
'addon_id' => $addOnId,
                    
'version_id' => (int)$template['version_id'],
                    
'version_string' => (string)$template['version_string']
                ));
                
$dw->save();
            } catch (
XenForo_Exception $e)
            {
                throw new 
XenForo_Exception("$templateName: " $e->getMessage(), true);
            }

            if (
$maxExecution && (microtime(true) - $startTime) > $maxExecution)
            {
                
$restartOffset $current;
                break;
            }
        }

        if (!
$restartOffset)
        {
            unset(
$existingTemplates); // just save memory

            // now look for templates that have been removed
            
$addOnTemplates $this->getMasterTemplatesInAddOn($addOnId);
            foreach (
$templates AS $template)
            {
                
$title = (string)$template['title'];
                unset(
$addOnTemplates[$title]);
            }

            foreach (
$addOnTemplates AS $addOnTemplate)
            {
                
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
                
$dw->setExistingData($addOnTemplatetrue);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
                
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
                
$dw->delete();
            }
        }

        
XenForo_Db::commit($db);

        return (
$restartOffset $restartOffset true);
    }

    
/**
     * Imports templates into a given style. Note that this assumes the style is already empty.
     * It does not check for conflicts.
     *
     * @param SimpleXMLElement $xml
     * @param integer $styleId
     * @param string|null $addOnId If non-null, consider only templates from this add-on to be imported
     */
    
public function importTemplatesStyleXml(SimpleXMLElement $xml$styleId$addOnId null)
    {
        
$db $this->_getDb();

        if (
$xml->template === null)
        {
            return;
        }

        
$existingTemplates $this->getAllTemplatesInStyle($styleId);

        
XenForo_Db::beginTransaction($db);

        foreach (
$xml->template AS $template)
        {
            
$templateName = (string)$template['title'];

            
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
            if (isset(
$existingTemplates[$templateName]))
            {
                
$dw->setExistingData($existingTemplates[$templateName], true);
            }
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
            
$dw->bulkSet(array(
                
'style_id' => $styleId,
                
'title' => (string)$template['title'],
                
'template' => XenForo_Helper_DevelopmentXml::processSimpleXmlCdata($template),
                
'addon_id' => (string)$template['addon_id'],
                
'version_id' => (int)$template['version_id'],
                
'version_string' => (string)$template['version_string'],
                
'disable_modifications' => (int)$template['disable_modifications']
            ));
            
$dw->save();

            unset(
$existingTemplates[$templateName]);
        }

        
// removed templates
        
foreach ($existingTemplates AS $existingTemplate)
        {
            if (
$addOnId !== null && $existingTemplate['addon_id'] !== $addOnId)
            {
                continue;
            }

            
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template');
            
$dw->setExistingData($existingTemplatetrue);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_TEST_COMPILEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_CHECK_DUPLICATEfalse);
            
$dw->setOption(XenForo_DataWriter_Template::OPTION_REBUILD_TEMPLATE_MAPfalse);
            
$dw->delete();
        }

        
XenForo_Db::commit($db);
    }

    
/**
     * Appends the add-on template XML to a given DOM element.
     *
     * @param DOMElement $rootNode Node to append all elements to
     * @param string $addOnId Add-on ID to be exported
     */
    
public function appendTemplatesAddOnXml(DOMElement $rootNode$addOnId)
    {
        
$document $rootNode->ownerDocument;

        
$templates $this->getMasterTemplatesInAddOn($addOnId);
        foreach (
$templates AS $template)
        {
            
$templateNode $document->createElement('template');
            
$templateNode->setAttribute('title'$template['title']);
            
$templateNode->setAttribute('version_id'$template['version_id']);
            
$templateNode->setAttribute('version_string'$template['version_string']);
            
$templateNode->appendChild(XenForo_Helper_DevelopmentXml::createDomCdataSection($document$template['template']));

            
$rootNode->appendChild($templateNode);
        }
    }

    
/**
     * Appends the template XML for templates in the specified style.
     *
     * @param DOMElement $rootNode
     * @param integer $styleId
     * @param string|null $limitAddOnId If non-null, limits only to templates in this add-on
     * @param boolean $independent If true, all customizations from parent styles will be included in this
     */
    
public function appendTemplatesStyleXml(DOMElement $rootNode$styleId$limitAddOnId null$independent false)
    {
        
$document $rootNode->ownerDocument;

        if (!
$styleId)
        {
            
// getting master data
            
$independent false;
        }

        if (
$independent)
        {
            
$templates $this->getAllEffectiveTemplatesInStyle($styleId);
        }
        else
        {
            
$templates $this->getAllTemplatesInStyle($styleId);
        }

        foreach (
$templates AS $template)
        {
            if (
$limitAddOnId !== null && $template['addon_id'] !== $limitAddOnId)
            {
                
// wrong add-on
                
continue;
            }

            if (
$independent && !$template['style_id'])
            {
                
// master version of a template
                
continue;
            }

            
$templateNode $document->createElement('template');
            
$templateNode->setAttribute('title'$template['title']);
            
$templateNode->setAttribute('addon_id'$template['addon_id']);
            
$templateNode->setAttribute('version_id'$template['version_id']);
            
$templateNode->setAttribute('version_string'$template['version_string']);
            
$templateNode->setAttribute('disable_modifications'$template['disable_modifications']);
            
$templateNode->appendChild(XenForo_Helper_DevelopmentXml::createDomCdataSection($document$template['template']));

            
$rootNode->appendChild($templateNode);
        }
    }

    
/**
     * Gets the templates development XML.
     *
     * @return DOMDocument
     */
    
public function getTemplatesDevelopmentXml()
    {
        
$document = new DOMDocument('1.0''utf-8');
        
$document->formatOutput true;
        
$rootNode $document->createElement('templates');
        
$document->appendChild($rootNode);

        
$this->appendTemplatesAddOnXml($rootNode'XenForo');

        return 
$document;
    }

    public function 
reparseTemplate($templateId$fullCompile true)
    {
        
$dw XenForo_DataWriter::create('XenForo_DataWriter_Template'XenForo_DataWriter::ERROR_SILENT);
        
$dw->setExistingData($templateId);
        
$dw->reparseTemplate();
        
$dw->setOption(XenForo_DataWriter_Template::OPTION_DEV_OUTPUT_DIR'');
        
$dw->setOption(XenForo_DataWriter_Template::OPTION_FULL_COMPILE$fullCompile);
        
$dw->save();
        return 
$dw;
    }

    public function 
getMapIdsToCompileByTitles(array $titles)
    {
        
$mapIds $this->getMapIdsByTemplateTitles($titles);
        
$mapIds array_merge($mapIds$this->getIncludingTemplateMapIds($mapIds));

        return 
array_unique($mapIds);
    }

    
/**
     * Reparses all templates.
     *
     * @param integer $maxExecution The approx maximum length of time this function will run for
     * @param integer $startStyle The ID of the style to start with
     * @param integer $startTemplate The number of the template to start with in that style (not ID, just counter)
     *
     * @return boolean|array True if completed successful, otherwise array of where to restart (values start style ID, start template counter)
     */
    
public function reparseAllTemplates($maxExecution 0$startStyle 0$startTemplate 0)
    {
        
$db $this->_getDb();

        
$styles $this->getModelFromCache('XenForo_Model_Style')->getAllStyles();
        
$styleIds array_merge(array(0), array_keys($styles));
        
sort($styleIds);

        
$lastStyle 0;
        
$startTime microtime(true);
        
$complete true;

        
XenForo_Db::beginTransaction($db);

        foreach (
$styleIds AS $styleId)
        {
            if (
$styleId $startStyle)
            {
                continue;
            }

            
$lastStyle $styleId;
            
$lastTemplate 0;

            
$templates $this->getAllTemplatesInStyle($styleIdtrue);
            foreach (
$templates AS $template)
            {
                
$lastTemplate++;
                if (
$styleId == $startStyle && $lastTemplate $startTemplate)
                {
                    continue;
                }

                
$this->reparseTemplate($templatefalse);

                if (
$maxExecution && (microtime(true) - $startTime) > $maxExecution)
                {
                    
$complete false;
                    break 
2;
                }
            }
        }

        
XenForo_Db::commit($db);

        if (
$complete)
        {
            return 
true;
        }
        else
        {
            return array(
$lastStyle$lastTemplate 1);
        }
    }

    
/**
     * Recompiles all templates.
     *
     * @param integer $maxExecution The approx maximum length of time this function will run for
     * @param integer $startStyle The ID of the style to start with
     * @param integer $startTemplate The number of the template to start with in that style (not ID, just counter)
     *
     * @return boolean|array True if completed successfull, otherwise array of where to restart (values start style ID, start template counter)
     */
    
public function compileAllTemplates($maxExecution 0$startStyle 0$startTemplate 0)
    {
        
$db $this->_getDb();

        
$styles $this->getModelFromCache('XenForo_Model_Style')->getAllStyles();
        
$styleIds array_merge(array(0), array_keys($styles));
        
sort($styleIds);

        
$lastStyle 0;
        
$startTime microtime(true);
        
$complete true;

        
XenForo_Db::beginTransaction($db);

        foreach (
$styleIds AS $styleId)
        {
            if (
$styleId $startStyle)
            {
                continue;
            }

            
$lastStyle $styleId;
            
$lastTemplate 0;

            
$templates $this->getAllTemplatesInStyle($styleIdtrue);
            foreach (
$templates AS $template)
            {
                
$lastTemplate++;
                if (
$styleId == $startStyle && $lastTemplate $startTemplate)
                {
                    continue;
                }

                
$this->compileNamedTemplateInStyleTree($template['title'], $template['style_id']);

                if (
$maxExecution && (microtime(true) - $startTime) > $maxExecution)
                {
                    
$complete false;
                    break 
2;
                }
            }
        }

        if (
$complete)
        {
            
$compiledRemove $db->fetchAll("
                SELECT DISTINCT c.title, c.style_id
                FROM xf_template_compiled AS c
                LEFT JOIN xf_template_map AS m ON (c.title = m.title AND c.style_id = m.style_id)
                WHERE m.title IS NULL
            "
);
            foreach (
$compiledRemove AS $remove)
            {
                
$db->delete('xf_template_compiled',
                    
"style_id = " $db->quote($remove['style_id']) . " AND title = " $db->quote($remove['title'])
                );
                if (
XenForo_Application::get('options')->templateFiles)
                {
                    
XenForo_Template_FileHandler::delete($remove['title'], $remove['style_id'], null);
                }
            }

            
$this->getModelFromCache('XenForo_Model_Style')->updateAllStylesLastModifiedDate();
            
$this->getModelFromCache('XenForo_Model_AdminTemplate')->updateAdminStyleLastModifiedDate();
        }

        
XenForo_Db::commit($db);

        if (
$complete)
        {
            return 
true;
        }
        else
        {
            return array(
$lastStyle$lastTemplate 1);
        }
    }

    
/**
     * Compiles the named template in the style tree. Any child templates that
     * use this template will be recompiled as well.
     *
     * @param string $title
     * @param integer $styleId
     *
     * @return array A list of template map IDs that were compiled
     */
    
public function compileNamedTemplateInStyleTree($title$styleId)
    {
        
$parsedRecord $this->getEffectiveTemplateByTitle($title$styleId);
        if (!
$parsedRecord)
        {
            return array();
        }
        return 
$this->compileTemplateInStyleTree($parsedRecord);
    }

    
/**
     * Compiles the list of template map IDs and any child templates that are using
     * the same core template.
     *
     * @param integer|array $templateMapIds One map ID as a scalar or many as an array
     *
     * @return array A list of template map IDs that were compiled
     */
    
public function compileMappedTemplatesInStyleTree($templateMapIds)
    {
        
$templates $this->getUniqueTemplatesByMapIds($templateMapIds);
        
$mapIds = array();

        foreach (
$templates AS $template)
        {
            
$mapIds array_merge($mapIds$this->compileTemplateInStyleTree($template));
        }

        return 
$mapIds;
    }

    
/**
     * Compiles the specified template data in the style tree. This compiles this template
     * in any style that is actually using this template.
     *
     * @param array $parsedRecord Full template information
     *
     * @return array List of template map IDs that were compiled
     */
    
public function compileTemplateInStyleTree(array $parsedRecord)
    {
        
$parsedTemplate unserialize($parsedRecord['template_parsed']);

        
$dependentTemplates = array();

        
$templateMaps $this->getMappedTemplatesByTemplateId($parsedRecord['template_id']);
        
$compileResults false;
        foreach (
$templateMaps AS $templateMap)
        {
            if (
$templateMap['style_id'] == $parsedRecord['style_id'])
            {
                
// only compile the root version and then figure out if we need to compile children
                
$compileResults $this->compileAndInsertParsedTemplate(
                    
$templateMap['template_map_id'],
                    
$parsedTemplate,
                    
$parsedRecord['title'],
                    
$templateMap['style_id']
                );
                if (
$compileResults)
                {
                    
$compileResults['templateMapId'] = $templateMap['template_map_id'];
                }
                break;
            }
        }

        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        
$includeDeleteIds = array();
        
$includeInserts = array();
        
$phraseDeleteIds = array();
        
$phraseInserts = array();

        if (
$compileResults && !$compileResults['failedTemplateIncludes'])
        {
            if (
$compileResults['includedTemplateIds'])
            {
                
$includedTemplates $db->fetchPairs("
                    SELECT title, template_id
                    FROM xf_template_map
                    WHERE template_map_id IN (" 
$db->quote($compileResults['includedTemplateIds']) . ")
                "
);
            }
            else
            {
                
$includedTemplates = array();
            }

            
// each template here is using the same parent version.
            // if we have no includes or all of the includes point to the same
            // template ID, then one compilation is all that's necessary and we
            // can simply copy the results between styles
            
foreach ($templateMaps AS $templateMap)
            {
                
$dependentTemplates[] = $templateMap['template_map_id'];

                if (
$templateMap['style_id'] == $parsedRecord['style_id'])
                {
                    
// already handled
                    
continue;
                }

                
$isSame true;

                if (
$includedTemplates)
                {
                    
$localIncludedTemplates $db->fetchAll("
                        SELECT title, template_id, template_map_id
                        FROM xf_template_map
                        WHERE style_id = ?
                            AND title IN (" 
$db->quote(array_keys($includedTemplates)) . ")
                    "
, array($templateMap['style_id']));
                    foreach (
$localIncludedTemplates AS $localInclude)
                    {
                        if (!isset(
$includedTemplates[$localInclude['title']])
                            || 
$includedTemplates[$localInclude['title']] != $localInclude['template_id']
                        )
                        {
                            
// including a different template, so we need to do a full compile
                            
$isSame false;
                            break;
                        }
                    }
                }
                else
                {
                    
$localIncludedTemplates = array();
                }

                if (
$isSame)
                {
                    
$includeDeleteIds[] = $templateMap['template_map_id'];
                    foreach (
$localIncludedTemplates AS $localInclude)
                    {
                        
$includeInserts[] = '(' $db->quote($templateMap['template_map_id']) . ', ' $db->quote($localInclude['template_map_id']) . ')';
                    }

                    
$phraseDeleteIds[] = $templateMap['template_map_id'];
                    foreach (
$compileResults['includedPhraseTitles'] AS $phraseTitles)
                    {
                        
$phraseInserts[] = '(' $db->quote($templateMap['template_map_id']) . ', ' $db->quote($phraseTitles) . ')';
                    }

                    foreach (
$compileResults['compiledCache'] AS $languageId => $compiled)
                    {
                        
$this->_insertCompiledTemplateRecord(
                            
$templateMap['style_id'], $languageId$parsedRecord['title'], $compiled
                        
);
                    }
                }
                else
                {
                    
$this->compileAndInsertParsedTemplate(
                        
$templateMap['template_map_id'],
                        
$parsedTemplate,
                        
$parsedRecord['title'],
                        
$templateMap['style_id']
                    );
                }
            }
        }
        else
        {
            foreach (
$templateMaps AS $templateMap)
            {
                
$this->compileAndInsertParsedTemplate(
                    
$templateMap['template_map_id'],
                    
$parsedTemplate,
                    
$parsedRecord['title'],
                    
$templateMap['style_id']
                );
                
$dependentTemplates[] = $templateMap['template_map_id'];
            }
        }

        if (
$includeDeleteIds)
        {
            
$db->delete('xf_template_include''source_map_id IN (' $db->quote($includeDeleteIds) . ')');
        }
        if (
$includeInserts)
        {
            
$db->query("
                INSERT IGNORE INTO xf_template_include
                    (source_map_id, target_map_id)
                VALUES " 
implode(','$includeInserts)
            );
        }
        if (
$phraseDeleteIds)
        {
            
$db->delete('xf_template_phrase''template_map_id IN (' $db->quote($phraseDeleteIds) . ')');
        }
        if (
$phraseInserts)
        {
            
$db->query("
                INSERT IGNORE INTO xf_template_phrase
                    (template_map_id, phrase_title)
                VALUES " 
implode(','$phraseInserts)
            );
        }

        
XenForo_Db::commit($db);

        return 
$dependentTemplates;
    }

    
/**
     * Compiles and inserts the specified effective templates.
     *
     * @param array $templates Array of effective template info
     */
    
public function compileAndInsertEffectiveTemplates(array $templates)
    {
        foreach (
$templates AS $template)
        {
            
$this->compileAndInsertParsedTemplate(
                
$template['template_map_id'],
                
unserialize($template['template_parsed']),
                
$template['title'],
                isset(
$template['map_style_id']) ? $template['map_style_id'] : $template['style_id']
            );
        }
    }

    
/**
     * Recompiles all templates that include the named phrase.
     *
     * @param string $phraseTitle
     * @param bool $deferred If true, defer this
     *
     * @return array List of template map IDs including the phrase
     */
    
public function compileTemplatesThatIncludePhrase($phraseTitle$deferred false)
    {
        
$mapIds $this->getTemplateMapIdsThatIncludePhrase($phraseTitle);
        if (
$deferred)
        {
            
XenForo_Application::defer('TemplatePartialCompile', array(
                
'recompileMapIds' => $mapIds
            
), nulltrue);
        }
        else
        {
            
$this->compileMappedTemplatesInStyleTree($mapIds);
        }

        return 
$mapIds;
    }

    
/**
     * Compiles the specified parsed template and updates the compiled table
     * and included templates list.
     *
     * @param integer $templateMapId The map ID of the template being compiled (for includes)
     * @param string|array $parsedTemplate Parsed form of the template
     * @param string $title Title of the template
     * @param integer $compileStyleId Style ID of the template
     * @param boolean $doDbWrite If non null, controls whether the DB write/template cache is written
     *
     * @return array|bool
     */
    
public function compileAndInsertParsedTemplate($templateMapId$parsedTemplate$title$compileStyleId$doDbWrite null)
    {
        
$isCss = (substr($title, -4) == '.css');

        if (
$doDbWrite === null)
        {
            
$doDbWrite = ($isCss || $compileStyleId);
        }

        
$compiler = new XenForo_Template_Compiler('');
        
$languages $this->getModelFromCache('XenForo_Model_Language')->getAllLanguages();

        
$db $this->_getDb();

        
$compiledCache = array();

        if (
$isCss)
        {
            
$compiledTemplate $compiler->compileParsed($parsedTemplate$title$compileStyleId0);
            
$compiledCache[0] = $compiledTemplate;
            if (
$doDbWrite)
            {
                
$this->_insertCompiledTemplateRecord($compileStyleId0$title$compiledTemplate);
            }
        }
        else
        {
            foreach (
$languages AS $language)
            {
                
$compiledTemplate $compiler->compileParsed($parsedTemplate$title$compileStyleId$language['language_id']);
                
$compiledCache[$language['language_id']] = $compiledTemplate;

                if (
$doDbWrite)
                {
                    
$this->_insertCompiledTemplateRecord($compileStyleId$language['language_id'], $title$compiledTemplate);
                }
            }
        }

        
$mapIdQuoted $db->quote($templateMapId);

        
$ins = array();
        
$includedTemplateIds = array();

        foreach (
$compiler->getIncludedTemplates() AS $includedMapId)
        {
            
$ins[] = '(' $mapIdQuoted ', ' $db->quote($includedMapId) . ')';
            
$includedTemplateIds[] = $includedMapId;
        }

        if (
$doDbWrite)
        {
            
$db->delete('xf_template_include''source_map_id = ' $db->quote($templateMapId));
            if (
$ins)
            {
                
$db->query("
                    INSERT IGNORE INTO xf_template_include
                        (source_map_id, target_map_id)
                    VALUES
                        " 
implode(','$ins)
                );
            }
        }

        
$ins = array();
        
$includedPhraseTitles = array();

        foreach (
$compiler->getIncludedPhrases() AS $includedPhrase)
        {
            if (
strlen($includedPhrase) > 75)
            {
                continue; 
// too long, can't be a valid phrase
            
}

            
$ins[] = '(' $mapIdQuoted ', ' $db->quote($includedPhrase) . ')';
            
$includedPhraseTitles[] = $includedPhrase;
        }

        if (
$doDbWrite)
        {
            
$db->delete('xf_template_phrase''template_map_id = ' $db->quote($templateMapId));
            if (
$ins)
            {
                
$db->query("
                    INSERT IGNORE INTO xf_template_phrase
                        (template_map_id, phrase_title)
                    VALUES
                        " 
implode(','$ins)
                );
            }
        }

        return array(
            
'includedTemplateIds' => $includedTemplateIds,
            
'failedTemplateIncludes' => $compiler->getFailedTemplateIncludes(),
            
'includedPhraseTitles' => $includedPhraseTitles,
            
'compiledCache' => $compiledCache,
            
'doDbWrite' => $doDbWrite
        
);
    }

    protected function 
_insertCompiledTemplateRecord($styleId$languageId$title$compiledTemplate)
    {
        
$this->_getDb()->query("
            INSERT INTO xf_template_compiled
                (style_id, language_id, title, template_compiled)
            VALUES
                (?, ?, ?, ?)
            ON DUPLICATE KEY UPDATE template_compiled = VALUES(template_compiled)
        "
, array($styleId$languageId$title$compiledTemplate));
        if (
XenForo_Application::get('options')->templateFiles)
        {
            
XenForo_Template_FileHandler::save($title$styleId$languageId$compiledTemplate);
        }
    }

    
/**
     * Determines if the visiting user can modify a template in the specified style.
     * If debug mode is not enabled, users can't modify templates in the master style.
     *
     * @param integer $styleId
     *
     * @return boolean
     */
    
public function canModifyTemplateInStyle($styleId)
    {
        return (
$styleId != || XenForo_Application::debugMode());
    }

    
/**
     * Builds (and inserts) the template map for a specified template, from
     * the root of the style tree.
     *
     * @param string $title Title of the template being build
     * @param array $data Injectable data. Supports styleTree and styleTemplateMap.
     */
    
public function buildTemplateMap($title, array $data = array())
    {
        if (!isset(
$data['styleTree']))
        {
            
/* @var $styleModel XenForo_Model_Style */
            
$styleModel $this->getModelFromCache('XenForo_Model_Style');
            
$data['styleTree'] = $styleModel->getStyleTreeAssociations($styleModel->getAllStyles());
        }

        if (!isset(
$data['styleTemplateMap']))
        {
            
$data['styleTemplateMap'] = $this->getTemplateIdInStylesByTitle($title);
        }

        
$mapUpdates $this->findTemplateMapUpdates(0$data['styleTree'], $data['styleTemplateMap']);
        if (
$mapUpdates)
        {
            
$db $this->_getDb();
            
$toDeleteInStyleIds = array();

            foreach (
$mapUpdates AS $styleId => $newTemplateId)
            {
                if (
$newTemplateId == 0)
                {
                    
$toDeleteInStyleIds[] = $styleId;
                    continue;
                }

                
$db->query('
                    INSERT INTO xf_template_map
                        (style_id, title, template_id)
                    VALUES
                        (?, ?, ?)
                    ON DUPLICATE KEY UPDATE
                        template_id = ?
                '
, array($styleId$title$newTemplateId$newTemplateId));
            }

            if (
$toDeleteInStyleIds)
            {
                
$db->delete('xf_template_map',
                    
'title = ' $db->quote($title) . ' AND style_id IN (' $db->quote($toDeleteInStyleIds) . ')'
                
);
                
$db->delete('xf_template_compiled',
                    
'title = ' $db->quote($title) . ' AND style_id IN (' $db->quote($toDeleteInStyleIds) . ')'
                
);
                if (
XenForo_Application::get('options')->templateFiles)
                {
                    
XenForo_Template_FileHandler::delete($title$toDeleteInStyleIdsnull);
                }
            }
        }
    }

    
/**
     * Finds the necessary template map updates for the specified template within the
     * sub-tree.
     *
     * If {$defaultTemplateId} is non-0, a return entry will be inserted for {$parentId}.
     *
     * @param integer $parentId Parent of the style sub-tree to search.
     * @param array $styleTree Tree of styles
     * @param array $styleTemplateMap List of styleId => templateId pairs for the places where this template has been customized.
     * @param integer $defaultTemplateId The default template ID that non-customized template in the sub-tree should get.
     *
     * @return array Format: [style id] => [effective template id]
     */
    
public function findTemplateMapUpdates($parentId, array $styleTree, array $styleTemplateMap$defaultTemplateId 0)
    {
        
$output = array();

        if (isset(
$styleTemplateMap[$parentId]))
        {
            
$defaultTemplateId $styleTemplateMap[$parentId];
        }

        
$output[$parentId] = $defaultTemplateId;

        if (!isset(
$styleTree[$parentId]))
        {
            return 
$output;
        }

        foreach (
$styleTree[$parentId] AS $styleId)
        {
            
$output += $this->findTemplateMapUpdates($styleId$styleTree$styleTemplateMap$defaultTemplateId);
        }

        return 
$output;
    }

    
/**
     * Inserts the template map records for all elements of various styles.
     *
     * @param array $styleMapList Format: [style id][title] => template id
     * @param bolean $truncate If true, all map data is truncated (quicker that way)
     */
    
public function insertTemplateMapForStyles(array $styleMapList$truncate false)
    {
        
$db $this->_getDb();

        
XenForo_Db::beginTransaction($db);

        if (
$truncate)
        {
            
$db->query('TRUNCATE TABLE xf_template_map');
            
$db->query('TRUNCATE TABLE xf_template_include');
            
$db->query('TRUNCATE TABLE xf_template_phrase');
        }

        foreach (
$styleMapList AS $builtStyleId => $map)
        {
            if (!
$truncate)
            {
                
$db->delete('xf_template_map''style_id = ' $db->quote($builtStyleId));
            }

            foreach (
$map AS $title => $templateId)
            {
                
$db->insert('xf_template_map', array(
                    
'style_id' => $builtStyleId,
                    
'title' => $title,
                    
'template_id' => $templateId
                
));
            }
        }

        
XenForo_Db::commit($db);
    }

    
/**
     * Builds the full template map data for an entire style sub-tree.
     *
     * @param integer $styleId Starting style. This style and all children will be built.
     *
     * @return array Format: [style id][title] => template id
     */
    
public function buildTemplateMapForStyleTree($styleId)
    {
        
/* @var $styleModel XenForo_Model_Style */
        
$styleModel $this->getModelFromCache('XenForo_Model_Style');

        
$styles $styleModel->getAllStyles();
        
$styleTree $styleModel->getStyleTreeAssociations($styles);
        
$styles[0] = true;

        if (
$styleId && !isset($styles[$styleId]))
        {
            return array();
        }

        
$map = array();
        if (
$styleId)
        {
            
$style $styles[$styleId];

            
$templates $this->getEffectiveTemplateListForStyle($style['parent_id']);
            foreach (
$templates AS $template)
            {
                
$map[$template['title']] = $template['template_id'];
            }
        }

        return 
$this->_buildTemplateMapForStyleTree($styleId$map$styles$styleTree);
    }

    
/**
     * Internal handler to build the template map data for a style sub-tree.
     * Calls itself recursively.
     *
     * @param integer $styleId Style to build (builds children automatically)
     * @param array $map Base template map data. Format: [title] => template id
     * @param array $styles List of styles
     * @param array $styleTree Style tree
     *
     * @return array Format: [style id][title] => template id
     */
    
protected function _buildTemplateMapForStyleTree($styleId, array $map, array $styles, array $styleTree)
    {
        if (!isset(
$styles[$styleId]))
        {
            return array();
        }

        
$customTemplates $this->getAllTemplatesInStyle($styleId);
        foreach (
$customTemplates AS $template)
        {
            
$map[$template['title']] = $template['template_id'];
        }

        
$output = array($styleId => $map);

        if (isset(
$styleTree[$styleId]))
        {
            foreach (
$styleTree[$styleId] AS $childStyleId)
            {
                
$output += $this->_buildTemplateMapForStyleTree($childStyleId$map$styles$styleTree);
            }
        }

        return 
$output;
    }

    
/**
     * Replaces <xen:require/include/edithint with <link rel="xenforo_x"
     * for the purposes of easy WebDAV editing.
     *
     * @param string $templateText
     *
     * @return string
     */
    
public static function replaceIncludesWithLinkRel($templateText)
    {
        
$search = array(
            
'#<xen:requires+css="([^"]+)"s*/>#siU'
            
=>    '<link rel="xenforo_stylesheet" type="text/css" href="1" />',

            
'#<xen:edithints+template="([^"]+.css)"s*/>#siU'
            
=> '<link rel="xenforo_stylesheet_hint" type="text/css" href="1" />',

            
'#<xen:edithints+template="([^"]+)"s*/>#siU'
            
=> '<link rel="xenforo_template_hint" type="text/html" href="1.html" />',

            
'#<xen:includes+template="([^"]+)"(s*/)?>#siU'
            
=> '<link rel="xenforo_template" type="text/html" href="1.html"2>',

            
'#</xen:include>#siU'
            
=> '</link>',
        );

        return 
preg_replace(array_keys($search), $search$templateText);
    }

    
/**
     * Replaces <link rel="xenforo_x" with <xen:require/include/edithint
     * for the purposes of easy WebDAV editing.
     *
     * @param string $templateText
     *
     * @return string
     */
    
public static function replaceLinkRelWithIncludes($templateText)
    {
        
$search = array(
            
'#</link>#siU'
            
=> '</xen:include>',

            
'#<link rel="xenforo_template" type="text/html" href="([^"]+)(.html)?"(s*/)?>#siU'
            
=> '<xen:include template="1"3>',

            
'#<link rel="xenforo_template_hint" type="text/html" href="([^"]+)(.html)?"s/>#siU'
            
=> '<xen:edithint template="1" />',

            
'#<link rel="xenforo_stylesheet_hint" type="text/css" href="([^"]+)"s*/>#siU'
            
=> '<xen:edithint template="1" />',

            
'#<link rel="xenforo_stylesheet" type="text/css" href="([^"]+)"s*/>#siU'
            
=> '<xen:require css="1" />'
        
);

        return 
preg_replace(array_keys($search), $search$templateText);
    }

    
/**
     * Writes out the complete set of template files to the file system
     *
     * @param boolean Enable the templateFiles option after completion.
     * @param boolean Manipulate the option values to ensure failsafe operation.
     */
    
public function writeTemplateFiles($enable false$handleOptions true)
    {
        if (
$handleOptions && XenForo_Application::get('options')->templateFiles)
        {
            
$this->getModelFromCache('XenForo_Model_Option')->updateOptions(array('templateFiles' => 0));
        }

        
$this->deleteTemplateFiles();

        
$templates $this->_getDb()->query('SELECT * FROM xf_template_compiled');
        while (
$template $templates->fetch())
        {
            
XenForo_Template_FileHandler::save($template['title'], $template['style_id'], $template['language_id'], $template['template_compiled']);
        }

        if (
$handleOptions && $enable)
        {
            
$this->getModelFromCache('XenForo_Model_Option')->updateOptions(array('templateFiles' => 1));
        }
    }

    
/**
     * Deletes the file versions of all templates
     */
    
public function deleteTemplateFiles()
    {
        
XenForo_Template_FileHandler::delete(nullnullnull);
    }
}
Онлайн: 2
Реклама