Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Entity/Style.php
<?php

namespace XF\Entity;

use
XF\Mvc\Entity\Entity;
use
XF\Mvc\Entity\Structure;
use
XF\Repository;

use function
in_array, intval, is_string, strval;

/**
 * COLUMNS
 * @property int|null $style_id
 * @property int $parent_id
 * @property array $parent_list
 * @property string $title
 * @property string $description
 * @property array $properties
 * @property array $assets
 * @property array $effective_assets
 * @property int $last_modified_date
 * @property bool $user_selectable
 * @property string|null $designer_mode
 *
 * RELATIONS
 * @property \XF\Entity\Style $Parent
 * @property \XF\Mvc\Entity\AbstractCollection|\XF\Entity\Template[] $Templates
 * @property \XF\Mvc\Entity\AbstractCollection|\XF\Entity\StyleProperty[] $Properties
 * @property \XF\Mvc\Entity\AbstractCollection|\XF\Entity\StylePropertyGroup[] $PropertyGroups
 */
class Style extends Entity
{
    public function
canEdit()
    {
        if (!
$this->style_id && !\XF::$developmentMode)
        {
            return
false;
        }

        return
true;
    }

    public function
canEditStylePropertyDefinitions()
    {
        return \
XF::$developmentMode;
    }

    public function
getGroupedAssets()
    {
       
$assets = $this->assets;
       
$effectiveAssets = $this->effective_assets;
       
$parentEffectiveAssets = [];
        if (
$this->Parent && $this->Parent->effective_assets)
        {
           
$parentEffectiveAssets = $this->Parent->effective_assets;
        }

       
$groupedAssets = [
           
'inherited' => [],
           
'modified' => [],
           
'custom' => []
        ];

        foreach (
$effectiveAssets AS $key => $path)
        {
            if (!isset(
$assets[$key]))
            {
               
$groupedAssets['inherited'][$key] = $path;
            }
            else
            {
                if (isset(
$parentEffectiveAssets[$key]) && $parentEffectiveAssets[$key] !== $path)
                {
                   
$groupedAssets['modified'][$key] = $path;
                }
                else
                {
                   
$groupedAssets['custom'][$key] = $path;
                }
            }
        }

        return
$groupedAssets;
    }

    public function
getParentAssetValue($key)
    {
       
$parentEffectiveAssets = $this->Parent ? $this->Parent->effective_assets : [];
        return
$parentEffectiveAssets[$key] ?? null;
    }

    public function
isAssetInherited($key)
    {
        return isset(
$this->getGroupedAssets()['inherited'][$key]);
    }

    public function
isAssetModified($key)
    {
        return isset(
$this->getGroupedAssets()['modified'][$key]);
    }

    public function
isAssetCustom($key)
    {
        return isset(
$this->getGroupedAssets()['custom'][$key]);
    }

    public function
hasDataUriAssets()
    {
       
$regex = '#^data://styles/' . intval($this->style_id). '/#';

        foreach (
$this->assets AS $assetPath)
        {
            if (
preg_match($regex, $assetPath))
            {
                return
true;
            }
        }

        return
false;
    }

    protected function
verifyParentId($parentId)
    {
        if (
$this->isUpdate() && $parentId)
        {
           
$parent = $this->_em->find('XF:Style', $parentId);
            if (!
$parent || in_array($this->style_id, $parent->parent_list))
            {
               
$this->error(\XF::phrase('please_select_valid_parent_style'), 'parent_id');
                return
false;
            }
        }

        return
true;
    }

