Файл: library/XenForo/Model/Style.php
Строк: 424
<?php
/**
* Model for styles
*
* @package XenForo_Styles
*/
class XenForo_Model_Style extends XenForo_Model
{
/**
* Gets style information by ID. Information about the master style
* can be fetched using an ID of 0 if $fetchMaster is true.
*
* @param integer $id Style ID
* @param boolean $fetchMaster If true, passing an ID of 0 will fetch info about the master style.
*
* @return array|false
*/
public function getStyleById($id, $fetchMaster = false)
{
if (strval($id) === '0')
{
if ($fetchMaster)
{
return array(
'style_id' => 0,
'title' => new XenForo_Phrase('master_style'),
'parent_id' => 0
);
}
else
{
return false;
}
}
$localCacheKey = 'style_' . $id;
if (($data = $this->_getLocalCacheData($localCacheKey)) === false)
{
$data = $this->_getDb()->fetchRow('
SELECT *
FROM xf_style
WHERE style_id = ?
', $id);
$this->setLocalCacheData($localCacheKey, $data);
}
return $data;
}
/**
* Gets information about all styles, not including the master style.
*
* @return array Format: [style id] => (array) style info
*/
public function getAllStyles()
{
if (($styles = $this->_getLocalCacheData('allStyles')) === false)
{
$styles = array();
$stylesDb = $this->_getDb()->fetchAll('
SELECT *
FROM xf_style
ORDER BY title
');
foreach ($stylesDb AS $style)
{
$styles[$style['style_id']] = $style;
}
$this->setLocalCacheData('allStyles', $styles);
}
return $styles;
}
/**
* Return results for admin quick search
*
* @param string Keywords for which to search
*
* @return array
*/
public function getStylesForAdminQuickSearch($searchText)
{
return $this->fetchAllKeyed('
SELECT *
FROM xf_style
WHERE title LIKE ' . XenForo_Db::quoteLike($searchText, 'lr', $this->_getDb()) . '
ORDER BY title
', 'style_id');
}
/**
* Fetches $styleId from cookie if it's available, or returns the default style ID.
*
* @param int $allowMaster If true, allow the master style to be returned
*
* @return integer
*/
public function getStyleIdFromCookie($allowMaster = true)
{
$styleId = XenForo_Helper_Cookie::getCookie('edit_style_id');
if ($styleId === false)
{
$styleId = (XenForo_Application::debugMode()
? 0
: XenForo_Application::get('options')->defaultStyleId
);
}
if ((!$allowMaster || !XenForo_Application::debugMode()) && !$styleId)
{
$styleId = XenForo_Application::get('options')->defaultStyleId;
}
return $styleId;
}
/**
* Generates the style tree association array based on the list of styles
* (see {@link getAllStyles()}).
*
* @param array $styleList List of styles
*
* @return array Format: [parent style id][] => child style id
*/
public function getStyleTreeAssociations(array $styleList)
{
$parents = array();
foreach ($styleList AS $style)
{
$parents[$style['parent_id']][] = $style['style_id'];
}
return $parents;
}
/**
* Gets a list of child style IDs that are direct children of the specified style.
*
* @param integer $styleId
*
* @return array Array of style IDs
*/
public function getDirectChildStyleIds($styleId)
{
$styles = $this->getAllStyles();
$styleTree = $this->getStyleTreeAssociations($styles);
if (isset($styleTree[$styleId]))
{
return $styleTree[$styleId];
}
else
{
return array();
}
}
/**
* Gets all children of a style ID, no matter how many levels below.
*
* @param integer $styleId
*
* @return array Array of style IDs
*/
public function getAllChildStyleIds($styleId)
{
$styles = $this->getAllStyles();
$styleTree = $this->getStyleTreeAssociations($styles);
if (isset($styleTree[$styleId]))
{
return $this->getAllChildStyleIdsFromTree($styleId, $styleTree);
}
else
{
return array();
}
}
/**
* Internal handler to get call child style IDs.
*
* @param integer $parentId Parent style ID
* @param array $styleTree Tree of styles ([parent id][] => style id)
*
* @return array
*/
public function getAllChildStyleIdsFromTree($parentId, array $styleTree)
{
if (!isset($styleTree[$parentId]))
{
return array();
}
$children = array();
foreach ($styleTree[$parentId] AS $childId)
{
$children[] = $childId;
$children = array_merge($children, $this->getAllChildStyleIdsFromTree($childId, $styleTree));
}
return $children;
}
/**
* Gets a list of styles in the form of a flattened tree. The return
* is an array containing all styles and their related info. Each style
* additionally includes a "depth" element that repesents the depth from
* the (implicit) master. Children of the master have a depth 0, unless
* $baseDepth is overridden.
*
* @param integer $baseDepth Starting depth value.
*
* @return array Format: [style id] => (array) style info, including depth
*/
public function getAllStylesAsFlattenedTree($baseDepth = 0)
{
$styles = $this->getAllStyles();
$tree = $this->getStyleTreeAssociations($styles);
return $this->_buildFlattenedStyleTree($styles, $tree, 0, $baseDepth);
}
/**
* Returns an array of all styles, suitable for use in ACP template syntax as options source.
*
* @param integer Selected style ID
* @param array $styleTree
*
* @return array
*/
public function getStylesForOptionsTag($selectedId = null, $styleTree = null)
{
if ($styleTree === null)
{
$styleTree = $this->getAllStylesAsFlattenedTree();
}
$styles = array();
foreach ($styleTree AS $id => $style)
{
$styles[$id] = array(
'value' => $id,
'label' => $style['title'],
'selected' => ($selectedId == $id),
'depth' => $style['depth']
);
}
return $styles;
}
/**
* Builds the flattened tree recursively, incrementing the depth each time.
*
* @param array $styleList List of styles and their information
* @param array $tree Tree structure of styles ([parent id][] => style id)
* @param integer $root Where to start in the tree
* @param integer $depth Current/starting depth
*
* @return array List of styles with additional depth key
*/
protected function _buildFlattenedStyleTree(array $styleList, array $tree, $root = 0, $depth = 0)
{
if (!isset($tree[$root]) || !is_array($tree[$root]))
{
return array();
}
$output = array();
foreach ($tree[$root] AS $styleId)
{
$output[$styleId] = $styleList[$styleId];
$output[$styleId]['depth'] = $depth;
$output += $this->_buildFlattenedStyleTree($styleList, $tree, $styleId, $depth + 1);
}
return $output;
}
/**
* Gets the base parent list for a style. This list starts with the *parent* of the given style ID, then
* works up the tree, eventually ending with 0.
*
* @param integer $styleId
*
* @return array List of parent style IDs (including 0)
*/
public function getStyleBaseParentList($styleId)
{
$styles = $this->getAllStyles();
$parents = array();
while (isset($styles[$styleId]) && $style = $styles[$styleId])
{
$parents[] = $style['parent_id'];
$styleId = $style['parent_id'];
}
return $parents;
}
/**
* Recursively rebuilds the parent list in part of the style tree.
*
* @param integer $styleId First style to start with. All child will be rebuild.
*/
public function rebuildStyleParentListRecursive($styleId)
{
$styles = $this->getAllStyles();
if (isset($styles[$styleId]))
{
$styleTree = $this->getStyleTreeAssociations($styles);
$baseParentList = $this->getStyleBaseParentList($styleId);
$this->_rebuildStyleParentListRecursive($styleId, $baseParentList, $styles, $styleTree);
}
}
/**
* Internal function to rebuild the style parent list recursively.
*
* @param integer $styleId Base style Id
* @param array $baseParentList Base parent list for the style. Should not include this style ID in it.
* @param array $styles List of styles
* @param array $styleTree Style tree
*/
protected function _rebuildStyleParentListRecursive($styleId, array $baseParentList, array $styles, array $styleTree)
{
if (isset($styles[$styleId]))
{
$parentList = $baseParentList;
array_unshift($parentList, $styleId);
$db = $this->_getDb();
$db->update(
'xf_style',
array('parent_list' => implode(',', $parentList)),
'style_id = ' . $db->quote($styleId)
);
if (isset($styleTree[$styleId]))
{
foreach ($styleTree[$styleId] AS $childStyleId)
{
$this->_rebuildStyleParentListRecursive($childStyleId, $parentList, $styles, $styleTree);
}
}
}
}
/**
* Updates the last modified date of all styles.
*
* @param int|null $time
*/
public function updateAllStylesLastModifiedDate($time = null)
{
if ($time === null)
{
$time = time(); // we want this to be now rather than the page start time
}
$this->_getDb()->update('xf_style', array(
'last_modified_date' => $time
));
$this->rebuildStyleCache();
}
/**
* Helper to determine whether the master style should be shown in lists.
*
* @return boolean
*/
public function showMasterStyle()
{
return XenForo_Application::debugMode();
}
/**
* Returns the total number of templates in the master style
*
* @return integer
*/
public function countMasterTemplates()
{
return $this->_getDb()->fetchOne('SELECT COUNT(*) FROM xf_template WHERE style_id = 0');
}
/**
* Counts the number of customized templates in each non-master style
*
* @param array $styles Array of styles
*
* @return array The $styles array including a templateCount key
*/
public function countCustomTemplatesPerStyle(array $styles = array())
{
$totals = $this->_getDb()->fetchPairs('
SELECT style_id, COUNT(template_id) AS templateCount
FROM xf_template
WHERE style_id <> 0
GROUP BY style_id
');
foreach ($totals AS $styleId => $templateCount)
{
if (isset($styles[$styleId]))
{
$styles[$styleId]['templateCount'] = $templateCount;
}
}
return $styles;
}
/**
* Rebuilds the style cache that is put in the data registry.
*
* @return array
*/
public function rebuildStyleCache()
{
$this->resetLocalCacheData('allStyles');
$styles = $this->getAllStyles();
$this->_getDataRegistryModel()->set('styles', $styles);
return $styles;
}
/**
* Gets the XML representation of a style, including customized templates and properties.
*
* @param array $style
* @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
*
* @return DOMDocument
*/
public function getStyleXml(array $style, $limitAddOnId = null, $independent = false)
{
$document = new DOMDocument('1.0', 'utf-8');
$document->formatOutput = true;
$rootNode = $document->createElement('style');
$rootNode->setAttribute('title', $style['title']);
$rootNode->setAttribute('description', $style['description']);
$rootNode->setAttribute('user_selectable', $style['user_selectable']);
if ($limitAddOnId !== null)
{
$rootNode->setAttribute('addon_id', $limitAddOnId);
}
$document->appendChild($rootNode);
$propertiesNode = $document->createElement('properties');
$rootNode->appendChild($propertiesNode);
$this->getModelFromCache('XenForo_Model_StyleProperty')->appendStylePropertyXml(
$propertiesNode, $style['style_id'], $limitAddOnId, $independent
);
$templatesNode = $document->createElement('templates');
$rootNode->appendChild($templatesNode);
$this->getModelFromCache('XenForo_Model_Template')->appendTemplatesStyleXml(
$templatesNode, $style['style_id'], $limitAddOnId, $independent
);
return $document;
}
/**
* Imports a style XML file.
*
* @param SimpleXMLElement $document
* @param integer $parentStyleId If not overwriting, the ID of the parent style
* @param integer $overwriteStyleId If non-0, parent style is ignored
*/
public function importStyleXml(SimpleXMLElement $document, $parentStyleId = 0, $overwriteStyleId = 0)
{
if ($document->getName() != 'style')
{
throw new XenForo_Exception(new XenForo_Phrase('provided_file_is_not_valid_style_xml'), true);
}
$title = (string)$document['title'];
if ($title === '')
{
throw new XenForo_Exception(new XenForo_Phrase('provided_file_is_not_valid_style_xml'), true);
}
$description = (string)$document['description'];
$addOnId = $document['addon_id'] === null ? null : (string)$document['addon_id'];
/* @var $templateModel XenForo_Model_Template */
$templateModel = $this->getModelFromCache('XenForo_Model_Template');
/* @var $propertyModel XenForo_Model_StyleProperty */
$propertyModel = $this->getModelFromCache('XenForo_Model_StyleProperty');
if ($document['user_selectable'] === null)
{
$userSelectable = 1;
}
else
{
$userSelectable = (integer)$document['user_selectable'];
}
$db = $this->_getDb();
XenForo_Db::beginTransaction($db);
if ($overwriteStyleId)
{
$targetStyleId = $overwriteStyleId;
}
else
{
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Style');
$dw->bulkSet(array(
'title' => $title,
'description' => $description,
'parent_id' => $parentStyleId,
'user_selectable' => $userSelectable
));
$dw->set('title', $title);
$dw->set('description', $description);
$dw->set('parent_id', $parentStyleId);
$dw->save();
$targetStyleId = $dw->get('style_id');
}
$templateModel->importTemplatesStyleXml($document->templates, $targetStyleId, $addOnId);
$propertyModel->importStylePropertyXml($document->properties, $targetStyleId, $addOnId);
XenForo_Application::defer('Template', array(), 'templateRebuild', true);
XenForo_Db::commit($db);
}
}