    protected function
verifyAssets(&$assets)
    {
       
$output = [];

        foreach (
$assets AS $key => $asset)
        {
            if (
is_string($asset)) // already in key => value format
           
{
               
$asset = [
                   
'key' => $key,
                   
'path' => $asset
               
];
            }

            if (isset(
$asset['key'], $asset['path']))
            {
               
$asset['key'] = trim(strval($asset['key']));
               
$asset['path'] = trim(rtrim(strval($asset['path']), '/'));

                if (
$asset['key'] !== '')
                {
                    if (!
preg_match('/^[a-z0-9_]*$/i', $asset['key']))
                    {
                       
$this->error(\XF::phrase('please_enter_key_using_only_alphanumeric_underscore'));
                        continue;
                    }

                   
$parentValue = $this->getParentAssetValue($asset['key']);

                    if (
$parentValue && $parentValue == $asset['path'])
                    {
                        continue;
                    }

                   
$output[$asset['key']] = $asset['path'];
                }
            }
        }

       
ksort($output);
       
$assets = $output;

        return
true;
    }

    protected function
verifyEffectiveAssets(&$assets)
    {
       
ksort($assets);
        return
true;
    }

    protected function
verifyUserSelectable($value)
    {
        if (!
$value)
        {
           
$defaultStyle = $this->app()->options()->defaultStyleId;
            if (
$this->style_id == $defaultStyle)
            {
               
$this->error(\XF::phrase('it_is_not_possible_to_prevent_users_selecting_the_default_style'), 'user_selectable');
                return
false;
            }
        }

        return
true;
    }

    protected function
rebuildStyleCache()
    {
       
$repo = $this->getStyleRepo();

        \
XF::runOnce('styleCacheRebuild', function() use ($repo)
        {
           
$repo->rebuildStyleCache();
        });
    }

    protected function
triggerPartialRebuild()
    {
       
$this->getStyleRepo()->enqueuePartialStyleDataRebuild();
    }

    protected function
triggerFullRebuild()
    {
       
$this->getStyleRepo()->triggerStyleDataRebuild();
    }

    protected function
_preSave()
    {
        if (
$this->isChanged(['properties', 'parent_id']))
        {
           
$this->last_modified_date = time();
        }

        if (
$this->isChanged('designer_mode')
            &&
$this->getExistingValue('designer_mode')
            &&
$this->getValue('designer_mode') !== null
       
)
        {
           
$this->error(\XF::phrase('once_enabled_it_is_not_possible_to_change_designer_mode_id_use_cli_disable_enable'));
        }
    }

    protected function
_postSave()
    {
        if (
$this->isChanged('parent_id'))
        {
           
$this->triggerFullRebuild();
        }
        else if (
$this->isChanged('assets'))
        {
           
$this->triggerPartialRebuild();
        }

        if (
$this->isChanged('assets'))
        {
           
// want to do this even with a parent ID change (as they are independent)
           
$this->app()->designerOutput()->rebuildAssetsFile($this);
        }

       
$this->rebuildStyleCache();
    }

    protected function
_preDelete()
    {
       
$styleCount = $this->finder('XF:Style')->total();
        if (
$styleCount <= 1)
        {
           
$this->error(\XF::phrase('it_is_not_possible_to_remove_last_style'));
        }

        if (
$this->style_id == $this->app()->options()->defaultStyleId)
        {
           
$this->error(\XF::phrase('it_is_not_possible_to_remove_default_style'));
        }
    }

    protected function
_postDelete()
    {
       
$id = $this->style_id;
       
$db = $this->db();

       
$db->delete('xf_template_map', 'style_id = ?', $id);
       
$db->delete('xf_style_property_map', 'style_id = ?', $id);

       
$hasChildren = (bool)$db->update('xf_style', ['parent_id' => $this->parent_id], 'parent_id = ?', $id);

       
$db->update('xf_node', [
           
'style_id' => 0,
           
'effective_style_id' => 0
       
], "style_id = ? OR effective_style_id = ?", [$id, $id]);

       
$db->update('xf_user', ['style_id' => 0], "style_id = ?", $id);

        foreach (
$this->Templates AS $template)
        {
           
/** @var \XF\Entity\Template $template */
           
$template->setOption('recompile', false);
           
$template->setOption('rebuild_map', false);
           
$template->getBehavior('XF:DesignerOutputWritable')->setOption('write_designer_output', false);
           
$template->delete();
        }

        foreach (
$this->Properties AS $property)
        {
           
/** @var \XF\Entity\StyleProperty $property */
           
$property->setOption('rebuild_map', false);
           
$property->setOption('rebuild_style', false);
           
$property->getBehavior('XF:DesignerOutputWritable')->setOption('write_designer_output', false);
           
$property->delete();
        }

        foreach (
$this->PropertyGroups AS $propertyGroup)
        {
           
/** @var \XF\Entity\StylePropertyGroup $propertyGroup */
           
$propertyGroup->getBehavior('XF:DesignerOutputWritable')->setOption('write_designer_output', false);
           
$propertyGroup->delete();
        }

       
$this->deleteCompiledTemplates();

        \
XF\Util\File::deleteAbstractedDirectory('data://styles/' . $this->style_id . '/');

        if (
$hasChildren)
        {
           
$this->triggerFullRebuild();
        }

       
$this->rebuildStyleCache();
    }

    protected function
deleteCompiledTemplates()
    {
       
$path = 'code-cache://templates';
       
$styleDir = 's' . $this->style_id;
       
$fs = $this->app()->fs();

        foreach (
$fs->listContents($path, false) AS $child)
        {
            if (
$child['type'] != 'dir')
            {
                continue;
            }

           
$stylePath = "$path/$child[basename]/$styleDir";
            if (!
$fs->has($stylePath))
            {
                continue;
            }

            try
            {
               
$fs->deleteDir($stylePath);
            }
            catch (\
Exception $e) {}
        }
    }

    public static function
getStructure(Structure $structure)
    {
       
$structure->table = 'xf_style';
       
$structure->shortName = 'XF:Style';
       
$structure->primaryKey = 'style_id';
       
$structure->columns = [
           
'style_id' => ['type' => self::UINT, 'autoIncrement' => true, 'nullable' => true],
           
'parent_id' => ['type' => self::UINT, 'default' => 0],
           
'parent_list' => ['type' => self::LIST_COMMA, 'maxLength' => 100, 'default' => []],
           
'title' => ['type' => self::STR, 'maxLength' => 50,
               
'required' => 'please_enter_valid_title'
           
],
           
'description' => ['type' => self::STR, 'maxLength' => 100, 'default' => ''],
           
'properties' => ['type' => self::JSON_ARRAY, 'default' => []],
           
'assets' => ['type' => self::JSON_ARRAY, 'default' => []],
           
'effective_assets' => ['type' => self::JSON_ARRAY, 'default' => []],
           
'last_modified_date' => ['type' => self::UINT, 'default' => \XF::$time],
           
'user_selectable' => ['type' => self::BOOL, 'default' => true],
           
'designer_mode' => ['type' => self::STR, 'maxLength' => 50, 'nullable' => true, 'default' => null,
               
'unique' => 'style_designer_mode_ids_must_be_unique',
               
'match' => [
                   
'#^$|^[a-z][a-z0-9_-]*$#i',
                   
'please_enter_valid_designer_mode_id_using_rules'
               
]
            ]
        ];
       
$structure->relations = [
           
'Parent' => [
               
'entity' => 'XF:Style',
               
'type' => self::TO_ONE,
               
'conditions' => [
                    [
'style_id', '=', '$parent_id']
                ],
               
'primary' => true
           
],
           
'Templates' => [
               
'entity' => 'XF:Template',
               
'type' => self::TO_MANY,
               
'conditions' => 'style_id'
           
],
           
'Properties' => [
               
'entity' => 'XF:StyleProperty',
               
'type' => self::TO_MANY,
               
'conditions' => 'style_id'
           
],
           
'PropertyGroups' => [
               
'entity' => 'XF:StylePropertyGroup',
               
'type' => self::TO_MANY,
               
'conditions' => 'style_id'
           
]
        ];

        return
$structure;
    }

   
/**
     * @return Repository\Style
     */
   
protected function getStyleRepo()
    {
        return
$this->_em->getRepository('XF:Style');
    }
}