From 97604642fd50b249a99f0d567cf2646929680dd4 Mon Sep 17 00:00:00 2001 From: Nicolas Morand Date: Thu, 1 Mar 2018 14:54:28 +0100 Subject: [PATCH 1/5] Improves getEditing() fallback --- textpattern/include/txp_css.php | 8 ++++---- textpattern/include/txp_form.php | 2 +- textpattern/include/txp_page.php | 8 ++++---- .../vendors/Textpattern/Skin/AssetBase.php | 16 +++++++++++----- textpattern/vendors/Textpattern/Skin/Form.php | 11 +++++++++++ 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/textpattern/include/txp_css.php b/textpattern/include/txp_css.php index 15a11c3da1..08834bc6bb 100644 --- a/textpattern/include/txp_css.php +++ b/textpattern/include/txp_css.php @@ -161,8 +161,6 @@ function css_edit($message = '', $refresh_partials = false) 'skin', )))); - $default_name = safe_field("css", 'txp_section', "name = 'default'"); - $name = Css::sanitize(assert_string(gps('name'))); $newname = Css::sanitize(assert_string(gps('newname'))); $skin = ($skin !== '') ? $skin : null; @@ -171,8 +169,10 @@ function css_edit($message = '', $refresh_partials = false) $thisSkin = Txp::get('Textpattern\Skin\Skin'); $skin = $thisSkin->setName($skin)->setEditing(); + $editing = $instance->getEditing(); + if ($step == 'css_delete' || empty($name) && $step != 'pour' && !$savenew) { - $name = get_pref('last_css_saved', $default_name); + $name = $editing; } elseif ((($copy || $savenew) && $newname) && !$save_error) { $name = $newname; } elseif ((($newname && ($newname != $name)) || $step === 'pour') && !$save_error) { @@ -217,7 +217,7 @@ function css_edit($message = '', $refresh_partials = false) $rs = array( 'name' => $name, 'newname' => $newname, - 'default' => $default_name, + 'default' => $editing, 'skin' => $skin, 'css' => $thecss, ); diff --git a/textpattern/include/txp_form.php b/textpattern/include/txp_form.php index 4d819d4782..d40ba7f0dd 100644 --- a/textpattern/include/txp_form.php +++ b/textpattern/include/txp_form.php @@ -310,7 +310,7 @@ function form_edit($message = '', $refresh_partials = false) $skin = $thisSkin->setName($skin)->setEditing(); if ($step == 'form_delete' || empty($name) && $step != 'form_create' && !$savenew) { - $name = get_pref('last_form_saved', 'default'); + $name = $instance->getEditing(); } elseif ((($copy || $savenew) && $newname) && !$save_error) { $name = $newname; } elseif ((($newname && ($newname != $name)) || $step === 'form_create') && !$save_error) { diff --git a/textpattern/include/txp_page.php b/textpattern/include/txp_page.php index d277586bc3..83e0866653 100644 --- a/textpattern/include/txp_page.php +++ b/textpattern/include/txp_page.php @@ -127,8 +127,6 @@ function page_edit($message = '', $refresh_partials = false) 'skin', )))); - $default_name = safe_field("page", 'txp_section', "name = 'default'"); - $name = Page::sanitize(assert_string(gps('name'))); $newname = Page::sanitize(assert_string(gps('newname'))); $skin = ($skin !== '') ? $skin : null; @@ -137,8 +135,10 @@ function page_edit($message = '', $refresh_partials = false) $thisSkin = Txp::get('Textpattern\Skin\Skin'); $skin = $thisSkin->setName($skin)->setEditing(); + $editing = $instance->getEditing(); + if ($step == 'page_delete' || empty($name) && $step != 'page_new' && !$savenew) { - $name = get_pref('last_page_saved', $default_name); + $name = $editing; } elseif ((($copy || $savenew) && $newname) && !$save_error) { $name = $newname; } elseif ((($newname && ($newname != $name)) || $step === 'page_new') && !$save_error) { @@ -183,7 +183,7 @@ function page_edit($message = '', $refresh_partials = false) $rs = array( 'name' => $name, 'newname' => $newname, - 'default' => $default_name, + 'default' => $editing, 'skin' => $skin, 'html' => $html, ); diff --git a/textpattern/vendors/Textpattern/Skin/AssetBase.php b/textpattern/vendors/Textpattern/Skin/AssetBase.php index b329c32327..850eb37342 100644 --- a/textpattern/vendors/Textpattern/Skin/AssetBase.php +++ b/textpattern/vendors/Textpattern/Skin/AssetBase.php @@ -307,15 +307,21 @@ protected function getFilePath($name = null) public function getEditing() { + global $prefs; + $editing = get_pref('last_'.$this->getEvent().'_saved', '', true); $installed = $this->getInstalled()[$this->getSkin()->getName()]; if (!$editing || !in_array($editing, $installed)) { - reset($installed); - $sliced = array_slice($installed, 0, 1); - $editing = array_shift($sliced); - $this->setEditing($editing); + $editing = static::getDefault(); + + if (!in_array($editing, $installed)) { + reset($installed); + + $sliced = array_slice($installed, 0, 1); + $editing = array_shift($sliced); + } } return $editing; @@ -344,7 +350,7 @@ public function setEditing($name = null) protected static function resetEditing() { - return $this->setEditing(self::getDefault()); + return $this->setEditing(static::getDefault()); } /** diff --git a/textpattern/vendors/Textpattern/Skin/Form.php b/textpattern/vendors/Textpattern/Skin/Form.php index 26e8cd6ccf..701922d7eb 100644 --- a/textpattern/vendors/Textpattern/Skin/Form.php +++ b/textpattern/vendors/Textpattern/Skin/Form.php @@ -102,5 +102,16 @@ public static function getTypes() { return static::$subdirValues; } + + /** + * Get the skin name used by the default section. + * + * @return mixed Skin name or FALSE on error. + */ + + protected function getDefault() + { + return 'default'; + } } } From 6e8eba2c4ee20b9dbbddbb66d7f9bccd6356594d Mon Sep 17 00:00:00 2001 From: Nicolas Morand Date: Thu, 1 Mar 2018 16:52:41 +0100 Subject: [PATCH 2/5] Removes useless variable setting --- textpattern/vendors/Textpattern/Skin/Skin.php | 1 - 1 file changed, 1 deletion(-) diff --git a/textpattern/vendors/Textpattern/Skin/Skin.php b/textpattern/vendors/Textpattern/Skin/Skin.php index d46f7ff410..2bee3f9a01 100644 --- a/textpattern/vendors/Textpattern/Skin/Skin.php +++ b/textpattern/vendors/Textpattern/Skin/Skin.php @@ -457,7 +457,6 @@ protected function setUploaded() if ($files) { foreach ($files as $file) { - $filePath = $file->getPath(); $name = basename($file->getPath()); if ($name === self::sanitize($name)) { From 3df3678c726e8034ea63c6311bfa3b018e163298 Mon Sep 17 00:00:00 2001 From: Nicolas Morand Date: Mon, 5 Mar 2018 10:49:02 +0100 Subject: [PATCH 3/5] Removes duplicated AND in queries --- textpattern/vendors/Textpattern/Skin/CommonBase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textpattern/vendors/Textpattern/Skin/CommonBase.php b/textpattern/vendors/Textpattern/Skin/CommonBase.php index 9e242a50d1..d01f7072bd 100644 --- a/textpattern/vendors/Textpattern/Skin/CommonBase.php +++ b/textpattern/vendors/Textpattern/Skin/CommonBase.php @@ -530,7 +530,7 @@ public function updateRow($set = null, $where = null, $debug = false) if ($skinName) { !$where or $where.= ' AND '; - $where .= " AND skin = '".doSlash($skinName)."'"; + $where .= "skin = '".doSlash($skinName)."'"; } } @@ -569,7 +569,7 @@ public function getField($thing = null, $where = null, $debug = false) if ($skinName) { !$where or $where.= ' AND '; - $where .= " AND skin = '".doSlash($skinName)."'"; + $where .= "skin = '".doSlash($skinName)."'"; } } From 115d659b1ebd0594706b3cfaea73ebcea6881b61 Mon Sep 17 00:00:00 2001 From: Nicolas Morand Date: Mon, 5 Mar 2018 12:10:03 +0100 Subject: [PATCH 4/5] Introduces `$fromDB` param in `getEditing()` --- textpattern/vendors/Textpattern/Skin/AssetBase.php | 10 ++++++---- textpattern/vendors/Textpattern/Skin/Skin.php | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/textpattern/vendors/Textpattern/Skin/AssetBase.php b/textpattern/vendors/Textpattern/Skin/AssetBase.php index 850eb37342..0dde7f1fa2 100644 --- a/textpattern/vendors/Textpattern/Skin/AssetBase.php +++ b/textpattern/vendors/Textpattern/Skin/AssetBase.php @@ -305,15 +305,17 @@ protected function getFilePath($name = null) * {@inheritdoc} */ - public function getEditing() + public function getEditing($fromDB = false) { global $prefs; - $editing = get_pref('last_'.$this->getEvent().'_saved', '', true); - $installed = $this->getInstalled()[$this->getSkin()->getName()]; + $editing = get_pref('last_'.$this->getEvent().'_saved', '', $fromDB); - if (!$editing || !in_array($editing, $installed)) { + if ($editing) { + $installed = $this->getInstalled()[$this->getSkin()->getName()]; + } + if (!$editing || !in_array($editing, $installed)) { $editing = static::getDefault(); if (!in_array($editing, $installed)) { diff --git a/textpattern/vendors/Textpattern/Skin/Skin.php b/textpattern/vendors/Textpattern/Skin/Skin.php index 2bee3f9a01..2b763d463e 100644 --- a/textpattern/vendors/Textpattern/Skin/Skin.php +++ b/textpattern/vendors/Textpattern/Skin/Skin.php @@ -309,9 +309,9 @@ public function updateSections($set = null, $where = null) * {@inheritdoc} */ - public function getEditing() + public function getEditing($fromDB = false) { - $editing = get_pref($this->getEvent().'_editing', '', true); + $editing = get_pref($this->getEvent().'_editing', '', $fromDB); if (!$editing) { $installed = $this->getInstalled(); From 5cdf0430a4afdeb1cdd28f6af3cbce903e98648d Mon Sep 17 00:00:00 2001 From: Nicolas Morand Date: Fri, 31 Aug 2018 17:31:28 +0200 Subject: [PATCH 5/5] braces-free namespaces --- .../vendors/Textpattern/Skin/AssetBase.php | 1123 ++++--- .../Textpattern/Skin/AssetInterface.php | 53 +- .../vendors/Textpattern/Skin/CommonBase.php | 1323 +++++---- .../Textpattern/Skin/CommonInterface.php | 343 ++- textpattern/vendors/Textpattern/Skin/Css.php | 57 +- .../vendors/Textpattern/Skin/CssInterface.php | 29 +- textpattern/vendors/Textpattern/Skin/Form.php | 147 +- .../Textpattern/Skin/FormInterface.php | 41 +- textpattern/vendors/Textpattern/Skin/Page.php | 51 +- .../Textpattern/Skin/PageInterface.php | 29 +- textpattern/vendors/Textpattern/Skin/Skin.php | 2593 ++++++++--------- .../Textpattern/Skin/SkinInterface.php | 249 +- 12 files changed, 3013 insertions(+), 3025 deletions(-) diff --git a/textpattern/vendors/Textpattern/Skin/AssetBase.php b/textpattern/vendors/Textpattern/Skin/AssetBase.php index 0dde7f1fa2..5d10c7f44e 100644 --- a/textpattern/vendors/Textpattern/Skin/AssetBase.php +++ b/textpattern/vendors/Textpattern/Skin/AssetBase.php @@ -30,720 +30,719 @@ * @package Skin */ -namespace Textpattern\Skin { - - abstract class AssetBase extends CommonBase implements AssetInterface +namespace Textpattern\Skin; + +abstract class AssetBase extends CommonBase implements AssetInterface +{ + /** + * Asset related directory. + * + * @var string Directory name. + * @see setDir(), getDir(). + */ + + protected static $dir; + + /** + * Asset related default subdirectory to store exported files. + * + * @var string Asset subdirectory name. + * @see getDefaultSubdir(). + */ + + protected static $defaultSubdir; + + /** + * Asset related table field used as subdirectories. + * + * @var string + * @see getSubdirField(). + */ + + protected static $subdirField; + + /** + * Asset related table field(s) used as asset file contents. + * + * @var string Field name (could accept an array in the future for JSON contents) + * @see getFileContentsField(). + */ + + protected static $fileContentsField; + + /** + * Asset related essential rows as an associative array of the following + * fields and their value: 'name', ($subdirField, ) $fileContentsField. + * + * @var array Associative array of the following fields and their value: + * 'name', ($subdirField, ) $fileContentsField. + * @see getEssential(). + */ + + protected static $essential = array(); + + /** + * Parent skin object. + * + * @var object skin + * @see __construct(). + */ + + protected $skin; + + /** + * Constructor. + */ + + public function __construct(Skin $skin = null) { - /** - * Asset related directory. - * - * @var string Directory name. - * @see setDir(), getDir(). - */ - - protected static $dir; - - /** - * Asset related default subdirectory to store exported files. - * - * @var string Asset subdirectory name. - * @see getDefaultSubdir(). - */ - - protected static $defaultSubdir; - - /** - * Asset related table field used as subdirectories. - * - * @var string - * @see getSubdirField(). - */ - - protected static $subdirField; - - /** - * Asset related table field(s) used as asset file contents. - * - * @var string Field name (could accept an array in the future for JSON contents) - * @see getFileContentsField(). - */ - - protected static $fileContentsField; - - /** - * Asset related essential rows as an associative array of the following - * fields and their value: 'name', ($subdirField, ) $fileContentsField. - * - * @var array Associative array of the following fields and their value: - * 'name', ($subdirField, ) $fileContentsField. - * @see getEssential(). - */ - - protected static $essential = array(); - - /** - * Parent skin object. - * - * @var object skin - * @see __construct(). - */ - - protected $skin; - - /** - * Constructor. - */ - - public function __construct(Skin $skin = null) - { - parent::__construct(); - - $this->setSkin($skin); - } + parent::__construct(); - /** - * {@inheritdoc} - */ + $this->setSkin($skin); + } - public function setSkin(Skin $skin = null) - { - $this->skin = $skin === null ? \Txp::get('Textpattern\Skin\Skin')->setName() : $skin; + /** + * {@inheritdoc} + */ - return $this; - } + public function setSkin(Skin $skin = null) + { + $this->skin = $skin === null ? \Txp::get('Textpattern\Skin\Skin')->setName() : $skin; - /** - * $skin property getter. - * - * @return $this->skin The asset related skin object. - */ + return $this; + } - protected function getSkin() - { - return $this->skin; - } + /** + * $skin property getter. + * + * @return $this->skin The asset related skin object. + */ - /** - * {@inheritdoc} - */ + protected function getSkin() + { + return $this->skin; + } - protected function getInfos($safe = false) - { - if ($safe) { - $infoQuery = array(); + /** + * {@inheritdoc} + */ - foreach ($this->infos as $col => $value) { - if ($col === self::getFileContentsField()) { - $infoQuery[] = $col." = '".$value."'"; - } else { - $infoQuery[] = $col." = '".doSlash($value)."'"; - } - } + protected function getInfos($safe = false) + { + if ($safe) { + $infoQuery = array(); - return implode(', ', $infoQuery); + foreach ($this->infos as $col => $value) { + if ($col === self::getFileContentsField()) { + $infoQuery[] = $col." = '".$value."'"; + } else { + $infoQuery[] = $col." = '".doSlash($value)."'"; + } } - return $this->infos; + return implode(', ', $infoQuery); } - /** - * $fileContentsField property getter. - * - * @return string static::$fileContentsField. - */ + return $this->infos; + } - protected static function getFileContentsField() - { - return static::$fileContentsField; - } + /** + * $fileContentsField property getter. + * + * @return string static::$fileContentsField. + */ - /** - * Get essential templates infos form the $essential property value. - * - * @param string $key $essential property key for whhich you want to get the value. - * @param string $whereKey $essential property key to check against the $valueIn value. - * @param array $valueIn Values to check against the $whereKey values. - * @return array $essential property value if $key is null, filtered infos otherwise. - */ - - public static function getEssential( - $key = null, - $whereKey = null, - $valueIn = null - ) { - if ($key === null) { - return static::$essential; - } elseif ($key === '*' && $whereKey) { - $keyValues = array(); - - foreach (static::$essential as $row) { - if (in_array($row[$whereKey], $valueIn)) { - $keyValues[] = $row; - } + protected static function getFileContentsField() + { + return static::$fileContentsField; + } + + /** + * Get essential templates infos form the $essential property value. + * + * @param string $key $essential property key for whhich you want to get the value. + * @param string $whereKey $essential property key to check against the $valueIn value. + * @param array $valueIn Values to check against the $whereKey values. + * @return array $essential property value if $key is null, filtered infos otherwise. + */ + + public static function getEssential( + $key = null, + $whereKey = null, + $valueIn = null + ) { + if ($key === null) { + return static::$essential; + } elseif ($key === '*' && $whereKey) { + $keyValues = array(); + + foreach (static::$essential as $row) { + if (in_array($row[$whereKey], $valueIn)) { + $keyValues[] = $row; } - } else { - $key !== null or $key = 'name'; - $keyValues = array(); + } + } else { + $key !== null or $key = 'name'; + $keyValues = array(); - foreach (static::$essential as $row) { - if ($whereKey) { - if (in_array($row[$whereKey], $valueIn)) { - $keyValues[] = $row[$key]; - } - } else { + foreach (static::$essential as $row) { + if ($whereKey) { + if (in_array($row[$whereKey], $valueIn)) { $keyValues[] = $row[$key]; } + } else { + $keyValues[] = $row[$key]; } } - - return $keyValues; } - /** - * $skin property setter. - * - * @return object $this The current class object (chainable). - */ + return $keyValues; + } - protected static function setDir($name) - { - static::$dir = $name; + /** + * $skin property setter. + * + * @return object $this The current class object (chainable). + */ - return $this; - } + protected static function setDir($name) + { + static::$dir = $name; - /** - * $dir property getter. - * - * @return string static::$dir. - */ + return $this; + } - protected static function getDir() - { - return static::$dir; - } + /** + * $dir property getter. + * + * @return string static::$dir. + */ - /** - * Gets the skin directory path. - * - * @return string path. - */ + protected static function getDir() + { + return static::$dir; + } - protected function getDirPath() - { - return $this->getSkin()->getSubdirPath().DS.static::getDir(); - } + /** + * Gets the skin directory path. + * + * @return string path. + */ - /** - * $subdirField property getter. - */ + protected function getDirPath() + { + return $this->getSkin()->getSubdirPath().DS.static::getDir(); + } - protected static function getSubdirField() - { - return static::$subdirField; - } + /** + * $subdirField property getter. + */ - /** - * $defaultSubdir property getter. - */ + protected static function getSubdirField() + { + return static::$subdirField; + } - protected static function getDefaultSubdir() - { - return static::$defaultSubdir; - } + /** + * $defaultSubdir property getter. + */ + + protected static function getDefaultSubdir() + { + return static::$defaultSubdir; + } - /** - * $defaultSubdir property getter. - */ + /** + * $defaultSubdir property getter. + */ - protected static function getSubdirValues() - { - return static::$subdirValues; - } + protected static function getSubdirValues() + { + return static::$subdirValues; + } - /** - * Whether a subdirectory name is valid or not. - * - * @param string $name Subdirectory name. - * @return string The subdirectory name if valid or the default subdirectory. - */ - - protected static function parseSubdir($name) - { - if (in_array($name, self::getSubdirValues())) { - return $name; - } else { - return self::getDefaultSubdir(); - } + /** + * Whether a subdirectory name is valid or not. + * + * @param string $name Subdirectory name. + * @return string The subdirectory name if valid or the default subdirectory. + */ + + protected static function parseSubdir($name) + { + if (in_array($name, self::getSubdirValues())) { + return $name; + } else { + return self::getDefaultSubdir(); } + } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - protected function getSubdirPath($name = null) - { - $name or $name = $this->getInfos()[self::getSubdirField()]; + protected function getSubdirPath($name = null) + { + $name or $name = $this->getInfos()[self::getSubdirField()]; - return $this->getDirPath().DS.$name; - } + return $this->getDirPath().DS.$name; + } - /** - * Get the template related file path. - * - * @param string path. - */ + /** + * Get the template related file path. + * + * @param string path. + */ - protected function getFilePath($name = null) - { - $dirPath = self::getSubdirField() ? $this->getSubdirPath($name) : $this->getDirPath(); + protected function getFilePath($name = null) + { + $dirPath = self::getSubdirField() ? $this->getSubdirPath($name) : $this->getDirPath(); - return $dirPath.DS.$this->getName().'.'.self::getExtension(); - } + return $dirPath.DS.$this->getName().'.'.self::getExtension(); + } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - public function getEditing($fromDB = false) - { - global $prefs; + public function getEditing($fromDB = false) + { + global $prefs; - $editing = get_pref('last_'.$this->getEvent().'_saved', '', $fromDB); + $editing = get_pref('last_'.$this->getEvent().'_saved', '', $fromDB); - if ($editing) { - $installed = $this->getInstalled()[$this->getSkin()->getName()]; - } + if ($editing) { + $installed = $this->getInstalled()[$this->getSkin()->getName()]; + } - if (!$editing || !in_array($editing, $installed)) { - $editing = static::getDefault(); + if (!$editing || !in_array($editing, $installed)) { + $editing = static::getDefault(); - if (!in_array($editing, $installed)) { - reset($installed); + if (!in_array($editing, $installed)) { + reset($installed); - $sliced = array_slice($installed, 0, 1); - $editing = array_shift($sliced); - } + $sliced = array_slice($installed, 0, 1); + $editing = array_shift($sliced); } - - return $editing; } - /** - * {@inheritdoc} - */ + return $editing; + } - public function setEditing($name = null) - { - global $prefs; + /** + * {@inheritdoc} + */ - $event = $this->getEvent(); - $pref = 'last_'.$event.'_saved'; - $name !== null or $name = $this->getName(); + public function setEditing($name = null) + { + global $prefs; - return set_pref($pref, $prefs[$pref] = $name, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); - } + $event = $this->getEvent(); + $pref = 'last_'.$event.'_saved'; + $name !== null or $name = $this->getName(); - /** - * Set the skin_editing pref to the skin used by the default section. - * - * @return bool FALSE on error. - */ + return set_pref($pref, $prefs[$pref] = $name, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); + } - protected static function resetEditing() - { - return $this->setEditing(static::getDefault()); - } + /** + * Set the skin_editing pref to the skin used by the default section. + * + * @return bool FALSE on error. + */ - /** - * {@inheritdoc} - */ + protected static function resetEditing() + { + return $this->setEditing(static::getDefault()); + } - protected function createFile($path = null, $contents = null) - { - if ($path === null || $contents === null) { - $infos = $this->getInfos(); - } + /** + * {@inheritdoc} + */ - if ($path === null) { - $subdirField = $this->getSubdirField(); - $file = $this->getName().'.'.self::getExtension(); + protected function createFile($path = null, $contents = null) + { + if ($path === null || $contents === null) { + $infos = $this->getInfos(); + } - if ($subdirField) { - $path = $infos[$subdirField].DS.$file; - } else { - $path = $file; - } - } + if ($path === null) { + $subdirField = $this->getSubdirField(); + $file = $this->getName().'.'.self::getExtension(); - if ($contents === null) { - $infos = $this->getInfos(); - $contents = $infos[self::getFileContentsField()]; + if ($subdirField) { + $path = $infos[$subdirField].DS.$file; + } else { + $path = $file; } - - return file_put_contents($this->getDirPath().DS.$path, $contents); } - /** - * {@inheritdoc} - */ + if ($contents === null) { + $infos = $this->getInfos(); + $contents = $infos[self::getFileContentsField()]; + } - public function createRows($rows = null) - { - $rows !== null or $rows = self::getEssential(); + return file_put_contents($this->getDirPath().DS.$path, $contents); + } - $skin = $this->getSkin()->getName(); - $fields = array('skin', 'name'); - $fileContentsField = self::getFileContentsField(); - $subdirField = self::getSubdirField(); - $values = array(); - $update = "skin=VALUES(skin), name=VALUES(name), "; + /** + * {@inheritdoc} + */ - if ($subdirField) { - $fields[] = $subdirField; - - foreach ($rows as $row) { - $values[] = "('".doSlash($skin)."', " - ."'".doSlash($row['name'])."', " - ."'".doSlash($row[$subdirField])."', " - ."'".doSlash($row[$fileContentsField])."')"; - } + public function createRows($rows = null) + { + $rows !== null or $rows = self::getEssential(); + + $skin = $this->getSkin()->getName(); + $fields = array('skin', 'name'); + $fileContentsField = self::getFileContentsField(); + $subdirField = self::getSubdirField(); + $values = array(); + $update = "skin=VALUES(skin), name=VALUES(name), "; + + if ($subdirField) { + $fields[] = $subdirField; + + foreach ($rows as $row) { + $values[] = "('".doSlash($skin)."', " + ."'".doSlash($row['name'])."', " + ."'".doSlash($row[$subdirField])."', " + ."'".doSlash($row[$fileContentsField])."')"; + } - $update .= $subdirField."=VALUES(".$subdirField."), "; - } else { - foreach ($rows as $row) { - $values[] = "('".doSlash($skin)."', " - ."'".doSlash($row['name'])."', " - ."'".doSlash($row[$fileContentsField])."')"; - } + $update .= $subdirField."=VALUES(".$subdirField."), "; + } else { + foreach ($rows as $row) { + $values[] = "('".doSlash($skin)."', " + ."'".doSlash($row['name'])."', " + ."'".doSlash($row[$fileContentsField])."')"; } + } - $fields[] = $fileContentsField; - $update .= $fileContentsField."=VALUES(".$fileContentsField.")"; + $fields[] = $fileContentsField; + $update .= $fileContentsField."=VALUES(".$fileContentsField.")"; - return safe_query( - "INSERT INTO ".safe_pfx($this->getTable())." (".implode(', ', $fields).") " - ."VALUES ".implode(', ', $values) - ." ON DUPLICATE KEY UPDATE ".$update - ); - } + return safe_query( + "INSERT INTO ".safe_pfx($this->getTable())." (".implode(', ', $fields).") " + ."VALUES ".implode(', ', $values) + ." ON DUPLICATE KEY UPDATE ".$update + ); + } - /** - * Delete obsolete template rows. - * - * @return bool FALSE on error. - */ - - protected function deleteExtraRows() - { - return $this->deleteRows( - "skin = '".doSlash($this->getSkin()->getName())."' AND " - ."name NOT IN ('".implode("', '", array_map('doSlash', $this->getNames()))."')" - ); - } + /** + * Delete obsolete template rows. + * + * @return bool FALSE on error. + */ - /** - * {@inheritdoc} - */ + protected function deleteExtraRows() + { + return $this->deleteRows( + "skin = '".doSlash($this->getSkin()->getName())."' AND " + ."name NOT IN ('".implode("', '", array_map('doSlash', $this->getNames()))."')" + ); + } - protected function parseFiles($files) - { - $rows = $row = array(); - $subdirField = self::getSubdirField(); - $event = $this->getEvent(); + /** + * {@inheritdoc} + */ - $parsed = $parsedFiles = $names = array(); + protected function parseFiles($files) + { + $rows = $row = array(); + $subdirField = self::getSubdirField(); + $event = $this->getEvent(); - if ($files) { - $Skin = $this->getSkin(); - $skin = $Skin->getName(); + $parsed = $parsedFiles = $names = array(); - foreach ($files as $file) { - $filename = $file->getFilename(); - $name = pathinfo($filename, PATHINFO_FILENAME); + if ($files) { + $Skin = $this->getSkin(); + $skin = $Skin->getName(); - if ($subdirField) { - $essentialSubdir = implode('', $this->getEssential($subdirField, 'name', array($name))); - } + foreach ($files as $file) { + $filename = $file->getFilename(); + $name = pathinfo($filename, PATHINFO_FILENAME); - if (in_array($filename, $parsedFiles)) { - $this->mergeResult($event.'_duplicate', array($skin => array($filename))); - } elseif ($subdirField && $essentialSubdir && $essentialSubdir !== basename($file->getPath())) { - $this->mergeResult($event.'_subdir_error', array($skin => array(basename($file->getPath()).'/'.$name))); - } else { - $names[] = $name; - $parsed[] = $row['name'] = $name; - $parsedFiles[] = $filename; + if ($subdirField) { + $essentialSubdir = implode('', $this->getEssential($subdirField, 'name', array($name))); + } - if ($subdirField) { - $subdir = basename($file->getPath()); - $subdirValid = self::parseSubdir($subdir); + if (in_array($filename, $parsedFiles)) { + $this->mergeResult($event.'_duplicate', array($skin => array($filename))); + } elseif ($subdirField && $essentialSubdir && $essentialSubdir !== basename($file->getPath())) { + $this->mergeResult($event.'_subdir_error', array($skin => array(basename($file->getPath()).'/'.$name))); + } else { + $names[] = $name; + $parsed[] = $row['name'] = $name; + $parsedFiles[] = $filename; - if ($subdir !== $subdirValid) { - $this->mergeResult($event.'_subdir_invalid', array($skin => array($subdir.'/'.$name))); - } + if ($subdirField) { + $subdir = basename($file->getPath()); + $subdirValid = self::parseSubdir($subdir); - $row[$subdirField] = $subdirValid; + if ($subdir !== $subdirValid) { + $this->mergeResult($event.'_subdir_invalid', array($skin => array($subdir.'/'.$name))); } - $row[self::getFileContentsField()] = $file->getContents(); - - $rows[] = $row; + $row[$subdirField] = $subdirValid; } + + $row[self::getFileContentsField()] = $file->getContents(); + + $rows[] = $row; } } + } - $missingNames = array_diff(self::getEssential('name'), $parsed); + $missingNames = array_diff(self::getEssential('name'), $parsed); - $this->setNames(array_merge($names, $missingNames)); + $this->setNames(array_merge($names, $missingNames)); - $missingRows = self::getEssential('*', 'name', $missingNames); + $missingRows = self::getEssential('*', 'name', $missingNames); - return array_merge($rows, $missingRows); - } + return array_merge($rows, $missingRows); + } - /** - * Unlink obsolete template files. - * - * @param array $not An array of template names to NOT unlink; - * @return array !Templates for which the unlink process FAILED!; - */ - - public function deleteExtraFiles($nameNotIn = null) - { - $filenames = array(); - $extension = self::getExtension(); - $hasSubdir = self::getSubdirField(); - - foreach ($this->getNames() as $name) { - $filenames[] = $name.'.'.$extension; - } + /** + * Unlink obsolete template files. + * + * @param array $not An array of template names to NOT unlink; + * @return array !Templates for which the unlink process FAILED!; + */ - $files = $this->getFiles($filenames, $hasSubdir ? 1 : 0); + public function deleteExtraFiles($nameNotIn = null) + { + $filenames = array(); + $extension = self::getExtension(); + $hasSubdir = self::getSubdirField(); - if ($files) { - $notRemoved = $subdirPaths = array(); + foreach ($this->getNames() as $name) { + $filenames[] = $name.'.'.$extension; + } - foreach ($files as $file) { - $name = pathinfo($file->getFilename(), PATHINFO_FILENAME); + $files = $this->getFiles($filenames, $hasSubdir ? 1 : 0); - $this->setName($name); + if ($files) { + $notRemoved = $subdirPaths = array(); - if (!$nameNotIn || ($nameNotIn && !in_array($name, $nameNotIn))) { - unlink($file->getPathname()) or $notRemoved[] = $name; + foreach ($files as $file) { + $name = pathinfo($file->getFilename(), PATHINFO_FILENAME); - !$hasSubdir or $subdirPaths[] = $file->getPath(); - } + $this->setName($name); + + if (!$nameNotIn || ($nameNotIn && !in_array($name, $nameNotIn))) { + unlink($file->getPathname()) or $notRemoved[] = $name; + + !$hasSubdir or $subdirPaths[] = $file->getPath(); } + } - if (!$notRemoved) { - if ($hasSubdir) { - foreach ($subdirPaths as $subdirPath) { - if (self::isDirEmpty($subdirPath) && !@rmdir($subdirPath)) { - $notRemoved[] = $subdirPath; - } + if (!$notRemoved) { + if ($hasSubdir) { + foreach ($subdirPaths as $subdirPath) { + if (self::isDirEmpty($subdirPath) && !@rmdir($subdirPath)) { + $notRemoved[] = $subdirPath; } } + } - $dirPath = $this->getDirPath(); + $dirPath = $this->getDirPath(); - if (self::isDirEmpty($dirPath) && !@rmdir($dirPath)) { - $notRemoved[] = $dirPath; - } + if (self::isDirEmpty($dirPath) && !@rmdir($dirPath)) { + $notRemoved[] = $dirPath; } } - - return $notRemoved; } - /** - * {@inheritdoc} - */ - - public function import($sync = false, $override = false) - { - $event = $this->getEvent(); - $dirPath = $this->getDirPath(); - $Skin = $this->getSkin(); - $skin = $Skin !== null ? $Skin->getName() : $this->getSkin()->getEditing(); - $names = $this->getNames(); - $callbackExtra = compact('skin', 'names'); - $done = array(); - $dirIsReadable = is_readable($dirPath); - - callback_event('txp.'.$event, 'import', 1, $callbackExtra); - - if (!$dirIsReadable) { - $this->mergeResult('path_not_readable', array($skin => array($dirPath))); - } - - if ($dirIsReadable || !$override) { - if ($dirIsReadable) { - $filenames = array(); - $extension = self::getExtension(); + return $notRemoved; + } - foreach ($this->getNames() as $name) { - $filenames[] = $name.'.'.$extension; - } + /** + * {@inheritdoc} + */ - $files = $this->getFiles($filenames, self::getSubdirField() ? 1 : 0); + public function import($sync = false, $override = false) + { + $event = $this->getEvent(); + $dirPath = $this->getDirPath(); + $Skin = $this->getSkin(); + $skin = $Skin !== null ? $Skin->getName() : $this->getSkin()->getEditing(); + $names = $this->getNames(); + $callbackExtra = compact('skin', 'names'); + $done = array(); + $dirIsReadable = is_readable($dirPath); + + callback_event('txp.'.$event, 'import', 1, $callbackExtra); + + if (!$dirIsReadable) { + $this->mergeResult('path_not_readable', array($skin => array($dirPath))); + } - if (!$files) { - $this->mergeResult($event.'_not_found', array($skin => array($dirPath))); - } + if ($dirIsReadable || !$override) { + if ($dirIsReadable) { + $filenames = array(); + $extension = self::getExtension(); - $rows = $this->parseFiles($files); - } else { - $rows = self::getEssential(); + foreach ($this->getNames() as $name) { + $filenames[] = $name.'.'.$extension; } - if (!$this->createRows($rows)) { - $this->mergeResult($event.'_import_failed', array($skin => $names)); - } else { - $done[] = $names; + $files = $this->getFiles($filenames, self::getSubdirField() ? 1 : 0); - $this->mergeResult($event.'_imported', array($skin => $names), 'success'); + if (!$files) { + $this->mergeResult($event.'_not_found', array($skin => array($dirPath))); } - // Drops extra rows… - if ($sync) { - if (!$this->deleteExtraRows()) { - $this->mergeResult($event.'_files_deletion_failed', array($skin => $notCleaned)); - } - } + $rows = $this->parseFiles($files); + } else { + $rows = self::getEssential(); } - callback_event('txp.'.$event, 'import', 0, $callbackExtra + compact('done')); + if (!$this->createRows($rows)) { + $this->mergeResult($event.'_import_failed', array($skin => $names)); + } else { + $done[] = $names; - return $this; + $this->mergeResult($event.'_imported', array($skin => $names), 'success'); + } + + // Drops extra rows… + if ($sync) { + if (!$this->deleteExtraRows()) { + $this->mergeResult($event.'_files_deletion_failed', array($skin => $notCleaned)); + } + } } - /** - * {@inheritdoc} - */ + callback_event('txp.'.$event, 'import', 0, $callbackExtra + compact('done')); - public function export($sync = false, $override = false) - { - $event = $this->getEvent(); - $dirPath = $this->getDirPath(); - $Skin = $this->getSkin(); - $skin = $Skin !== null ? $Skin->getName() : $this->getSkin()->getEditing(); - $names = $this->getNames(); - $callbackExtra = compact('skin', 'names'); - $done = array(); + return $this; + } - callback_event('txp.'.$event, 'export', 1, $callbackExtra); + /** + * {@inheritdoc} + */ - if (!is_writable($dirPath) && !@mkdir($dirPath)) { - $this->mergeResult('path_not_writable', array($skin => array($dirPath))); + public function export($sync = false, $override = false) + { + $event = $this->getEvent(); + $dirPath = $this->getDirPath(); + $Skin = $this->getSkin(); + $skin = $Skin !== null ? $Skin->getName() : $this->getSkin()->getEditing(); + $names = $this->getNames(); + $callbackExtra = compact('skin', 'names'); + $done = array(); + + callback_event('txp.'.$event, 'export', 1, $callbackExtra); + + if (!is_writable($dirPath) && !@mkdir($dirPath)) { + $this->mergeResult('path_not_writable', array($skin => array($dirPath))); + } else { + $rows = $this->getRows(); + + if (!$rows) { + $this->mergeResult($event.'_not_found', $skin); } else { - $rows = $this->getRows(); - - if (!$rows) { - $this->mergeResult($event.'_not_found', $skin); - } else { - foreach ($rows as $row) { - extract($row); + foreach ($rows as $row) { + extract($row); - if (!$this->setName($name)->isInstalled()) { - $this->mergeResult($event.'_unknown', array($skin => array($name))); - } elseif (!self::isExportable()) { - $this->mergeResult($event.'_name_unsafe', array($skin => array($name))); - } else { - $ready = true; - $subdirField = self::getSubdirField(); - $contentsField = self::getFileContentsField(); + if (!$this->setName($name)->isInstalled()) { + $this->mergeResult($event.'_unknown', array($skin => array($name))); + } elseif (!self::isExportable()) { + $this->mergeResult($event.'_name_unsafe', array($skin => array($name))); + } else { + $ready = true; + $subdirField = self::getSubdirField(); + $contentsField = self::getFileContentsField(); - if ($subdirField) { - $subdirPath = $this->setInfos($name, $$subdirField, $$contentsField)->getSubdirPath(); + if ($subdirField) { + $subdirPath = $this->setInfos($name, $$subdirField, $$contentsField)->getSubdirPath(); - if (!is_dir($subdirPath) && !@mkdir($subdirPath)) { - $this->mergeResult($event.'_not_writable', array($skin => array($name))); - $ready = false; - } - } else { - $this->setInfos($name, $$contentsField); + if (!is_dir($subdirPath) && !@mkdir($subdirPath)) { + $this->mergeResult($event.'_not_writable', array($skin => array($name))); + $ready = false; } + } else { + $this->setInfos($name, $$contentsField); + } - if ($ready) { - if ($this->createFile() === false) { - $this->mergeResult($event.'_export_failed', array($skin => array($name))); - } else { - $this->mergeResult($event.'_exported', array($skin => array($name)), 'success'); + if ($ready) { + if ($this->createFile() === false) { + $this->mergeResult($event.'_export_failed', array($skin => array($name))); + } else { + $this->mergeResult($event.'_exported', array($skin => array($name)), 'success'); - $done[] = $name; - } + $done[] = $name; } + } - // Drops extra files… - if ($sync && isset($done)) { - $notUnlinked = $this->deleteExtraFiles($done); + // Drops extra files… + if ($sync && isset($done)) { + $notUnlinked = $this->deleteExtraFiles($done); - if ($notUnlinked) { - $this->mergeResult($event.'_files_deletion_failed', array($skin => $notUnlinked)); - } + if ($notUnlinked) { + $this->mergeResult($event.'_files_deletion_failed', array($skin => $notUnlinked)); } } } } } - - callback_event('txp.'.$event, 'export', 0, $callbackExtra + compact('done')); - - return $this; } - /** - * {@inheritdoc} - */ + callback_event('txp.'.$event, 'export', 0, $callbackExtra + compact('done')); - public function getSelectEdit() - { - $event = $this->getEvent(); - $Skin = $this->getSkin(); - $skins = $Skin->getInstalled(); - - if (count($skins) > 1) { - return form( - inputLabel( - 'skin', - selectInput('skin', $skins, $Skin->getEditing(), false, 1, 'skin'), - 'skin' - ) - .eInput($event) - .sInput($event.'_skin_change'), - '', - '', - 'post' - ); - } + return $this; + } - return; + /** + * {@inheritdoc} + */ + + public function getSelectEdit() + { + $event = $this->getEvent(); + $Skin = $this->getSkin(); + $skins = $Skin->getInstalled(); + + if (count($skins) > 1) { + return form( + inputLabel( + 'skin', + selectInput('skin', $skins, $Skin->getEditing(), false, 1, 'skin'), + 'skin' + ) + .eInput($event) + .sInput($event.'_skin_change'), + '', + '', + 'post' + ); } - /** - * Select the asset related skin to edit. - * Keeps track from panel to panel. - * - * @param string $skin Optional skin name. Read from GET/POST otherwise - * @return object $this The current class object (chainable). - */ - - public function selectEdit($skin = null) - { - if ($skin === null) { - $skin = gps('skin'); - } + return; + } - if ($skin) { - $Skin = $this->getSkin(); - $Skin->setEditing($skin); - $Skin->setName($skin); - } + /** + * Select the asset related skin to edit. + * Keeps track from panel to panel. + * + * @param string $skin Optional skin name. Read from GET/POST otherwise + * @return object $this The current class object (chainable). + */ - $this->getEditing(); + public function selectEdit($skin = null) + { + if ($skin === null) { + $skin = gps('skin'); + } - return $this; + if ($skin) { + $Skin = $this->getSkin(); + $Skin->setEditing($skin); + $Skin->setName($skin); } + + $this->getEditing(); + + return $this; } } diff --git a/textpattern/vendors/Textpattern/Skin/AssetInterface.php b/textpattern/vendors/Textpattern/Skin/AssetInterface.php index e51258cddb..0016931572 100644 --- a/textpattern/vendors/Textpattern/Skin/AssetInterface.php +++ b/textpattern/vendors/Textpattern/Skin/AssetInterface.php @@ -30,37 +30,36 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - interface AssetInterface - { - /** - * $skin property setter. - */ +interface AssetInterface +{ + /** + * $skin property setter. + */ - public function setSkin(Skin $skin = null); + public function setSkin(Skin $skin = null); - /** - * $essential property getter. - * - * @param string $key $essential property array key for which you want to get values. - * @param string $whereKey Array key used to filter the output with $valueIn. - * @param array $valueIn Values to check against the $whereKey values to filter the output. - * @return array Filtered values. - */ + /** + * $essential property getter. + * + * @param string $key $essential property array key for which you want to get values. + * @param string $whereKey Array key used to filter the output with $valueIn. + * @param array $valueIn Values to check against the $whereKey values to filter the output. + * @return array Filtered values. + */ - public static function getEssential( - $key = null, - $whereKey = null, - $valueIn = null - ); + public static function getEssential( + $key = null, + $whereKey = null, + $valueIn = null + ); - /** - * Build the Skin switch form. - * - * @return HTML Auto submited select list of installed skins. - */ + /** + * Build the Skin switch form. + * + * @return HTML Auto submited select list of installed skins. + */ - public function getSelectEdit(); - } + public function getSelectEdit(); } diff --git a/textpattern/vendors/Textpattern/Skin/CommonBase.php b/textpattern/vendors/Textpattern/Skin/CommonBase.php index d01f7072bd..35284e9829 100644 --- a/textpattern/vendors/Textpattern/Skin/CommonBase.php +++ b/textpattern/vendors/Textpattern/Skin/CommonBase.php @@ -30,794 +30,793 @@ * @package Skin */ -namespace Textpattern\Skin { - - abstract class CommonBase implements CommonInterface - { - /** - * Class related textpack string (usually the event name). - * - * @var string 'skin', 'page', 'form', 'css', etc. - * @see getEvent(). - */ - - protected $event; - - /** - * Skin/templates directory/files name(s) pattern. - * - * @var string Regex without delimiters. - * @see getNamePattern(). - */ - - protected static $namePattern = '[a-zA-Z0-9_\-\.]{0,63}'; - - /** - * Installed. - * - * @var array Associative array of skin names and their titles. - * @see setUploaded(), getUploaded(). - */ - - protected $installed; - - /** - * Class related skin/template names to work with. - * - * @var array Names. - * @see setNames(), getNames(). - */ - - protected $names; - - /** - * Class related file extension. - * - * @see getExtension(). - */ - - protected static $extension = 'txp'; - - /** - * Skin/template name to work with. - * - * @var string Name. - * @see setName(), getName(). - */ - - protected $name; - - /** - * Skin/template related infos. - * - * @var array Associative array of class related table main fields and their values. - * @see setInfos(), getInfos(). - */ - - protected $infos; - - /** - * Skin/template name used as the base for update or duplication. - * - * @var string Name. - * @see setBase(), getBase(). - */ - - protected $base; - - /** - * Storage for admin related method results. - * - * @var array Associative array of 'success', 'warning' and 'error' - * textpack related items and their related '{list}' parameters. - * @see mergeResult(), getResults(), getMessage(). - */ - - protected $results = array( - 'success' => array(), - 'warning' => array(), - 'error' => array(), - ); - - /** - * Constructor - */ +namespace Textpattern\Skin; + +abstract class CommonBase implements CommonInterface +{ + /** + * Class related textpack string (usually the event name). + * + * @var string 'skin', 'page', 'form', 'css', etc. + * @see getEvent(). + */ + + protected $event; + + /** + * Skin/templates directory/files name(s) pattern. + * + * @var string Regex without delimiters. + * @see getNamePattern(). + */ + + protected static $namePattern = '[a-zA-Z0-9_\-\.]{0,63}'; + + /** + * Installed. + * + * @var array Associative array of skin names and their titles. + * @see setUploaded(), getUploaded(). + */ + + protected $installed; + + /** + * Class related skin/template names to work with. + * + * @var array Names. + * @see setNames(), getNames(). + */ + + protected $names; + + /** + * Class related file extension. + * + * @see getExtension(). + */ + + protected static $extension = 'txp'; + + /** + * Skin/template name to work with. + * + * @var string Name. + * @see setName(), getName(). + */ + + protected $name; + + /** + * Skin/template related infos. + * + * @var array Associative array of class related table main fields and their values. + * @see setInfos(), getInfos(). + */ + + protected $infos; + + /** + * Skin/template name used as the base for update or duplication. + * + * @var string Name. + * @see setBase(), getBase(). + */ + + protected $base; + + /** + * Storage for admin related method results. + * + * @var array Associative array of 'success', 'warning' and 'error' + * textpack related items and their related '{list}' parameters. + * @see mergeResult(), getResults(), getMessage(). + */ + + protected $results = array( + 'success' => array(), + 'warning' => array(), + 'error' => array(), + ); + + /** + * Constructor + */ + + public function __construct() + { + $this->setEvent(); + } - public function __construct() - { - $this->setEvent(); - } + /** + * Get the class related Database table name + * + * @return string Table name. + */ - /** - * Get the class related Database table name - * - * @return string Table name. - */ + protected function getTable() + { + return 'txp_'.$this->getEvent(); + } - protected function getTable() - { - return 'txp_'.$this->getEvent(); - } + /** + * $event property setter. + * + * @return $this The current object (chainable). + */ - /** - * $event property setter. - * - * @return $this The current object (chainable). - */ + protected function setEvent() + { + $this->event = strtolower((new \ReflectionClass($this))->getShortName()); - protected function setEvent() - { - $this->event = strtolower((new \ReflectionClass($this))->getShortName()); + return $this; + } - return $this; - } + /** + * $event property getter. + * + * @return string $this->event Class related textpack string (usually the event name). + */ - /** - * $event property getter. - * - * @return string $this->event Class related textpack string (usually the event name). - */ + public function getEvent() + { + return $this->event; + } - public function getEvent() - { - return $this->event; - } + /** + * $namePattern property getter + * + * @return string self::$namePattern Skin/templates directory/files name(s) pattern. + */ - /** - * $namePattern property getter - * - * @return string self::$namePattern Skin/templates directory/files name(s) pattern. - */ + protected static function getNamePattern() + { + return self::$namePattern; + } - protected static function getNamePattern() - { - return self::$namePattern; - } + /** + * Whether a $name property related value is a valid directory name or not. + * + * @return bool FALSE on error. + */ - /** - * Whether a $name property related value is a valid directory name or not. - * - * @return bool FALSE on error. - */ + protected function isExportable($name = null) + { + return preg_match('#^'.self::getNamePattern().'$#', $this->getName()); + } - protected function isExportable($name = null) - { - return preg_match('#^'.self::getNamePattern().'$#', $this->getName()); - } + /** + * Sanitizes a string for use in a theme template's name. + * + * Just runs sanitizeForPage() followed by sanitizeForFile(), + * then limits the number of characters to 63. + * + * @param string $text The string + * @return string + */ + + public static function sanitize($text) + { + $out = sanitizeForFile(sanitizeForPage($text)); - /** - * Sanitizes a string for use in a theme template's name. - * - * Just runs sanitizeForPage() followed by sanitizeForFile(), - * then limits the number of characters to 63. - * - * @param string $text The string - * @return string - */ - - public static function sanitize($text) - { - $out = sanitizeForFile(sanitizeForPage($text)); - - return \Txp::get('\Textpattern\Type\StringType', $out)->substring(0, 63)->getString(); - } + return \Txp::get('\Textpattern\Type\StringType', $out)->substring(0, 63)->getString(); + } - /** - * $names property setter/sanitizer. - * - * @param array $names Multiple skin or template names to work with related methods. - * @return object $this The current class object (chainable). - */ - - public function setNames($names = null) - { - if ($names === null) { - $this->names = array(); - } else { - $parsed = array(); + /** + * $names property setter/sanitizer. + * + * @param array $names Multiple skin or template names to work with related methods. + * @return object $this The current class object (chainable). + */ - foreach ($names as $name) { - $parsed[] = static::sanitize($name); - } + public function setNames($names = null) + { + if ($names === null) { + $this->names = array(); + } else { + $parsed = array(); - $this->names = $parsed; + foreach ($names as $name) { + $parsed[] = static::sanitize($name); } - return $this; + $this->names = $parsed; } - /** - * $names property getter. - * - * @return array Skin or template sanitized names. - */ + return $this; + } - protected function getNames() - { - return $this->names; - } + /** + * $names property getter. + * + * @return array Skin or template sanitized names. + */ - /** - * $name property setter. - * - * @param array $name Single skin or template name to work with related methods. - * Takes the '_last_saved' or '_editing' related preference value if null. - * @return object $this The current class object (chainable). - */ + protected function getNames() + { + return $this->names; + } - public function setName($name = null) - { - $this->name = $name === null ? $this->getEditing() : static::sanitize($name); + /** + * $name property setter. + * + * @param array $name Single skin or template name to work with related methods. + * Takes the '_last_saved' or '_editing' related preference value if null. + * @return object $this The current class object (chainable). + */ - return $this; - } + public function setName($name = null) + { + $this->name = $name === null ? $this->getEditing() : static::sanitize($name); - /** - * $name property getter. - * - * @return string Sanitized skin or template name. - */ + return $this; + } - protected function getName() - { - return $this->name; - } + /** + * $name property getter. + * + * @return string Sanitized skin or template name. + */ - /** - * $infos property getter/parser. - * - * @param bool $safe Whether to get the property value - * as a safe SET query clause. - * @return mixed The $infos property value or the related SET clause. - */ - - protected function getInfos($safe = false) - { - if ($safe) { - $infoQuery = array(); - - foreach ($this->infos as $col => $value) { - $infoQuery[] = $col." = '".doSlash($value)."'"; - } + protected function getName() + { + return $this->name; + } - return implode(', ', $infoQuery); + /** + * $infos property getter/parser. + * + * @param bool $safe Whether to get the property value + * as a safe SET query clause. + * @return mixed The $infos property value or the related SET clause. + */ + + protected function getInfos($safe = false) + { + if ($safe) { + $infoQuery = array(); + + foreach ($this->infos as $col => $value) { + $infoQuery[] = $col." = '".doSlash($value)."'"; } - return $this->infos; + return implode(', ', $infoQuery); } - /** - * $base property setter. - * - * @param object $this The current object (chainable). - */ + return $this->infos; + } - public function setBase($name) - { - $this->base = static::sanitize($name); + /** + * $base property setter. + * + * @param object $this The current object (chainable). + */ - return $this; - } + public function setBase($name) + { + $this->base = static::sanitize($name); - /** - * $base property getter. - * - * @return string Sanitized skin or template base name. - */ + return $this; + } - protected function getBase() - { - return $this->base; - } + /** + * $base property getter. + * + * @return string Sanitized skin or template base name. + */ - /** - * Get the 'synchronize' preference value. - * - * @return bool - */ + protected function getBase() + { + return $this->base; + } - protected function getSyncPref() - { - global $prefs; + /** + * Get the 'synchronize' preference value. + * + * @return bool + */ - $pref = 'synchronize'; - $value = get_pref($pref, true); + protected function getSyncPref() + { + global $prefs; - if (!isset($prefs[$pref])) { - $prefs[$pref] = $value; - } + $pref = 'synchronize'; + $value = get_pref($pref, true); - return $value; + if (!isset($prefs[$pref])) { + $prefs[$pref] = $value; } - /** - * Switch the 'synchronize' preference value - * and its related global variable. - * - * @return bool FALSE on error. - */ - - protected function switchSyncPref() - { - global $prefs; - - $name = 'synchronize'; - - return set_pref( - $name, - $prefs[$name] = !$prefs[$name], - 'skin', - PREF_HIDDEN, - 'text_input', - 0, - PREF_PRIVATE - ); - } + return $value; + } - /** - * Merge a result into the $results property array. - * - * @param string $txtItem Textpack related item. - * @param mixed $list A name or an array of names associated with the result - * to build the txtItem related '{list}'. - * List values can be grouped like so: array($skin => $templates) - * @param string $status 'success'|'warning'|'error'. - * @return object $this The current class object (chainable). - */ - - protected function mergeResult($txtItem, $list, $status = null) - { - !is_string($list) or $list = array($list); - $status = in_array($status, array('success', 'warning', 'error')) ? $status : 'error'; - - $this->results = array_merge_recursive( - $this->getResults(), - array($status => array($txtItem => $list)) - ); - - return $this; - } + /** + * Switch the 'synchronize' preference value + * and its related global variable. + * + * @return bool FALSE on error. + */ - /** - * $results property getter. - * - * @param array $status Array of results related status ('success', 'warning', 'error') to filter the outpout. - * @return array Associative array of status textpack related items - * and their related '{list}' parameters. - */ - - protected function getResults($status = null) - { - if ($status === null) { - return $this->results; - } else { - $results = array(); + protected function switchSyncPref() + { + global $prefs; + + $name = 'synchronize'; + + return set_pref( + $name, + $prefs[$name] = !$prefs[$name], + 'skin', + PREF_HIDDEN, + 'text_input', + 0, + PREF_PRIVATE + ); + } - foreach ($status as $severity) { - $results[$severity] = $this->results[$severity]; - } + /** + * Merge a result into the $results property array. + * + * @param string $txtItem Textpack related item. + * @param mixed $list A name or an array of names associated with the result + * to build the txtItem related '{list}'. + * List values can be grouped like so: array($skin => $templates) + * @param string $status 'success'|'warning'|'error'. + * @return object $this The current class object (chainable). + */ + + protected function mergeResult($txtItem, $list, $status = null) + { + !is_string($list) or $list = array($list); + $status = in_array($status, array('success', 'warning', 'error')) ? $status : 'error'; - return $results; - } - } + $this->results = array_merge_recursive( + $this->getResults(), + array($status => array($txtItem => $list)) + ); - /** - * Get the $results property value as a message to display in the admin tabs. - * - * @return mixed Message or array containing the message - * and its related user notice constant. - */ - - public function getMessage() - { - $message = array(); - - $thisResults = $this->getResults(); - - foreach ($this->getResults() as $status => $results) { - foreach ($results as $txtItem => $listGroup) { - $list = array(); - - if (isset($listGroup[0])) { - $list = $listGroup; - } else { - foreach ($listGroup as $group => $names) { - if (count($listGroup) > 1) { - $list[] = '('.$group.') '.implode(', ', $names); - } else { - $list[] = implode(', ', $names); - } - } - } + return $this; + } - $message[] = gTxt($txtItem, array('{list}' => implode(', ', $list))); - } - } + /** + * $results property getter. + * + * @param array $status Array of results related status ('success', 'warning', 'error') to filter the outpout. + * @return array Associative array of status textpack related items + * and their related '{list}' parameters. + */ - $message = implode('
', $message); + protected function getResults($status = null) + { + if ($status === null) { + return $this->results; + } else { + $results = array(); - if ($thisResults['success'] && ($thisResults['warning'] || $thisResults['error'])) { - $severity = 'E_WARNING'; - } elseif ($thisResults['warning']) { - $severity = 'E_WARNING'; - } elseif ($thisResults['error']) { - $severity = 'E_ERROR'; - } else { - $severity = ''; + foreach ($status as $severity) { + $results[$severity] = $this->results[$severity]; } - return $severity ? array($message, constant($severity)) : $message; + return $results; } + } - /** - * $extension property getter. - * - * @return string static::$extension. - */ + /** + * Get the $results property value as a message to display in the admin tabs. + * + * @return mixed Message or array containing the message + * and its related user notice constant. + */ - protected static function getExtension() - { - return static::$extension; - } + public function getMessage() + { + $message = array(); - /** - * Get files from the $dir property value related directory. - * - * @param array $names Optional filenames to filter the result. - * @param int $maxDepth Optional RecursiveIteratorIterator related property value (default = -1 infinite). - * @return object Collection of file objects. - */ - - protected function getFiles($names = null, $maxDepth = null) - { - $filter = $names ? $names : '#^'.self::getNamePattern().'.'.self::getExtension().'$#'; - $files = \Txp::get('Textpattern\Iterator\RecDirIterator', $this->getDirPath()); - $filter = \Txp::get('Textpattern\Iterator\RecFilterIterator', $files, $filter); - $filteredFiles = \Txp::get('Textpattern\Iterator\RecIteratorIterator', $filter); - $maxDepth !== null or $filteredFiles->setMaxDepth($maxDepth); - - return $filteredFiles; - } + $thisResults = $this->getResults(); + + foreach ($this->getResults() as $status => $results) { + foreach ($results as $txtItem => $listGroup) { + $list = array(); - /** - * Insert a row into the $table property value related table. - * - * @param string $set Optional SET clause. - * Builds the clause from the $infos (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool FALSE on error. - */ - - public function createRow($set = null, $debug = false) - { - if ($set === null) { - $set = $this->getInfos(true); - - if (property_exists($this, 'skin')) { - $set .= " skin = '".doSlash($this->getSkin()->getName())."'"; + if (isset($listGroup[0])) { + $list = $listGroup; + } else { + foreach ($listGroup as $group => $names) { + if (count($listGroup) > 1) { + $list[] = '('.$group.') '.implode(', ', $names); + } else { + $list[] = implode(', ', $names); + } + } } + + $message[] = gTxt($txtItem, array('{list}' => implode(', ', $list))); } + } + + $message = implode('
', $message); - return safe_insert($this->getTable(), $set, $debug); + if ($thisResults['success'] && ($thisResults['warning'] || $thisResults['error'])) { + $severity = 'E_WARNING'; + } elseif ($thisResults['warning']) { + $severity = 'E_WARNING'; + } elseif ($thisResults['error']) { + $severity = 'E_ERROR'; + } else { + $severity = ''; } - /** - * Update the $table property value related table. - * - * @param string $set Optional SET clause. - * Builds the clause from the $infos property value if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $base (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool FALSE on error. - */ - - public function updateRow($set = null, $where = null, $debug = false) - { - $set !== null or $set = $this->getInfos(true); - - if ($where === null) { - $where = ''; - $base = $this->getBase(); - - if ($base) { - $where = "name = '".doSlash($base)."'"; - } + return $severity ? array($message, constant($severity)) : $message; + } - if (property_exists($this, 'skin')) { - $skin = $this->getSkin(); - $skinName = $skin ? $skin->getName() : ''; + /** + * $extension property getter. + * + * @return string static::$extension. + */ - if ($skinName) { - !$where or $where.= ' AND '; - $where .= "skin = '".doSlash($skinName)."'"; - } - } + protected static function getExtension() + { + return static::$extension; + } - $where or $where = '1 = 1'; - } + /** + * Get files from the $dir property value related directory. + * + * @param array $names Optional filenames to filter the result. + * @param int $maxDepth Optional RecursiveIteratorIterator related property value (default = -1 infinite). + * @return object Collection of file objects. + */ - return safe_update($this->getTable(), $set, $where, $debug); + protected function getFiles($names = null, $maxDepth = null) + { + $filter = $names ? $names : '#^'.self::getNamePattern().'.'.self::getExtension().'$#'; + $files = \Txp::get('Textpattern\Iterator\RecDirIterator', $this->getDirPath()); + $filter = \Txp::get('Textpattern\Iterator\RecFilterIterator', $files, $filter); + $filteredFiles = \Txp::get('Textpattern\Iterator\RecIteratorIterator', $filter); + $maxDepth !== null or $filteredFiles->setMaxDepth($maxDepth); + + return $filteredFiles; + } + + /** + * Insert a row into the $table property value related table. + * + * @param string $set Optional SET clause. + * Builds the clause from the $infos (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool FALSE on error. + */ + + public function createRow($set = null, $debug = false) + { + if ($set === null) { + $set = $this->getInfos(true); + + if (property_exists($this, 'skin')) { + $set .= " skin = '".doSlash($this->getSkin()->getName())."'"; + } } - /** - * Get a row field from the $table property value related table. - * - * @param string $thing Optional SELECT clause. - * Uses 'name' if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $name (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return mixed The Field or FALSE on error. - */ - - public function getField($thing = null, $where = null, $debug = false) - { - $thing !== null or $thing = 'name'; - - if ($where === null) { - $where = ''; - $name = $this->getName(); - - if ($name) { - $where .= "name = '".doSlash($name)."'"; - } + return safe_insert($this->getTable(), $set, $debug); + } - if (property_exists($this, 'skin')) { - $skin = $this->getSkin(); - $skinName = $skin ? $skin->getName() : ''; + /** + * Update the $table property value related table. + * + * @param string $set Optional SET clause. + * Builds the clause from the $infos property value if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $base (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool FALSE on error. + */ + + public function updateRow($set = null, $where = null, $debug = false) + { + $set !== null or $set = $this->getInfos(true); - if ($skinName) { - !$where or $where.= ' AND '; - $where .= "skin = '".doSlash($skinName)."'"; - } - } + if ($where === null) { + $where = ''; + $base = $this->getBase(); - $where or $where = '1 = 1'; + if ($base) { + $where = "name = '".doSlash($base)."'"; } - return safe_field($thing, $this->getTable(), $where, $debug); - } + if (property_exists($this, 'skin')) { + $skin = $this->getSkin(); + $skinName = $skin ? $skin->getName() : ''; - /** - * Delete rows from the $table property value related table. - * - * @param string $where Optional WHERE clause. - * Builds the clause from the $names (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool FALSE on error. - */ - - public function deleteRows($where = null, $debug = false) - { - if ($where === null) { - $where = ''; - $names = $this->getNames(); - - if ($names) { - $where .= "name IN ('".implode("', '", array_map('doSlash', $names))."')"; + if ($skinName) { + !$where or $where.= ' AND '; + $where .= "skin = '".doSlash($skinName)."'"; } + } + + $where or $where = '1 = 1'; + } - if (property_exists($this, 'skin')) { - $skin = $this->getSkin(); - $skinName = $skin ? $skin->getName() : ''; + return safe_update($this->getTable(), $set, $where, $debug); + } - if ($skinName) { - !$where or $where.= ' AND '; - $where .= "skin = '".doSlash($skinName)."'"; - } - } + /** + * Get a row field from the $table property value related table. + * + * @param string $thing Optional SELECT clause. + * Uses 'name' if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $name (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return mixed The Field or FALSE on error. + */ + + public function getField($thing = null, $where = null, $debug = false) + { + $thing !== null or $thing = 'name'; - $where or $where = '1 = 1'; + if ($where === null) { + $where = ''; + $name = $this->getName(); + + if ($name) { + $where .= "name = '".doSlash($name)."'"; } - return safe_delete($this->getTable(), $where, $debug); - } + if (property_exists($this, 'skin')) { + $skin = $this->getSkin(); + $skinName = $skin ? $skin->getName() : ''; + + if ($skinName) { + !$where or $where.= ' AND '; + $where .= "skin = '".doSlash($skinName)."'"; + } + } - /** - * Count rows in the $table property value related table. - * - * @param string $where The where clause. - * @param bool $debug Dump query - * @return mixed Number of rows or FALSE on error - */ - - public function countRows($where = null, $debug = false) - { - return safe_count($this->getTable(), ($where === null ? '1 = 1' : $where), $debug); + $where or $where = '1 = 1'; } - /** - * Get a row from the $table property value related table as an associative array. - * - * @param string $things Optional SELECT clause. - * Uses '*' (all) if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $name (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool Array. - */ - - public function getRow($things = null, $where = null, $debug = false) - { - $things !== null or $things = '*'; - - if ($where === null) { - $where = ''; - $name = $this->getName(); - - if ($name) { - $where .= "name = '".doSlash($name)."'"; - } + return safe_field($thing, $this->getTable(), $where, $debug); + } - if (property_exists($this, 'skin')) { - $skin = $this->getSkin(); - $skinName = $skin ? $skin->getName() : ''; + /** + * Delete rows from the $table property value related table. + * + * @param string $where Optional WHERE clause. + * Builds the clause from the $names (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool FALSE on error. + */ - if ($skinName) { - !$where or $where .= ' AND '; - $where .= "skin = '".doSlash($skinName)."'"; - } - } + public function deleteRows($where = null, $debug = false) + { + if ($where === null) { + $where = ''; + $names = $this->getNames(); - $where or $where = '1=1'; + if ($names) { + $where .= "name IN ('".implode("', '", array_map('doSlash', $names))."')"; } - return safe_row($things, $this->getTable(), $where, $debug); - } + if (property_exists($this, 'skin')) { + $skin = $this->getSkin(); + $skinName = $skin ? $skin->getName() : ''; - /** - * Get rows from the $table property value related table as an associative array. - * - * @param string $thing Optional SELECT clause. - * Uses '*' (all) if null. - * @param string $where Optional WHERE clause (default: "name = '".doSlash($this->getName())."'") - * Builds the clause from the $names (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return array (Empty on error) - */ - - public function getRows($things = null, $where = null, $debug = false) - { - $things !== null or $things = '*'; - - if ($where === null) { - $where = ''; - $names = $this->getNames(); - - if ($names) { - $where .= "name IN ('".implode("', '", array_map('doSlash', $names))."')"; + if ($skinName) { + !$where or $where.= ' AND '; + $where .= "skin = '".doSlash($skinName)."'"; } + } - if (property_exists($this, 'skin')) { - $skin = $this->getSkin(); - $skinName = $skin ? $skin->getName() : ''; + $where or $where = '1 = 1'; + } - if ($skinName) { - !$where or $where.= ' AND '; - $where .= "skin = '".doSlash($skinName)."'"; - } - } + return safe_delete($this->getTable(), $where, $debug); + } - $where or $where = '1=1'; - } + /** + * Count rows in the $table property value related table. + * + * @param string $where The where clause. + * @param bool $debug Dump query + * @return mixed Number of rows or FALSE on error + */ - $rs = safe_rows_start($things, $this->getTable(), $where, $debug); + public function countRows($where = null, $debug = false) + { + return safe_count($this->getTable(), ($where === null ? '1 = 1' : $where), $debug); + } - if ($rs) { - $rows = array(); + /** + * Get a row from the $table property value related table as an associative array. + * + * @param string $things Optional SELECT clause. + * Uses '*' (all) if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $name (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool Array. + */ + + public function getRow($things = null, $where = null, $debug = false) + { + $things !== null or $things = '*'; - while ($row = nextRow($rs)) { - $rows[] = $row; - } + if ($where === null) { + $where = ''; + $name = $this->getName(); - return $rows; + if ($name) { + $where .= "name = '".doSlash($name)."'"; } - return array(); - } + if (property_exists($this, 'skin')) { + $skin = $this->getSkin(); + $skinName = $skin ? $skin->getName() : ''; - /** - * Get the skin name used by the default section. - * - * @return mixed Skin name or FALSE on error. - */ + if ($skinName) { + !$where or $where .= ' AND '; + $where .= "skin = '".doSlash($skinName)."'"; + } + } - protected function getDefault() - { - return safe_field($this->getEvent(), 'txp_section', 'name = "default"'); + $where or $where = '1=1'; } - /** - * $installed property setter. - * - * @param array $this->installed. - */ + return safe_row($things, $this->getTable(), $where, $debug); + } - protected function setInstalled() - { - $things = 'name'; - $isAsset = property_exists($this, 'skin'); - $thing = $isAsset ? 'skin' : 'title'; - $things .= ', '.$thing; + /** + * Get rows from the $table property value related table as an associative array. + * + * @param string $thing Optional SELECT clause. + * Uses '*' (all) if null. + * @param string $where Optional WHERE clause (default: "name = '".doSlash($this->getName())."'") + * Builds the clause from the $names (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return array (Empty on error) + */ + + public function getRows($things = null, $where = null, $debug = false) + { + $things !== null or $things = '*'; - $rows = $this->getRows($things, '1=1 ORDER BY name'); + if ($where === null) { + $where = ''; + $names = $this->getNames(); - $this->installed = array(); + if ($names) { + $where .= "name IN ('".implode("', '", array_map('doSlash', $names))."')"; + } - foreach ($rows as $row) { - if ($isAsset) { - $this->installed[$row[$thing]][] = $row['name']; - } else { - $this->installed[$row['name']] = $row[$thing]; + if (property_exists($this, 'skin')) { + $skin = $this->getSkin(); + $skinName = $skin ? $skin->getName() : ''; + + if ($skinName) { + !$where or $where.= ' AND '; + $where .= "skin = '".doSlash($skinName)."'"; } } - return $this->getInstalled(); + $where or $where = '1=1'; } - /** - * $installed property getter. - * - * @return array $this->installed. - */ + $rs = safe_rows_start($things, $this->getTable(), $where, $debug); - public function getInstalled() - { - return $this->installed === null ? $this->setInstalled() : $this->installed; + if ($rs) { + $rows = array(); + + while ($row = nextRow($rs)) { + $rows[] = $row; + } + + return $rows; } - /** - * Whether a skin/template is installed or not. - * - * @param string $name Skin name (default: $this->getName()). - * @return bool - */ + return array(); + } + + /** + * Get the skin name used by the default section. + * + * @return mixed Skin name or FALSE on error. + */ + + protected function getDefault() + { + return safe_field($this->getEvent(), 'txp_section', 'name = "default"'); + } + + /** + * $installed property setter. + * + * @param array $this->installed. + */ + + protected function setInstalled() + { + $things = 'name'; + $isAsset = property_exists($this, 'skin'); + $thing = $isAsset ? 'skin' : 'title'; + $things .= ', '.$thing; + + $rows = $this->getRows($things, '1=1 ORDER BY name'); - protected function isInstalled($name = null) - { - $isAsset = property_exists($this, 'skin'); - $name !== null or $name = $this->getName(); + $this->installed = array(); - if ($this->installed === null) { - $isInstalled = (bool) $this->getField('name', "name = '".$name."'"); + foreach ($rows as $row) { + if ($isAsset) { + $this->installed[$row[$thing]][] = $row['name']; } else { - if ($isAsset) { - $isInstalled = false; - $installed = $this->getInstalled(); + $this->installed[$row['name']] = $row[$thing]; + } + } - foreach ($installed as $skin) { - if (in_array($name, array_keys($installed))) { - $isInstalled = $skin; - } + return $this->getInstalled(); + } + + /** + * $installed property getter. + * + * @return array $this->installed. + */ + + public function getInstalled() + { + return $this->installed === null ? $this->setInstalled() : $this->installed; + } + + /** + * Whether a skin/template is installed or not. + * + * @param string $name Skin name (default: $this->getName()). + * @return bool + */ + + protected function isInstalled($name = null) + { + $isAsset = property_exists($this, 'skin'); + $name !== null or $name = $this->getName(); + + if ($this->installed === null) { + $isInstalled = (bool) $this->getField('name', "name = '".$name."'"); + } else { + if ($isAsset) { + $isInstalled = false; + $installed = $this->getInstalled(); + + foreach ($installed as $skin) { + if (in_array($name, array_keys($installed))) { + $isInstalled = $skin; } - } else { - $isInstalled = in_array($name, array_keys($this->getInstalled())); } + } else { + $isInstalled = in_array($name, array_keys($this->getInstalled())); } - - return $isInstalled; } - /** - * Wether a directory is empty or not. - * - * @param string $path The directory path - * @return mixed NULL if the directory is not readable (or does not exist), - * TRUE if empty, otherwise, FALSE. - */ - protected static function isDirEmpty($path) - { - if (!is_readable($path)) { - return null; - } - $handle = opendir($path); - while (false !== ($entry = readdir($handle))) { - if ($entry != "." && $entry != "..") { - return false; - } + return $isInstalled; + } + + /** + * Wether a directory is empty or not. + * + * @param string $path The directory path + * @return mixed NULL if the directory is not readable (or does not exist), + * TRUE if empty, otherwise, FALSE. + */ + protected static function isDirEmpty($path) + { + if (!is_readable($path)) { + return null; + } + $handle = opendir($path); + while (false !== ($entry = readdir($handle))) { + if ($entry != "." && $entry != "..") { + return false; } - return true; } + return true; } } diff --git a/textpattern/vendors/Textpattern/Skin/CommonInterface.php b/textpattern/vendors/Textpattern/Skin/CommonInterface.php index 1c9c4a518e..4e98ab3c87 100644 --- a/textpattern/vendors/Textpattern/Skin/CommonInterface.php +++ b/textpattern/vendors/Textpattern/Skin/CommonInterface.php @@ -30,176 +30,175 @@ * @package Skin */ -namespace Textpattern\Skin { - - interface CommonInterface - { - /** - * $names property setter/sanitizer. - * - * @param array $names Multiple skin or template names to work with related methods. - * @return object $this The current object (chainable). - */ - - public function setNames($names = null); - - /** - * $name property setter. - * - * @param array $name Single skin or template name to work with related methods. - * Takes the '_last_saved' or '_editing' related preference value if null. - * @return object $this The current object (chainable). - */ - - public function setName($name = null); - - /** - * $base property setter. - * - * @param object $this The current object (chainable). - */ - - public function setBase($name); - - /** - * Get the current 'skin_editing' or '{asset}_last_saved' pref value. - * - * @return mixed Skin/template name | false on error. - */ - - public function getEditing(); - - /** - * Set the 'skin_editing' or '{asset}_last_saved' pref value - * to the $name property value. - * - * @return bool false on error. - */ - - public function setEditing(); - - /** - * Get the $results property value as a message to display in the admin tabs. - * - * @return mixed Message or array containing the message - * and its related user notice constant. - */ - - public function getMessage(); - - /** - * Import/Override (and clean) multiple skins (and their related $assets) - * or multiple templates from the $names (+ $skin) property value(s). - * Merges results in the related property. - * - * @param bool $sync Whether to removes extra skin template rows or not; - * @param bool $override Whether to insert or update the skins. - * @return object $this The current object (chainable). - */ - - public function import($sync = false, $override = false); - - /** - * Export (and clean) multiple skins (and their related $assets) - * or multiple templates from the $names (+ $skin) property value(s). - * Merges results in the related property. - * - * @param bool $sync Whether to removes extra skin template files or not; - * @return object $this The current object (chainable). - */ - - public function export($sync = false, $override = false); - - /** - * Insert a row into the $table property value related table. - * - * @param string $set Optional SET clause. - * Builds the clause from the $infos (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool FALSE on error. - */ - - public function createRow($set = null, $debug = false); - - /** - * Update the $table property value related table. - * - * @param string $set Optional SET clause. - * Builds the clause from the $infos property value if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $base (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool FALSE on error. - */ - - public function updateRow($set = null, $where = null, $debug = false); - - /** - * Get a row field from the $table property value related table. - * - * @param string $thing Optional SELECT clause. - * Uses 'name' if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $name (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return mixed The Field or FALSE on error. - */ - - public function getField($thing = null, $where = null, $debug = false); - - /** - * Delete rows from the $table property value related table. - * - * @param string $where Optional WHERE clause. - * Builds the clause from the $names (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool false on error. - */ - - public function deleteRows($where = null, $debug = false); - - /** - * Count rows in the $table property value related table. - * - * @param string $where The where clause. - * @param bool $debug Dump query - * @return mixed Number of rows or FALSE on error - */ - - public function countRows($where = null, $debug = false); - - /** - * Get a row from the $table property value related table as an associative array. - * - * @param string $things Optional SELECT clause. - * Uses '*' (all) if null. - * @param string $where Optional WHERE clause. - * Builds the clause from the $name (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return bool Array. - */ - - public function getRow($things = null, $where = null, $debug = false); - - /** - * Get rows from the $table property value related table as an associative array. - * - * @param string $thing Optional SELECT clause. - * Uses '*' (all) if null. - * @param string $where Optional WHERE clause (default: "name = '".doSlash($this->getName())."'") - * Builds the clause from the $names (+ $skin) property value(s) if null. - * @param bool $debug Dump query - * @return array (Empty on error) - */ - - public function getRows($things = null, $where = null, $debug = false); - - /** - * $installed property getter. - * - * @return array $this->installed. - */ - - public function getInstalled(); - } +namespace Textpattern\Skin; + +interface CommonInterface +{ + /** + * $names property setter/sanitizer. + * + * @param array $names Multiple skin or template names to work with related methods. + * @return object $this The current object (chainable). + */ + + public function setNames($names = null); + + /** + * $name property setter. + * + * @param array $name Single skin or template name to work with related methods. + * Takes the '_last_saved' or '_editing' related preference value if null. + * @return object $this The current object (chainable). + */ + + public function setName($name = null); + + /** + * $base property setter. + * + * @param object $this The current object (chainable). + */ + + public function setBase($name); + + /** + * Get the current 'skin_editing' or '{asset}_last_saved' pref value. + * + * @return mixed Skin/template name | false on error. + */ + + public function getEditing(); + + /** + * Set the 'skin_editing' or '{asset}_last_saved' pref value + * to the $name property value. + * + * @return bool false on error. + */ + + public function setEditing(); + + /** + * Get the $results property value as a message to display in the admin tabs. + * + * @return mixed Message or array containing the message + * and its related user notice constant. + */ + + public function getMessage(); + + /** + * Import/Override (and clean) multiple skins (and their related $assets) + * or multiple templates from the $names (+ $skin) property value(s). + * Merges results in the related property. + * + * @param bool $sync Whether to removes extra skin template rows or not; + * @param bool $override Whether to insert or update the skins. + * @return object $this The current object (chainable). + */ + + public function import($sync = false, $override = false); + + /** + * Export (and clean) multiple skins (and their related $assets) + * or multiple templates from the $names (+ $skin) property value(s). + * Merges results in the related property. + * + * @param bool $sync Whether to removes extra skin template files or not; + * @return object $this The current object (chainable). + */ + + public function export($sync = false, $override = false); + + /** + * Insert a row into the $table property value related table. + * + * @param string $set Optional SET clause. + * Builds the clause from the $infos (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool FALSE on error. + */ + + public function createRow($set = null, $debug = false); + + /** + * Update the $table property value related table. + * + * @param string $set Optional SET clause. + * Builds the clause from the $infos property value if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $base (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool FALSE on error. + */ + + public function updateRow($set = null, $where = null, $debug = false); + + /** + * Get a row field from the $table property value related table. + * + * @param string $thing Optional SELECT clause. + * Uses 'name' if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $name (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return mixed The Field or FALSE on error. + */ + + public function getField($thing = null, $where = null, $debug = false); + + /** + * Delete rows from the $table property value related table. + * + * @param string $where Optional WHERE clause. + * Builds the clause from the $names (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool false on error. + */ + + public function deleteRows($where = null, $debug = false); + + /** + * Count rows in the $table property value related table. + * + * @param string $where The where clause. + * @param bool $debug Dump query + * @return mixed Number of rows or FALSE on error + */ + + public function countRows($where = null, $debug = false); + + /** + * Get a row from the $table property value related table as an associative array. + * + * @param string $things Optional SELECT clause. + * Uses '*' (all) if null. + * @param string $where Optional WHERE clause. + * Builds the clause from the $name (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return bool Array. + */ + + public function getRow($things = null, $where = null, $debug = false); + + /** + * Get rows from the $table property value related table as an associative array. + * + * @param string $thing Optional SELECT clause. + * Uses '*' (all) if null. + * @param string $where Optional WHERE clause (default: "name = '".doSlash($this->getName())."'") + * Builds the clause from the $names (+ $skin) property value(s) if null. + * @param bool $debug Dump query + * @return array (Empty on error) + */ + + public function getRows($things = null, $where = null, $debug = false); + + /** + * $installed property getter. + * + * @return array $this->installed. + */ + + public function getInstalled(); } diff --git a/textpattern/vendors/Textpattern/Skin/Css.php b/textpattern/vendors/Textpattern/Skin/Css.php index 7040ffaa29..bc02c4f36b 100644 --- a/textpattern/vendors/Textpattern/Skin/Css.php +++ b/textpattern/vendors/Textpattern/Skin/Css.php @@ -30,34 +30,33 @@ * @package Skin */ -namespace Textpattern\Skin { - - class Css extends AssetBase implements CssInterface - { - protected static $extension = 'css'; - protected static $dir = 'styles'; - protected static $fileContentsField = 'css'; - protected static $essential = array( - array( - 'name' => 'default', - 'css' => '/* Contents of the css tag goes here. ' - .'See https://docs.textpattern.io/tags/css */' - ), - ); - - /** - * {@inheritdoc} - */ - - public function setInfos( - $name, - $css = null - ) { - $name = $this->setName($name)->getName(); - - $this->infos = compact('name', 'css'); - - return $this; - } +namespace Textpattern\Skin; + +class Css extends AssetBase implements CssInterface +{ + protected static $extension = 'css'; + protected static $dir = 'styles'; + protected static $fileContentsField = 'css'; + protected static $essential = array( + array( + 'name' => 'default', + 'css' => '/* Contents of the css tag goes here. ' + .'See https://docs.textpattern.io/tags/css */' + ), + ); + + /** + * {@inheritdoc} + */ + + public function setInfos( + $name, + $css = null + ) { + $name = $this->setName($name)->getName(); + + $this->infos = compact('name', 'css'); + + return $this; } } diff --git a/textpattern/vendors/Textpattern/Skin/CssInterface.php b/textpattern/vendors/Textpattern/Skin/CssInterface.php index 51134e8919..7c1ba5eee8 100644 --- a/textpattern/vendors/Textpattern/Skin/CssInterface.php +++ b/textpattern/vendors/Textpattern/Skin/CssInterface.php @@ -30,21 +30,20 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - interface CssInterface - { - /** - * $infos+$name properties setter. - * - * @param string $name CSS name; - * @param string $css CSS contents; - * @return object $this The current class object (chainable). - */ +interface CssInterface +{ + /** + * $infos+$name properties setter. + * + * @param string $name CSS name; + * @param string $css CSS contents; + * @return object $this The current class object (chainable). + */ - public function setInfos( - $name, - $css = null - ); - } + public function setInfos( + $name, + $css = null + ); } diff --git a/textpattern/vendors/Textpattern/Skin/Form.php b/textpattern/vendors/Textpattern/Skin/Form.php index 701922d7eb..ae65d62b45 100644 --- a/textpattern/vendors/Textpattern/Skin/Form.php +++ b/textpattern/vendors/Textpattern/Skin/Form.php @@ -30,88 +30,87 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - class Form extends AssetBase implements FormInterface - { - protected static $dir = 'forms'; - protected static $subdirField = 'type'; - protected static $subdirValues = array('article', 'category', 'comment', 'file', 'link', 'section', 'misc'); - protected static $defaultSubdir = 'misc'; - protected static $fileContentsField = 'Form'; - protected static $essential = array( - array( - 'name' => 'comments', - 'type' => 'comment', - 'Form' => '', - ), - array( - 'name' => 'comments_display', - 'type' => 'comment', - 'Form' => '', - ), - array( - 'name' => 'comment_form', - 'type' => 'comment', - 'Form' => '', - ), - array( - 'name' => 'default', - 'type' => 'article', - 'Form' => '', - ), - array( - 'name' => 'plainlinks', - 'type' => 'link', - 'Form' => '', - ), - array( - 'name' => 'files', - 'type' => 'file', - 'Form' => '', - ), - ); +class Form extends AssetBase implements FormInterface +{ + protected static $dir = 'forms'; + protected static $subdirField = 'type'; + protected static $subdirValues = array('article', 'category', 'comment', 'file', 'link', 'section', 'misc'); + protected static $defaultSubdir = 'misc'; + protected static $fileContentsField = 'Form'; + protected static $essential = array( + array( + 'name' => 'comments', + 'type' => 'comment', + 'Form' => '', + ), + array( + 'name' => 'comments_display', + 'type' => 'comment', + 'Form' => '', + ), + array( + 'name' => 'comment_form', + 'type' => 'comment', + 'Form' => '', + ), + array( + 'name' => 'default', + 'type' => 'article', + 'Form' => '', + ), + array( + 'name' => 'plainlinks', + 'type' => 'link', + 'Form' => '', + ), + array( + 'name' => 'files', + 'type' => 'file', + 'Form' => '', + ), + ); - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - public function setInfos( - $name, - $type = null, - $Form = null - ) { - $name = $this->setName($name)->getName(); + public function setInfos( + $name, + $type = null, + $Form = null + ) { + $name = $this->setName($name)->getName(); - $this->infos = compact('name', 'type', 'Form'); + $this->infos = compact('name', 'type', 'Form'); - return $this; - } + return $this; + } - /** - * $defaultSubdir property getter. - */ + /** + * $defaultSubdir property getter. + */ - public static function getTypes() - { - return static::$subdirValues; - } + public static function getTypes() + { + return static::$subdirValues; + } - /** - * Get the skin name used by the default section. - * - * @return mixed Skin name or FALSE on error. - */ + /** + * Get the skin name used by the default section. + * + * @return mixed Skin name or FALSE on error. + */ - protected function getDefault() - { - return 'default'; - } + protected function getDefault() + { + return 'default'; } } diff --git a/textpattern/vendors/Textpattern/Skin/FormInterface.php b/textpattern/vendors/Textpattern/Skin/FormInterface.php index 0068efe1ed..d50344dd13 100644 --- a/textpattern/vendors/Textpattern/Skin/FormInterface.php +++ b/textpattern/vendors/Textpattern/Skin/FormInterface.php @@ -30,29 +30,28 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - interface FormInterface - { - /** - * $infos+$name properties setter. - * - * @param string $name Form name; - * @param string $type Form type; - * @param string $Form Form contents; - * @return object $this The current class object (chainable). - */ +interface FormInterface +{ + /** + * $infos+$name properties setter. + * + * @param string $name Form name; + * @param string $type Form type; + * @param string $Form Form contents; + * @return object $this The current class object (chainable). + */ - public function setInfos( - $name, - $type = null, - $Form = null - ); + public function setInfos( + $name, + $type = null, + $Form = null + ); - /** - * $subdirValues getter. - */ + /** + * $subdirValues getter. + */ - public static function getTypes(); - } + public static function getTypes(); } diff --git a/textpattern/vendors/Textpattern/Skin/Page.php b/textpattern/vendors/Textpattern/Skin/Page.php index 6124be5519..cf223cbbf2 100644 --- a/textpattern/vendors/Textpattern/Skin/Page.php +++ b/textpattern/vendors/Textpattern/Skin/Page.php @@ -30,36 +30,35 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - class Page extends AssetBase implements PageInterface - { - protected static $dir = 'pages'; - protected static $fileContentsField = 'user_html'; - protected static $essential = array( - array( - 'name' => 'default', - 'user_html' => '', - ), - array( - 'name' => 'error_default', - 'user_html' => '', - ), - ); +class Page extends AssetBase implements PageInterface +{ + protected static $dir = 'pages'; + protected static $fileContentsField = 'user_html'; + protected static $essential = array( + array( + 'name' => 'default', + 'user_html' => '', + ), + array( + 'name' => 'error_default', + 'user_html' => '', + ), + ); - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - public function setInfos( - $name, - $user_html = null - ) { - $name = $this->setName($name)->getName(); + public function setInfos( + $name, + $user_html = null + ) { + $name = $this->setName($name)->getName(); - $this->infos = compact('name', 'user_html'); + $this->infos = compact('name', 'user_html'); - return $this; - } + return $this; } } diff --git a/textpattern/vendors/Textpattern/Skin/PageInterface.php b/textpattern/vendors/Textpattern/Skin/PageInterface.php index 2cce5f1b55..39faa607b7 100644 --- a/textpattern/vendors/Textpattern/Skin/PageInterface.php +++ b/textpattern/vendors/Textpattern/Skin/PageInterface.php @@ -30,21 +30,20 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - interface PageInterface - { - /** - * $infos+$name properties setter. - * - * @param string $name Page name; - * @param string $user_html Page contents; - * @return object $this The current class object (chainable). - */ +interface PageInterface +{ + /** + * $infos+$name properties setter. + * + * @param string $name Page name; + * @param string $user_html Page contents; + * @return object $this The current class object (chainable). + */ - public function setInfos( - $name, - $user_html = null - ); - } + public function setInfos( + $name, + $user_html = null + ); } diff --git a/textpattern/vendors/Textpattern/Skin/Skin.php b/textpattern/vendors/Textpattern/Skin/Skin.php index 2b763d463e..06b9f17f0c 100644 --- a/textpattern/vendors/Textpattern/Skin/Skin.php +++ b/textpattern/vendors/Textpattern/Skin/Skin.php @@ -30,1596 +30,1595 @@ * @package Skin */ -namespace Textpattern\Skin { +namespace Textpattern\Skin; - class Skin extends CommonBase implements SkinInterface +class Skin extends CommonBase implements SkinInterface +{ + /** + * Skin assets related objects. + * + * @var array Page, Form and CSS class objects. + * @see setAssets(). + */ + + private $assets; + + /** + * Class related main file. + * + * @var string Filename. + * @see getFile(). + */ + + protected static $filename = 'manifest.json'; + + /** + * {@inheritdoc} + */ + + protected static $extension = 'json'; + + /** + * Importable skins. + * + * @var array Associative array of skin names and their infos from JSON files + * @see setUploaded(), getUploaded(). + */ + + protected $uploaded; + + /** + * Class related directory path. + * + * @var string Path. + * @see setDirPath(), getDirPath(). + */ + + protected $dirPath; + + /** + * {@inheritdoc} + */ + + public function __construct() { - /** - * Skin assets related objects. - * - * @var array Page, Form and CSS class objects. - * @see setAssets(). - */ - - private $assets; - - /** - * Class related main file. - * - * @var string Filename. - * @see getFile(). - */ - - protected static $filename = 'manifest.json'; - - /** - * {@inheritdoc} - */ - - protected static $extension = 'json'; - - /** - * Importable skins. - * - * @var array Associative array of skin names and their infos from JSON files - * @see setUploaded(), getUploaded(). - */ - - protected $uploaded; - - /** - * Class related directory path. - * - * @var string Path. - * @see setDirPath(), getDirPath(). - */ - - protected $dirPath; - - /** - * {@inheritdoc} - */ - - public function __construct() - { - parent::__construct(); - } + parent::__construct(); + } - public function __toString() - { - return $this->getName(); - } + public function __toString() + { + return $this->getName(); + } - protected function mergeResults($asset, $status) - { - $this->results = array_merge_recursive($this->getResults(), $asset->getResults($status)); + protected function mergeResults($asset, $status) + { + $this->results = array_merge_recursive($this->getResults(), $asset->getResults($status)); - return $this; - } + return $this; + } - /** - * $dirPath property setter. - * - * @param string $path Path (default: get_pref('path_to_site').DS.get_pref('skin_dir')). - * @return string $this->dirPath - */ + /** + * $dirPath property setter. + * + * @param string $path Path (default: get_pref('path_to_site').DS.get_pref('skin_dir')). + * @return string $this->dirPath + */ - public function setDirPath($path = null) - { - $path !== null or $path = get_pref('path_to_site').DS.get_pref($this->getEvent().'_dir'); + public function setDirPath($path = null) + { + $path !== null or $path = get_pref('path_to_site').DS.get_pref($this->getEvent().'_dir'); - $this->dirPath = rtrim($path, DS); - $this->uploaded = null; + $this->dirPath = rtrim($path, DS); + $this->uploaded = null; - return $this->getDirPath(); - } + return $this->getDirPath(); + } - /** - * $dirPath property getter - * - * @return string $this->dirPath - */ + /** + * $dirPath property getter + * + * @return string $this->dirPath + */ - protected function getDirPath() - { - $this->dirPath !== null or $this->setDirPath(); + protected function getDirPath() + { + $this->dirPath !== null or $this->setDirPath(); + + return $this->dirPath; + } - return $this->dirPath; + /** + * $assets property setter. + * + * @param array $pages Page names to work with; + * @param array $forms Form names to work with; + * @param array $styles CSS names to work with. + * @return object $this The current class object (chainable) + */ + + public function setAssets($pages = null, $forms = null, $styles = null) + { + $assets = array( + 'Page' => $pages, + 'Form' => $forms, + 'Css' => $styles, + ); + + foreach ($assets as $class => $assets) { + $this->assets[] = \Txp::get('Textpattern\Skin\\'.$class, $this)->setNames($assets); } - /** - * $assets property setter. - * - * @param array $pages Page names to work with; - * @param array $forms Form names to work with; - * @param array $styles CSS names to work with. - * @return object $this The current class object (chainable) - */ - - public function setAssets($pages = null, $forms = null, $styles = null) - { - $assets = array( - 'Page' => $pages, - 'Form' => $forms, - 'Css' => $styles, - ); + return $this; + } - foreach ($assets as $class => $assets) { - $this->assets[] = \Txp::get('Textpattern\Skin\\'.$class, $this)->setNames($assets); - } + /** + * $assets property getter. + * + * @return array $this->$assets + */ - return $this; - } + protected function getAssets() + { + $this->assets !== null or $this->setAssets(); - /** - * $assets property getter. - * - * @return array $this->$assets - */ + return $this->assets; + } - protected function getAssets() - { - $this->assets !== null or $this->setAssets(); + /** + * $infos and $name properties setter. + * + * @param string $name Skin name; + * @param string $title Skin title; + * @param string $version Skin version; + * @param string $description Skin description; + * @param string $author Skin author; + * @param string $author_uri Skin author URL; + * @return object $this The current class object (chainable). + */ + + public function setInfos( + $name, + $title = null, + $version = null, + $description = null, + $author = null, + $author_uri = null + ) { + $name = $this->setName($name)->getName(); + + $title or $title = ucfirst($name); + + $this->infos = compact('name', 'title', 'version', 'description', 'author', 'author_uri'); + + return $this; + } - return $this->assets; - } + /** + * Get a $dir property value related subdirectory path. + * + * @param string $name Directory(/skin) name (default: $this->getName()). + * @return string The Path + */ - /** - * $infos and $name properties setter. - * - * @param string $name Skin name; - * @param string $title Skin title; - * @param string $version Skin version; - * @param string $description Skin description; - * @param string $author Skin author; - * @param string $author_uri Skin author URL; - * @return object $this The current class object (chainable). - */ - - public function setInfos( - $name, - $title = null, - $version = null, - $description = null, - $author = null, - $author_uri = null - ) { - $name = $this->setName($name)->getName(); - - $title or $title = ucfirst($name); - - $this->infos = compact('name', 'title', 'version', 'description', 'author', 'author_uri'); - - return $this; - } + public function getSubdirPath($name = null) + { + $name !== null or $name = $this->getName(); - /** - * Get a $dir property value related subdirectory path. - * - * @param string $name Directory(/skin) name (default: $this->getName()). - * @return string The Path - */ + return $this->getDirPath().DS.$name; + } - public function getSubdirPath($name = null) - { - $name !== null or $name = $this->getName(); + /** + * $file property getter. + * + * @return string self::$filename. + */ - return $this->getDirPath().DS.$name; - } + protected static function getFilename() + { + return self::$filename; + } - /** - * $file property getter. - * - * @return string self::$filename. - */ + /** + * Get the $file property value related path. + * + * @return string Path. + */ - protected static function getFilename() - { - return self::$filename; - } + protected function getFilePath() + { + return $this->getSubdirPath().DS.self::getFilename(); + } - /** - * Get the $file property value related path. - * - * @return string Path. - */ + /** + * Get and complete the skin related file contents. + * + * @return array Associative array of JSON fields and their related values / fallback values. + */ - protected function getFilePath() - { - return $this->getSubdirPath().DS.self::getFilename(); - } + protected function getFileContents() + { + $contents = json_decode(file_get_contents($this->getFilePath()), true); - /** - * Get and complete the skin related file contents. - * - * @return array Associative array of JSON fields and their related values / fallback values. - */ + $contents === null or $contents = $this->parseInfos($contents); - protected function getFileContents() - { - $contents = json_decode(file_get_contents($this->getFilePath()), true); + return $contents; + } - $contents === null or $contents = $this->parseInfos($contents); + /** + * Parse a skin related infos. + * + * @return array $infos Associative array of fields and their related values / fallback values. + */ - return $contents; - } + protected function parseInfos($infos) + { + extract($infos); - /** - * Parse a skin related infos. - * - * @return array $infos Associative array of fields and their related values / fallback values. - */ + !empty($title) or $title = ucfirst($this->getName()); + !empty($version) or $version = gTxt('unknown'); + !empty($description) or $description = ''; + !empty($author) or $author = gTxt('unknown'); + !empty($author_uri) or $author_uri = ''; - protected function parseInfos($infos) - { - extract($infos); + return compact('title', 'version', 'description', 'author', 'author_uri'); + } - !empty($title) or $title = ucfirst($this->getName()); - !empty($version) or $version = gTxt('unknown'); - !empty($description) or $description = ''; - !empty($author) or $author = gTxt('unknown'); - !empty($author_uri) or $author_uri = ''; + /** + * $sections property getter. + * + * @param array Section names. + */ + + protected function getSections($skin = null) + { + $skin !== null or $skin = $this->getName(); + + return array_values( + safe_column( + 'name', + 'txp_section', + $this->getEvent()." ='".doSlash($skin)."'" + ) + ); + } + + /** + * Update the txp_section table. + * + * @param string $set The SET clause (default: "skin = '".doSlash($this->getName())."'") + * @param string $where The WHERE clause (default: "skin = '".doSlash($this->getBase())."'") + * @return bool FALSE on error. + */ + + public function updateSections($set = null, $where = null) + { + $event = $this->getEvent(); - return compact('title', 'version', 'description', 'author', 'author_uri'); - } + $set !== null or $set = $event." = '".doSlash($this->getName())."'"; - /** - * $sections property getter. - * - * @param array Section names. - */ - - protected function getSections($skin = null) - { - $skin !== null or $skin = $this->getName(); - - return array_values( - safe_column( - 'name', - 'txp_section', - $this->getEvent()." ='".doSlash($skin)."'" - ) - ); - } - - /** - * Update the txp_section table. - * - * @param string $set The SET clause (default: "skin = '".doSlash($this->getName())."'") - * @param string $where The WHERE clause (default: "skin = '".doSlash($this->getBase())."'") - * @return bool FALSE on error. - */ - - public function updateSections($set = null, $where = null) - { - $event = $this->getEvent(); - - $set !== null or $set = $event." = '".doSlash($this->getName())."'"; - - if ($where === null) { - $base = $this->getBase(); - - $where = $base ? $event." = '".doSlash($this->getBase())."'" : '1 = 1'; - } + if ($where === null) { + $base = $this->getBase(); - return safe_update('txp_section', $set, $where); + $where = $base ? $event." = '".doSlash($this->getBase())."'" : '1 = 1'; } - /** - * {@inheritdoc} - */ + return safe_update('txp_section', $set, $where); + } - public function getEditing($fromDB = false) - { - $editing = get_pref($this->getEvent().'_editing', '', $fromDB); + /** + * {@inheritdoc} + */ - if (!$editing) { - $installed = $this->getInstalled(); + public function getEditing($fromDB = false) + { + $editing = get_pref($this->getEvent().'_editing', '', $fromDB); - reset($installed); + if (!$editing) { + $installed = $this->getInstalled(); - $editing = $this->setEditing(key($installed)); - } + reset($installed); - return $editing; + $editing = $this->setEditing(key($installed)); } - /** - * {@inheritdoc} - */ + return $editing; + } - public function setEditing($name = null) - { - global $prefs; + /** + * {@inheritdoc} + */ - $event = $this->getEvent(); + public function setEditing($name = null) + { + global $prefs; - $name !== null or $name = $this->getName(); - $prefs[$event.'_editing'] = $name; + $event = $this->getEvent(); - set_pref($event.'_editing', $name, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); + $name !== null or $name = $this->getName(); + $prefs[$event.'_editing'] = $name; - return $this->getEditing(); - } + set_pref($event.'_editing', $name, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); - /** - * Create a file in the $dir property value related directory. - * - * @param string $pathname The file related path (default: $this->getName().DS.self::getFilename()). - * @param mixed $contents The file related contents as as a string or - * as an associative array for a .json file - * (uses the $infos property related array). - * @return bool Written octets number or FALSE on error. - */ - - protected function createFile($pathname = null, $contents = null) - { - $pathname !== null or $pathname = $this->getName().DS.self::getFilename(); - - if ($contents === null) { - $contents = array_merge( - $this->getInfos(), - array('txp-type' => 'textpattern-theme') - ); + return $this->getEditing(); + } - unset($contents['name']); - } + /** + * Create a file in the $dir property value related directory. + * + * @param string $pathname The file related path (default: $this->getName().DS.self::getFilename()). + * @param mixed $contents The file related contents as as a string or + * as an associative array for a .json file + * (uses the $infos property related array). + * @return bool Written octets number or FALSE on error. + */ + + protected function createFile($pathname = null, $contents = null) + { + $pathname !== null or $pathname = $this->getName().DS.self::getFilename(); - if (pathinfo($pathname, PATHINFO_EXTENSION) === 'json') { - $contents = self::JSONPrettyPrint(json_encode($contents, TEXTPATTERN_JSON)); - } + if ($contents === null) { + $contents = array_merge( + $this->getInfos(), + array('txp-type' => 'textpattern-theme') + ); - return file_put_contents($this->getDirPath().DS.$pathname, $contents); + unset($contents['name']); } - /** - * Replaces the JSON_PRETTY_PRINT flag in json_encode for PHP versions under 5.4. - * - * From https://stackoverflow.com/a/9776726 - * - * @param string $json The JSON contents to prettify; - * @return string Prettified JSON contents. - */ - - protected static function JSONPrettyPrint($json) - { - $result = ''; - $level = 0; - $in_quotes = false; - $in_escape = false; - $ends_line_level = null; - $json_length = strlen($json); - - for ($i = 0; $i < $json_length; $i++) { - $char = $json[$i]; - $new_line_level = null; - $post = ""; - - if ($ends_line_level !== null) { - $new_line_level = $ends_line_level; - $ends_line_level = null; - } + if (pathinfo($pathname, PATHINFO_EXTENSION) === 'json') { + $contents = self::JSONPrettyPrint(json_encode($contents, TEXTPATTERN_JSON)); + } - if ($in_escape) { - $in_escape = false; - } elseif ($char === '"') { - $in_quotes = !$in_quotes; - } elseif (! $in_quotes) { - switch ($char) { - case '}': - case ']': - $level--; - $ends_line_level = null; - $new_line_level = $level; - break; - case '{': - case '[': - $level++; - case ',': - $ends_line_level = $level; - break; - case ':': - $post = " "; - break; - case " ": - case " ": - case "\n": - case "\r": - $char = ""; - $ends_line_level = $new_line_level; - $new_line_level = null; - break; - } - } elseif ($char === '\\') { - $in_escape = true; - } + return file_put_contents($this->getDirPath().DS.$pathname, $contents); + } + + /** + * Replaces the JSON_PRETTY_PRINT flag in json_encode for PHP versions under 5.4. + * + * From https://stackoverflow.com/a/9776726 + * + * @param string $json The JSON contents to prettify; + * @return string Prettified JSON contents. + */ + + protected static function JSONPrettyPrint($json) + { + $result = ''; + $level = 0; + $in_quotes = false; + $in_escape = false; + $ends_line_level = null; + $json_length = strlen($json); + + for ($i = 0; $i < $json_length; $i++) { + $char = $json[$i]; + $new_line_level = null; + $post = ""; + + if ($ends_line_level !== null) { + $new_line_level = $ends_line_level; + $ends_line_level = null; + } - if ($new_line_level !== null) { - $result .= "\n".str_repeat(" ", $new_line_level); + if ($in_escape) { + $in_escape = false; + } elseif ($char === '"') { + $in_quotes = !$in_quotes; + } elseif (! $in_quotes) { + switch ($char) { + case '}': + case ']': + $level--; + $ends_line_level = null; + $new_line_level = $level; + break; + case '{': + case '[': + $level++; + case ',': + $ends_line_level = $level; + break; + case ':': + $post = " "; + break; + case " ": + case " ": + case "\n": + case "\r": + $char = ""; + $ends_line_level = $new_line_level; + $new_line_level = null; + break; } + } elseif ($char === '\\') { + $in_escape = true; + } - $result .= $char.$post; + if ($new_line_level !== null) { + $result .= "\n".str_repeat(" ", $new_line_level); } - return $result; + $result .= $char.$post; } - /** - * $uploaded property setter. - * - * @return object $this The current class object (chainable). - */ + return $result; + } - protected function setUploaded() - { - $this->uploaded = array(); - $files = $this->getFiles(array(self::getFilename()), 1); + /** + * $uploaded property setter. + * + * @return object $this The current class object (chainable). + */ - if ($files) { - foreach ($files as $file) { - $name = basename($file->getPath()); + protected function setUploaded() + { + $this->uploaded = array(); + $files = $this->getFiles(array(self::getFilename()), 1); - if ($name === self::sanitize($name)) { - $infos = $file->getJSONContents(); + if ($files) { + foreach ($files as $file) { + $name = basename($file->getPath()); - if ($infos && $infos['txp-type'] === 'textpattern-theme') { - $this->uploaded[$name] = $this->setName($name)->parseInfos($infos); - } + if ($name === self::sanitize($name)) { + $infos = $file->getJSONContents(); + + if ($infos && $infos['txp-type'] === 'textpattern-theme') { + $this->uploaded[$name] = $this->setName($name)->parseInfos($infos); } } } - - return $this; } - /** - * {@inheritdoc} - */ + return $this; + } - public function getUploaded($expanded = true) - { - $this->uploaded !== null or $this->setUploaded(); + /** + * {@inheritdoc} + */ - if (!$expanded) { - $contracted = array(); + public function getUploaded($expanded = true) + { + $this->uploaded !== null or $this->setUploaded(); - foreach ($this->uploaded as $name => $infos) { - $contracted[$name] = $infos['title']; - } + if (!$expanded) { + $contracted = array(); - return $contracted; + foreach ($this->uploaded as $name => $infos) { + $contracted[$name] = $infos['title']; } - return $this->uploaded; + return $contracted; } - /** - * $installed property merger. - * - * @param array $this->installed. - */ + return $this->uploaded; + } - protected function mergeInstalled($skins) - { - $this->installed = array_merge($this->getInstalled(), $skins); + /** + * $installed property merger. + * + * @param array $this->installed. + */ - return $this->getInstalled(); - } - - /** - * $installed property remover. - * - * @return array $this->installed. - */ - - protected function removeInstalled($names) - { - $this->installed = array_diff_key( - $this->getInstalled(), - array_fill_keys($names, '') - ); + protected function mergeInstalled($skins) + { + $this->installed = array_merge($this->getInstalled(), $skins); - return $this->getInstalled(); - } + return $this->getInstalled(); + } - /** - * {@inheritdoc} - */ - - protected function getTableData($criteria, $sortSQL, $offset, $limit) - { - $assets = array('section', 'page', 'form', 'css'); - $things = array('*'); - $table = $this->getTable(); - - foreach ($assets as $asset) { - $things[] = '(SELECT COUNT(*) ' - .'FROM '.safe_pfx_j('txp_'.$asset).' ' - .'WHERE txp_'.$asset.'.'.$this->getEvent().' = '.$table.'.name) ' - .$asset.'_count'; - } + /** + * $installed property remover. + * + * @return array $this->installed. + */ - return safe_rows_start( - implode(', ', $things), - $table, - $criteria.' order by '.$sortSQL.' limit '.$offset.', '.$limit - ); - } + protected function removeInstalled($names) + { + $this->installed = array_diff_key( + $this->getInstalled(), + array_fill_keys($names, '') + ); - /** - * Create/CreateFrom a single skin (and its related assets) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function create() - { - $event = $this->getEvent(); - $infos = $this->getInfos(); - $name = $infos['name']; - $base = $this->getBase(); - $callbackExtra = compact('infos', 'base'); - $done = false; + return $this->getInstalled(); + } - callback_event('txp.'.$event, 'create', 1, $callbackExtra); + /** + * {@inheritdoc} + */ - if (empty($name)) { - $this->mergeResult($event.'_name_invalid', $name); - } elseif ($base && !$this->isInstalled($base)) { - $this->mergeResult($event.'_unknown', $base); - } elseif ($this->isInstalled()) { - $this->mergeResult($event.'_already_exists', $name); - } elseif (is_dir($nameDirPath = $this->getSubdirPath())) { - // Create a skin which would already have a related directory could cause conflicts. - $this->mergeResult($event.'_already_exists', $nameDirPath); - } elseif (!$this->createRow()) { - $this->mergeResult($event.'_creation_failed', $name); - } else { - $this->mergeResult($event.'_created', $name, 'success'); + protected function getTableData($criteria, $sortSQL, $offset, $limit) + { + $assets = array('section', 'page', 'form', 'css'); + $things = array('*'); + $table = $this->getTable(); + + foreach ($assets as $asset) { + $things[] = '(SELECT COUNT(*) ' + .'FROM '.safe_pfx_j('txp_'.$asset).' ' + .'WHERE txp_'.$asset.'.'.$this->getEvent().' = '.$table.'.name) ' + .$asset.'_count'; + } - // Start working with the skin related assets. - foreach ($this->getAssets() as $assetModel) { - if ($base) { - $this->setName($base); - $rows = $assetModel->getRows(); - $this->setName($name); - } else { - $rows = null; - } + return safe_rows_start( + implode(', ', $things), + $table, + $criteria.' order by '.$sortSQL.' limit '.$offset.', '.$limit + ); + } - if (!$assetModel->createRows($rows)) { - $assetsfailed = true; + /** + * Create/CreateFrom a single skin (and its related assets) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ - $this->mergeResult($assetModel->getEvent().'_creation_failed', $name); - } + public function create() + { + $event = $this->getEvent(); + $infos = $this->getInfos(); + $name = $infos['name']; + $base = $this->getBase(); + $callbackExtra = compact('infos', 'base'); + $done = false; + + callback_event('txp.'.$event, 'create', 1, $callbackExtra); + + if (empty($name)) { + $this->mergeResult($event.'_name_invalid', $name); + } elseif ($base && !$this->isInstalled($base)) { + $this->mergeResult($event.'_unknown', $base); + } elseif ($this->isInstalled()) { + $this->mergeResult($event.'_already_exists', $name); + } elseif (is_dir($nameDirPath = $this->getSubdirPath())) { + // Create a skin which would already have a related directory could cause conflicts. + $this->mergeResult($event.'_already_exists', $nameDirPath); + } elseif (!$this->createRow()) { + $this->mergeResult($event.'_creation_failed', $name); + } else { + $this->mergeResult($event.'_created', $name, 'success'); + + // Start working with the skin related assets. + foreach ($this->getAssets() as $assetModel) { + if ($base) { + $this->setName($base); + $rows = $assetModel->getRows(); + $this->setName($name); + } else { + $rows = null; } - // If the assets related process did not failed; that is a success… - isset($assetsfailed) or $done = $name; - } + if (!$assetModel->createRows($rows)) { + $assetsfailed = true; - callback_event('txp.'.$event, 'create', 0, $callbackExtra + compact('done')); + $this->mergeResult($assetModel->getEvent().'_creation_failed', $name); + } + } - return $this; // Chainable. + // If the assets related process did not failed; that is a success… + isset($assetsfailed) or $done = $name; } - /** - * Update a single skin (and its related dependencies) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function update() - { - $event = $this->getEvent(); - $infos = $this->getInfos(); - $name = $infos['name']; - $base = $this->getBase(); - $callbackExtra = compact('infos', 'base'); - $done = null; - $ready = false; + callback_event('txp.'.$event, 'create', 0, $callbackExtra + compact('done')); - callback_event('txp.'.$event, 'update', 1, $callbackExtra); + return $this; // Chainable. + } - if (empty($name)) { - $this->mergeResult($event.'_name_invalid', $name); - } elseif (!$this->isInstalled($base)) { - $this->mergeResult($event.'_unknown', $base); - } elseif ($base !== $name && $this->isInstalled()) { - $this->mergeResult($event.'_already_exists', $name); - } elseif (is_dir($nameDirPath = $this->getSubdirPath()) && $base !== $name) { - // Rename the skin with a name which would already have a related directory could cause conflicts. - $this->mergeResult($event.'_already_exists', $nameDirPath); - } elseif (!$this->updateRow()) { - $this->mergeResult($event.'_update_failed', $base); - $locked = $base; + /** + * Update a single skin (and its related dependencies) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function update() + { + $event = $this->getEvent(); + $infos = $this->getInfos(); + $name = $infos['name']; + $base = $this->getBase(); + $callbackExtra = compact('infos', 'base'); + $done = null; + $ready = false; + + callback_event('txp.'.$event, 'update', 1, $callbackExtra); + + if (empty($name)) { + $this->mergeResult($event.'_name_invalid', $name); + } elseif (!$this->isInstalled($base)) { + $this->mergeResult($event.'_unknown', $base); + } elseif ($base !== $name && $this->isInstalled()) { + $this->mergeResult($event.'_already_exists', $name); + } elseif (is_dir($nameDirPath = $this->getSubdirPath()) && $base !== $name) { + // Rename the skin with a name which would already have a related directory could cause conflicts. + $this->mergeResult($event.'_already_exists', $nameDirPath); + } elseif (!$this->updateRow()) { + $this->mergeResult($event.'_update_failed', $base); + $locked = $base; + } else { + $this->mergeResult($event.'_updated', $name, 'success'); + $ready = true; + $locked = $base; + $baseDirPath = $this->getSubdirPath($base); + + // Rename the skin related directory to allow new updates from files. + if (is_dir($baseDirPath) && !@rename($baseDirPath, $nameDirPath)) { + $this->mergeResult('path_renaming_failed', $base, 'warning'); } else { - $this->mergeResult($event.'_updated', $name, 'success'); - $ready = true; - $locked = $base; - $baseDirPath = $this->getSubdirPath($base); - - // Rename the skin related directory to allow new updates from files. - if (is_dir($baseDirPath) && !@rename($baseDirPath, $nameDirPath)) { - $this->mergeResult('path_renaming_failed', $base, 'warning'); - } else { - $locked = $name; - } + $locked = $name; } + } - if ($ready) { - // Update skin related sections. - $sections = $this->getSections($base); + if ($ready) { + // Update skin related sections. + $sections = $this->getSections($base); - if ($sections && !$this->updateSections()) { - $this->mergeResult($event.'_related_sections_update_failed', array($base => $sections)); - } + if ($sections && !$this->updateSections()) { + $this->mergeResult($event.'_related_sections_update_failed', array($base => $sections)); + } - // update the skin_editing pref if needed. - $this->getEditing() !== $base or $this->setEditing(); + // update the skin_editing pref if needed. + $this->getEditing() !== $base or $this->setEditing(); - // Start working with the skin related assets. - $assetUpdateSet = $event." = '".doSlash($this->getName())."'"; - $assetUpdateWhere = $event." = '".doSlash($this->getBase())."'"; + // Start working with the skin related assets. + $assetUpdateSet = $event." = '".doSlash($this->getName())."'"; + $assetUpdateWhere = $event." = '".doSlash($this->getBase())."'"; - foreach ($this->getAssets() as $assetModel) { - if (!$assetModel->updateRow($assetUpdateSet, $assetUpdateWhere)) { - $assetsFailed = true; - $this->mergeResult($assetModel->getEvent().'_update_failed', $base); - } + foreach ($this->getAssets() as $assetModel) { + if (!$assetModel->updateRow($assetUpdateSet, $assetUpdateWhere)) { + $assetsFailed = true; + $this->mergeResult($assetModel->getEvent().'_update_failed', $base); } - - // If the assets related process did not failed; that is a success… - isset($assetsFailed) or $done = $name; } - callback_event('txp.'.$event, 'update', 0, $callbackExtra + compact('done')); - - return $this; // Chainable + // If the assets related process did not failed; that is a success… + isset($assetsFailed) or $done = $name; } - /** - * Duplicate multiple skins (and their related $assets) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function duplicate() - { - $event = $this->getEvent(); - $names = $this->getNames(); - $callbackExtra = compact('names'); - $ready = $done = array(); - - callback_event('txp.'.$event, 'duplicate', 1, $callbackExtra); - - foreach ($names as $name) { - $nameDirPath = $this->setName($name)->getSubdirPath(); - $copy = $name.'_copy'; - - if (!$this->isInstalled()) { - $this->mergeResult($event.'_unknown', $name); - } elseif ($this->isInstalled($copy)) { - $this->mergeResult($event.'_already_exists', $copy); - } elseif (is_dir($copyPath = $this->getSubdirPath($copy))) { - $this->mergeResult($event.'_already_exists', $copyPath); - } else { - $ready[] = $name; - } + callback_event('txp.'.$event, 'update', 0, $callbackExtra + compact('done')); + + return $this; // Chainable + } + + /** + * Duplicate multiple skins (and their related $assets) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function duplicate() + { + $event = $this->getEvent(); + $names = $this->getNames(); + $callbackExtra = compact('names'); + $ready = $done = array(); + + callback_event('txp.'.$event, 'duplicate', 1, $callbackExtra); + + foreach ($names as $name) { + $nameDirPath = $this->setName($name)->getSubdirPath(); + $copy = $name.'_copy'; + + if (!$this->isInstalled()) { + $this->mergeResult($event.'_unknown', $name); + } elseif ($this->isInstalled($copy)) { + $this->mergeResult($event.'_already_exists', $copy); + } elseif (is_dir($copyPath = $this->getSubdirPath($copy))) { + $this->mergeResult($event.'_already_exists', $copyPath); + } else { + $ready[] = $name; } + } - if ($ready) { - $rows = $this->getRows( - "*", - "name IN ('".implode("', '", array_map('doSlash', $ready))."')" - ); + if ($ready) { + $rows = $this->getRows( + "*", + "name IN ('".implode("', '", array_map('doSlash', $ready))."')" + ); - if (!$rows) { - $this->mergeResult($event.'_not_found', $ready); - } else { - foreach ($rows as $row) { - extract($row); + if (!$rows) { + $this->mergeResult($event.'_not_found', $ready); + } else { + foreach ($rows as $row) { + extract($row); - $copy = $name.'_copy'; - $copyTitle = $title.' (copy)'; + $copy = $name.'_copy'; + $copyTitle = $title.' (copy)'; - $this->setInfos($copy, $copyTitle, $version, $description, $author, $author_uri); + $this->setInfos($copy, $copyTitle, $version, $description, $author, $author_uri); - if (!$this->createRow()) { - $this->mergeResult($event.'_creation_failed', $copy); - } else { - $this->mergeResult($event.'_created', $copy, 'success'); - $this->mergeInstalled(array($copy => $copyTitle)); + if (!$this->createRow()) { + $this->mergeResult($event.'_creation_failed', $copy); + } else { + $this->mergeResult($event.'_created', $copy, 'success'); + $this->mergeInstalled(array($copy => $copyTitle)); - // Start working with the skin related assets. - foreach ($this->getAssets() as $assetModel) { - $this->setName($name); - $assetString = $assetModel->getEvent(); - $assetRows = $assetModel->getRows(); + // Start working with the skin related assets. + foreach ($this->getAssets() as $assetModel) { + $this->setName($name); + $assetString = $assetModel->getEvent(); + $assetRows = $assetModel->getRows(); - if (!$assetRows) { - $deleteExtraFiles = true; + if (!$assetRows) { + $deleteExtraFiles = true; - $this->mergeResult($assetString.'_not_found', array($skin => $nameDirPath)); - } elseif ($this->setName($copy) && !$assetModel->createRows($assetRows)) { - $deleteExtraFiles = true; + $this->mergeResult($assetString.'_not_found', array($skin => $nameDirPath)); + } elseif ($this->setName($copy) && !$assetModel->createRows($assetRows)) { + $deleteExtraFiles = true; - $this->mergeResult($assetString.'_creation_failed', $copy); - } + $this->mergeResult($assetString.'_creation_failed', $copy); } + } - $this->setName($name); // Be sure to restore the right $name. + $this->setName($name); // Be sure to restore the right $name. - // If the assets related process did not failed; that is a success… - isset($deleteExtraFiles) or $done[] = $name; - } + // If the assets related process did not failed; that is a success… + isset($deleteExtraFiles) or $done[] = $name; } } } + } - callback_event('txp.'.$event, 'duplicate', 0, $callbackExtra + compact('done')); + callback_event('txp.'.$event, 'duplicate', 0, $callbackExtra + compact('done')); - return $this; // Chainable - } + return $this; // Chainable + } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - public function import($sync = false, $override = false) - { - $event = $this->getEvent(); - $sync == $this->getSyncPref() or $this->switchSyncPref(); - $names = $this->getNames(); - $callbackExtra = compact('names'); - $done = array(); + public function import($sync = false, $override = false) + { + $event = $this->getEvent(); + $sync == $this->getSyncPref() or $this->switchSyncPref(); + $names = $this->getNames(); + $callbackExtra = compact('names'); + $done = array(); - callback_event('txp.'.$event, 'import', 1, $callbackExtra); + callback_event('txp.'.$event, 'import', 1, $callbackExtra); - foreach ($names as $name) { - $this->setName($name); - $this->setBase($name); + foreach ($names as $name) { + $this->setName($name); + $this->setBase($name); - $isInstalled = $this->isInstalled(); - $isInstalled or $sync = $override = false; // Avoid useless work. + $isInstalled = $this->isInstalled(); + $isInstalled or $sync = $override = false; // Avoid useless work. - if (!$override && $isInstalled) { - $this->mergeResult($event.'_already_exists', $name); - } elseif (!is_readable($filePath = $this->getFilePath())) { - $this->mergeResult('path_not_readable', $filePath); - } else { - $skinInfos = array_merge(array('name' => $name), $this->getFileContents()); + if (!$override && $isInstalled) { + $this->mergeResult($event.'_already_exists', $name); + } elseif (!is_readable($filePath = $this->getFilePath())) { + $this->mergeResult('path_not_readable', $filePath); + } else { + $skinInfos = array_merge(array('name' => $name), $this->getFileContents()); - if (!$skinInfos) { - $this->mergeResult('invalid_json', $filePath); - } else { - extract($skinInfos); + if (!$skinInfos) { + $this->mergeResult('invalid_json', $filePath); + } else { + extract($skinInfos); - $this->setInfos($name, $title, $version, $description, $author, $author_uri); + $this->setInfos($name, $title, $version, $description, $author, $author_uri); - if (!$override && !$this->createRow()) { - $this->mergeResult($event.'_import_failed', $name); - } elseif ($override && !$this->updateRow()) { - $this->mergeResult($event.'_import_failed', $name); - } else { - $this->mergeResult($event.'_imported', $name, 'success'); - $this->mergeInstalled(array($name => $title)); + if (!$override && !$this->createRow()) { + $this->mergeResult($event.'_import_failed', $name); + } elseif ($override && !$this->updateRow()) { + $this->mergeResult($event.'_import_failed', $name); + } else { + $this->mergeResult($event.'_imported', $name, 'success'); + $this->mergeInstalled(array($name => $title)); - // Start working with the skin related assets. - foreach ($this->getAssets() as $asset) { - $asset->import($sync, $override); + // Start working with the skin related assets. + foreach ($this->getAssets() as $asset) { + $asset->import($sync, $override); - if (is_array($asset->getMessage())) { - $assetFailed = true; + if (is_array($asset->getMessage())) { + $assetFailed = true; - $this->mergeResults($asset, array('warning', 'error')); - } + $this->mergeResults($asset, array('warning', 'error')); } } - - // If the assets related process did not failed; that is a success… - isset($assetFailed) or $done[] = $name; } + + // If the assets related process did not failed; that is a success… + isset($assetFailed) or $done[] = $name; } } + } - callback_event('txp.'.$event, 'import', 0, $callbackExtra + compact('done')); + callback_event('txp.'.$event, 'import', 0, $callbackExtra + compact('done')); - return $this; - } + return $this; + } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - public function export($sync = false, $override = false) - { - $sync == $this->getSyncPref() or $this->switchSyncPref(); + public function export($sync = false, $override = false) + { + $sync == $this->getSyncPref() or $this->switchSyncPref(); - $event = $this->getEvent(); - $names = $this->getNames(); - $callbackExtra = compact('names'); - $ready = $done = array(); + $event = $this->getEvent(); + $names = $this->getNames(); + $callbackExtra = compact('names'); + $ready = $done = array(); - callback_event('txp.'.$event, 'export', 1, $callbackExtra); + callback_event('txp.'.$event, 'export', 1, $callbackExtra); - foreach ($names as $name) { - $this->setName($name); + foreach ($names as $name) { + $this->setName($name); - $nameDirPath = $this->getSubdirPath(); + $nameDirPath = $this->getSubdirPath(); - if (!is_writable($nameDirPath)) { - $sync = false; - $override = false; - } + if (!is_writable($nameDirPath)) { + $sync = false; + $override = false; + } - if (!self::isExportable($name)) { - $this->mergeResult($event.'_unsafe_name', $name); - } elseif (!$override && is_dir($nameDirPath)) { - $this->mergeResult($event.'_already_exists', $nameDirPath); - } elseif (!is_dir($nameDirPath) && !@mkdir($nameDirPath)) { - $this->mergeResult('path_not_writable', $nameDirPath); - } else { - $ready[] = $name; - } + if (!self::isExportable($name)) { + $this->mergeResult($event.'_unsafe_name', $name); + } elseif (!$override && is_dir($nameDirPath)) { + $this->mergeResult($event.'_already_exists', $nameDirPath); + } elseif (!is_dir($nameDirPath) && !@mkdir($nameDirPath)) { + $this->mergeResult('path_not_writable', $nameDirPath); + } else { + $ready[] = $name; } + } - if ($ready) { - $rows = $this->getRows( - "*", - "name IN ('".implode("', '", array_map('doSlash', $ready))."')" - ); + if ($ready) { + $rows = $this->getRows( + "*", + "name IN ('".implode("', '", array_map('doSlash', $ready))."')" + ); - if (!$rows) { - $this->mergeResult($event.'_unknown', $names); - } else { - foreach ($rows as $row) { - extract($row); + if (!$rows) { + $this->mergeResult($event.'_unknown', $names); + } else { + foreach ($rows as $row) { + extract($row); - $this->setInfos($name, $title, $version, $description, $author, $author_uri); + $this->setInfos($name, $title, $version, $description, $author, $author_uri); - if ($this->createFile() === false) { - $this->mergeResult($event.'_export_failed', $name); - } else { - $this->mergeResult($event.'_exported', $name, 'success'); + if ($this->createFile() === false) { + $this->mergeResult($event.'_export_failed', $name); + } else { + $this->mergeResult($event.'_exported', $name, 'success'); - foreach ($this->getAssets() as $asset) { - $asset->export($sync, $override); + foreach ($this->getAssets() as $asset) { + $asset->export($sync, $override); - if (is_array($asset->getMessage())) { - $assetFailed = true; + if (is_array($asset->getMessage())) { + $assetFailed = true; - $this->mergeResults($asset, array('warning', 'error')); - } + $this->mergeResults($asset, array('warning', 'error')); } - - isset($assetFailed) or $done[] = $name; } + + isset($assetFailed) or $done[] = $name; } } } + } - callback_event('txp.'.$event, 'export', 0, $callbackExtra + compact('done')); + callback_event('txp.'.$event, 'export', 0, $callbackExtra + compact('done')); - return $this; - } + return $this; + } - /** - * Delete multiple skins (and their related $assets + directories if empty) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function delete($sync = false) - { - $event = $this->getEvent(); - $names = $this->getNames(); - $callbackExtra = compact('names'); - $ready = $done = array(); - - callback_event('txp.'.$event, 'delete', 1, $callbackExtra); - - foreach ($names as $name) { - $isDir = is_dir($this->getSubdirPath($name)); - - if (!$this->setName($name)->isInstalled()) { - $this->mergeResult($event.'_unknown', $name); - } elseif ($sections = $this->getSections()) { - $this->mergeResult($event.'_in_use', array($name => $sections)); - } else { - /** - * Start working with the skin related assets. - * Done first as assets won't be accessible - * once their parent skin will be deleted. - */ - $assetFailed = false; - - foreach ($this->getAssets() as $assetModel) { - if (!$assetModel->deleteRows()) { - $assetFailed = true; - $this->mergeResult($assetModel->getEvent().'_deletion_failed', $name); - } elseif ($sync && $isDir) { - $notDeleted = $assetModel->deleteExtraFiles(); - - if ($notDeleted) { - $this->mergeResult($assetModel->getEvent().'_files_deletion_failed', array($name => $notDeleted)); - } + /** + * Delete multiple skins (and their related $assets + directories if empty) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function delete($sync = false) + { + $event = $this->getEvent(); + $names = $this->getNames(); + $callbackExtra = compact('names'); + $ready = $done = array(); + + callback_event('txp.'.$event, 'delete', 1, $callbackExtra); + + foreach ($names as $name) { + $isDir = is_dir($this->getSubdirPath($name)); + + if (!$this->setName($name)->isInstalled()) { + $this->mergeResult($event.'_unknown', $name); + } elseif ($sections = $this->getSections()) { + $this->mergeResult($event.'_in_use', array($name => $sections)); + } else { + /** + * Start working with the skin related assets. + * Done first as assets won't be accessible + * once their parent skin will be deleted. + */ + $assetFailed = false; + + foreach ($this->getAssets() as $assetModel) { + if (!$assetModel->deleteRows()) { + $assetFailed = true; + $this->mergeResult($assetModel->getEvent().'_deletion_failed', $name); + } elseif ($sync && $isDir) { + $notDeleted = $assetModel->deleteExtraFiles(); + + if ($notDeleted) { + $this->mergeResult($assetModel->getEvent().'_files_deletion_failed', array($name => $notDeleted)); } } - - $assetFailed or $ready[] = $name; } - } - if ($ready) { - if ($this->deleteRows("name IN ('".implode("', '", array_map('doSlash', $ready))."')")) { - $done = $ready; + $assetFailed or $ready[] = $name; + } + } - $this->removeInstalled($ready); + if ($ready) { + if ($this->deleteRows("name IN ('".implode("', '", array_map('doSlash', $ready))."')")) { + $done = $ready; - if (in_array($this->getEditing(), $ready)) { - $default = $this->getDefault(); + $this->removeInstalled($ready); - !$default or $this->setEditing($default); - } + if (in_array($this->getEditing(), $ready)) { + $default = $this->getDefault(); - $this->mergeResult($event.'_deleted', $ready, 'success'); + !$default or $this->setEditing($default); + } - // Remove all skins files and directories if needed. - if ($sync) { - $notDeleted = $this->deleteFiles($ready); + $this->mergeResult($event.'_deleted', $ready, 'success'); - !$notDeleted or $this->mergeResult($event.'_files_deletion_failed', $notDeleted); - } + // Remove all skins files and directories if needed. + if ($sync) { + $notDeleted = $this->deleteFiles($ready); - update_lastmod($event.'.delete', $ready); - } else { - $this->mergeResult($event.'_deletion_failed', $ready); + !$notDeleted or $this->mergeResult($event.'_files_deletion_failed', $notDeleted); } + + update_lastmod($event.'.delete', $ready); + } else { + $this->mergeResult($event.'_deletion_failed', $ready); } + } - callback_event('txp.'.$event, 'delete', 0, $callbackExtra + compact('done')); + callback_event('txp.'.$event, 'delete', 0, $callbackExtra + compact('done')); - return $this; - } + return $this; + } - /** - * Delete Files from the $dir property value related directory. - * - * @param string $names directory/file names. - * @return bool 0 on error. - */ + /** + * Delete Files from the $dir property value related directory. + * + * @param string $names directory/file names. + * @return bool 0 on error. + */ - protected function deleteFiles($names = null) - { - $notRemoved = array(); + protected function deleteFiles($names = null) + { + $notRemoved = array(); - foreach ($names as $name) { - if (is_dir($this->getSubdirPath($name))) { - $filePath = $this->getFilePath(); + foreach ($names as $name) { + if (is_dir($this->getSubdirPath($name))) { + $filePath = $this->getFilePath(); - if (file_exists($filePath) && !unlink($filePath)) { - $notRemoved[$name][] = $filePath; - } + if (file_exists($filePath) && !unlink($filePath)) { + $notRemoved[$name][] = $filePath; + } - $subdirPath = $this->getSubdirPath(); - $isDirEmpty = self::isDirEmpty($subdirPath); + $subdirPath = $this->getSubdirPath(); + $isDirEmpty = self::isDirEmpty($subdirPath); - if (!isset($notRemoved[$name]) && ($isDirEmpty && !@rmdir($subdirPath) || !$isDirEmpty)) { - $notRemoved[$name][] = $subdirPath; - } + if (!isset($notRemoved[$name]) && ($isDirEmpty && !@rmdir($subdirPath) || !$isDirEmpty)) { + $notRemoved[$name][] = $subdirPath; } } - - return $notRemoved; } - /** - * Control the admin tab. - */ + return $notRemoved; + } - public function admin() - { - if (!defined('txpinterface')) { - die('txpinterface is undefined.'); - } + /** + * Control the admin tab. + */ - global $event, $step; - - if ($event === $this->getEvent()) { - require_privs($event); - - bouncer($step, array( - $event.'_change_pageby' => true, // Prefixed to make it work with the paginator… - 'list' => false, - 'edit' => false, - 'save' => true, - 'import' => false, - 'multi_edit' => true, - )); - - switch ($step) { - case 'save': - $infos = array_map('assert_string', psa(array( - 'name', - 'title', - 'old_name', - 'old_title', - 'version', - 'description', - 'author', - 'author_uri', - 'copy', - ))); - - extract($infos); - - if ($old_name) { - if ($copy) { - $name !== $old_name or $name .= '_copy'; - $title !== $old_title or $title .= ' (copy)'; - - $this->setInfos($name, $title, $version, $description, $author, $author_uri) - ->setBase($old_name) - ->create(); - } else { - $this->setInfos($name, $title, $version, $description, $author, $author_uri) - ->setBase($old_name) - ->update(); - } - } else { - $title !== '' or $title = ucfirst($name); - $author !== '' or $author = substr(cs('txp_login_public'), 10); - $version !== '' or $version = '0.0.1'; + public function admin() + { + if (!defined('txpinterface')) { + die('txpinterface is undefined.'); + } + + global $event, $step; + + if ($event === $this->getEvent()) { + require_privs($event); + + bouncer($step, array( + $event.'_change_pageby' => true, // Prefixed to make it work with the paginator… + 'list' => false, + 'edit' => false, + 'save' => true, + 'import' => false, + 'multi_edit' => true, + )); + + switch ($step) { + case 'save': + $infos = array_map('assert_string', psa(array( + 'name', + 'title', + 'old_name', + 'old_title', + 'version', + 'description', + 'author', + 'author_uri', + 'copy', + ))); + + extract($infos); + + if ($old_name) { + if ($copy) { + $name !== $old_name or $name .= '_copy'; + $title !== $old_title or $title .= ' (copy)'; $this->setInfos($name, $title, $version, $description, $author, $author_uri) + ->setBase($old_name) ->create(); + } else { + $this->setInfos($name, $title, $version, $description, $author, $author_uri) + ->setBase($old_name) + ->update(); } - break; - case 'multi_edit': - extract(psa(array( - 'edit_method', - 'selected', - 'sync', - ))); - - if (!$selected || !is_array($selected)) { - return; - } + } else { + $title !== '' or $title = ucfirst($name); + $author !== '' or $author = substr(cs('txp_login_public'), 10); + $version !== '' or $version = '0.0.1'; - $this->setNames(ps('selected')); - - switch ($edit_method) { - case 'export': - $this->export($sync, true); - break; - case 'duplicate': - $this->duplicate(); - break; - case 'import': - $this->import($sync, true); - break; - case 'delete': - $this->delete($sync); - break; - } - break; - case 'edit': - break; - case 'import': - $this->setNames(array(ps('skins')))->import(); - break; - case $event.'_change_pageby': - $this->getPaginator()->change(); - break; - } + $this->setInfos($name, $title, $version, $description, $author, $author_uri) + ->create(); + } + break; + case 'multi_edit': + extract(psa(array( + 'edit_method', + 'selected', + 'sync', + ))); + + if (!$selected || !is_array($selected)) { + return; + } - return $this->render($step); + $this->setNames(ps('selected')); + + switch ($edit_method) { + case 'export': + $this->export($sync, true); + break; + case 'duplicate': + $this->duplicate(); + break; + case 'import': + $this->import($sync, true); + break; + case 'delete': + $this->delete($sync); + break; + } + break; + case 'edit': + break; + case 'import': + $this->setNames(array(ps('skins')))->import(); + break; + case $event.'_change_pageby': + $this->getPaginator()->change(); + break; } + + return $this->render($step); } + } - /** - * Render (echo) the $step related admin tab. - * - * @param string $step - */ + /** + * Render (echo) the $step related admin tab. + * + * @param string $step + */ - public function render($step) - { - $message = $this->getMessage(); + public function render($step) + { + $message = $this->getMessage(); - if ($step === 'edit') { - echo $this->getEditForm($message); - } else { - echo $this->getList($message); - } + if ($step === 'edit') { + echo $this->getEditForm($message); + } else { + echo $this->getList($message); } + } - /** - * {@inheritdoc} - */ + /** + * {@inheritdoc} + */ - protected function getList($message = '') - { - $event = $this->getEvent(); - $table = $this->getTable(); + protected function getList($message = '') + { + $event = $this->getEvent(); + $table = $this->getTable(); + + pagetop(gTxt('tab_'.$event), $message); + + extract(gpsa(array( + 'page', + 'sort', + 'dir', + 'crit', + 'search_method', + ))); + + if ($sort === '') { + $sort = get_pref($event.'_sort_column', 'name'); + } else { + $sortOpts = array( + 'title', + 'version', + 'author', + 'section_count', + 'page_count', + 'form_count', + 'css_count', + 'name', + ); - pagetop(gTxt('tab_'.$event), $message); + in_array($sort, $sortOpts) or $sort = 'name'; - extract(gpsa(array( - 'page', - 'sort', - 'dir', - 'crit', - 'search_method', - ))); + set_pref($event.'_sort_column', $sort, $event, 2, '', 0, PREF_PRIVATE); + } - if ($sort === '') { - $sort = get_pref($event.'_sort_column', 'name'); - } else { - $sortOpts = array( - 'title', - 'version', - 'author', - 'section_count', - 'page_count', - 'form_count', - 'css_count', - 'name', - ); + if ($dir === '') { + $dir = get_pref($event.'_sort_dir', 'desc'); + } else { + $dir = ($dir == 'asc') ? 'asc' : 'desc'; - in_array($sort, $sortOpts) or $sort = 'name'; + set_pref($event.'_sort_dir', $dir, $event, 2, '', 0, PREF_PRIVATE); + } - set_pref($event.'_sort_column', $sort, $event, 2, '', 0, PREF_PRIVATE); - } + $searchOpts = array(); - if ($dir === '') { - $dir = get_pref($event.'_sort_dir', 'desc'); - } else { - $dir = ($dir == 'asc') ? 'asc' : 'desc'; + foreach (array('name', 'title', 'description', 'author') as $option) { + $searchOpts[$option] = array( + 'column' => $table.'.'.$option, + 'label' => gTxt($option), + ); + } - set_pref($event.'_sort_dir', $dir, $event, 2, '', 0, PREF_PRIVATE); - } + $search = $this->getSearchFilter($searchOpts); - $searchOpts = array(); + list($criteria, $crit, $search_method) = $search->getFilter(); - foreach (array('name', 'title', 'description', 'author') as $option) { - $searchOpts[$option] = array( - 'column' => $table.'.'.$option, - 'label' => gTxt($option), - ); - } + $total = $this->countRows($criteria); + $limit = $this->getPaginator()->getLimit(); - $search = $this->getSearchFilter($searchOpts); + list($page, $offset, $numPages) = pager($total, $limit, $page); - list($criteria, $crit, $search_method) = $search->getFilter(); + $table = \Txp::get('Textpattern\Admin\Table'); - $total = $this->countRows($criteria); - $limit = $this->getPaginator()->getLimit(); + return $table->render( + compact('total', 'criteria') + array('html_id' => false, 'help' => 'skin_overview'), + $this->getSearchBlock($search), + '', + $this->getCreateBlock(). // Create block is here to be loaded async. + $this->getContentBlock(compact('offset', 'limit', 'total', 'criteria', 'crit', 'search_method', 'page', 'sort', 'dir')), + $this->getFootBlock(compact('limit', 'numPages', 'total', 'crit', 'search_method', 'page', 'sort', 'dir')) + ); + } - list($page, $offset, $numPages) = pager($total, $limit, $page); + /** + * Get the admin related search form wrapped in its div. + * + * @param object $search Textpattern\Search\Filter class object. + * @return HTML + */ - $table = \Txp::get('Textpattern\Admin\Table'); + protected function getSearchBlock($search) + { + $event = $this->getEvent(); + + return n.tag( + $search->renderForm($event, array('placeholder' => 'search_skins')), + 'div', + array( + 'class' => 'txp-layout-4col-3span', + 'id' => $event.'_control', + ) + ); + } - return $table->render( - compact('total', 'criteria') + array('html_id' => false, 'help' => 'skin_overview'), - $this->getSearchBlock($search), - '', - $this->getCreateBlock(). // Create block is here to be loaded async. - $this->getContentBlock(compact('offset', 'limit', 'total', 'criteria', 'crit', 'search_method', 'page', 'sort', 'dir')), - $this->getFootBlock(compact('limit', 'numPages', 'total', 'crit', 'search_method', 'page', 'sort', 'dir')) + /** + * Get the .txp-control-panel div. + * + * @return HTML div containing the 'Create' button and the import form. + * @see getImportForm(), getCreateButton(). + */ + + protected function getCreateBlock() + { + if (has_privs($this->getEvent().'.edit')) { + return tag( + $this->getCreateButton().$this->getImportForm(), + 'div', + array('class' => 'txp-control-panel') ); } + } - /** - * Get the admin related search form wrapped in its div. - * - * @param object $search Textpattern\Search\Filter class object. - * @return HTML - */ + /** + * Get the skin import form. + * + * @return HTML The form or a message if no new skin directory is found. + */ - protected function getSearchBlock($search) - { - $event = $this->getEvent(); + protected function getImportForm() + { + $event = $this->getEvent(); + $dirPath = $this->getDirPath(); - return n.tag( - $search->renderForm($event, array('placeholder' => 'search_skins')), - 'div', - array( - 'class' => 'txp-layout-4col-3span', - 'id' => $event.'_control', - ) - ); - } + if (is_dir($dirPath) && is_writable($dirPath)) { + $new = array_diff_key($this->getUploaded(false), $this->getInstalled()); - /** - * Get the .txp-control-panel div. - * - * @return HTML div containing the 'Create' button and the import form. - * @see getImportForm(), getCreateButton(). - */ - - protected function getCreateBlock() - { - if (has_privs($this->getEvent().'.edit')) { - return tag( - $this->getCreateButton().$this->getImportForm(), - 'div', - array('class' => 'txp-control-panel') - ); - } - } + if ($new) { + asort($new); - /** - * Get the skin import form. - * - * @return HTML The form or a message if no new skin directory is found. - */ - - protected function getImportForm() - { - $event = $this->getEvent(); - $dirPath = $this->getDirPath(); - - if (is_dir($dirPath) && is_writable($dirPath)) { - $new = array_diff_key($this->getUploaded(false), $this->getInstalled()); - - if ($new) { - asort($new); - - return n - .tag_start('form', array( - 'id' => $event.'_import_form', - 'name' => $event.'_import_form', - 'method' => 'post', - 'action' => 'index.php', - )) - .tag(gTxt('import_'.$event), 'label', array('for' => $event.'_import')) - .popHelp($event.'_import') - .selectInput('skins', $new, '', true, false, 'skins') - .eInput($this->getEvent()) - .sInput('import') - .fInput('submit', '', gTxt('upload')) - .n - .tag_end('form'); - } - } else { return n - .graf( - span(null, array('class' => 'ui-icon ui-icon-alert')).' '. - gTxt('path_not_writable', array('list' => $this->getDirPath())), - array('class' => 'alert-block warning') - ); + .tag_start('form', array( + 'id' => $event.'_import_form', + 'name' => $event.'_import_form', + 'method' => 'post', + 'action' => 'index.php', + )) + .tag(gTxt('import_'.$event), 'label', array('for' => $event.'_import')) + .popHelp($event.'_import') + .selectInput('skins', $new, '', true, false, 'skins') + .eInput($this->getEvent()) + .sInput('import') + .fInput('submit', '', gTxt('upload')) + .n + .tag_end('form'); } + } else { + return n + .graf( + span(null, array('class' => 'ui-icon ui-icon-alert')).' '. + gTxt('path_not_writable', array('list' => $this->getDirPath())), + array('class' => 'alert-block warning') + ); } + } - /** - * Get the class related Admin\Paginator instance. - * - * @return object Admin\Paginator instance. - */ - - protected function getPaginator() - { - return \Txp::get('\Textpattern\Admin\Paginator', $this->getEvent(), ''); - } + /** + * Get the class related Admin\Paginator instance. + * + * @return object Admin\Paginator instance. + */ - /** - * Get the class related Search\Filter instance. - * - * @param array $methods Available search methods. - * @return object Search\Filter instance. - */ + protected function getPaginator() + { + return \Txp::get('\Textpattern\Admin\Paginator', $this->getEvent(), ''); + } - protected function getSearchFilter($methods) - { - return \Txp::get('Textpattern\Search\Filter', $this->getEvent(), $methods); - } + /** + * Get the class related Search\Filter instance. + * + * @param array $methods Available search methods. + * @return object Search\Filter instance. + */ - /** - * Get the button to create a new skin. - * - * @return HTML Link. - */ + protected function getSearchFilter($methods) + { + return \Txp::get('Textpattern\Search\Filter', $this->getEvent(), $methods); + } - protected function getCreateButton() - { - $event = $this->getEvent(); + /** + * Get the button to create a new skin. + * + * @return HTML Link. + */ - return sLink($event, 'edit', gTxt('create_'.$event), 'txp-button'); - } + protected function getCreateButton() + { + $event = $this->getEvent(); - /** - * Get the Admin\Table $content block. - * - * @param array $data compact('offset', 'limit', 'total', 'criteria', 'crit', 'search_method', 'page', 'sort', 'dir') - * @return HTML Skin list. - */ - - protected function getContentBlock($data) - { - extract($data); - - $event = $this->getEvent(); - $sortSQL = $sort.' '.$dir; - $switchDir = ($dir == 'desc') ? 'asc' : 'desc'; - - if ($total < 1) { - if ($criteria != 1) { - $out = graf( - span(null, array('class' => 'ui-icon ui-icon-info')).' '. - gTxt('no_results_found'), - array('class' => 'alert-block information') - ); - } else { - $out = graf( - span(null, array('class' => 'ui-icon ui-icon-info')).' '. - gTxt('no_'.$event.'_recorded'), - array('class' => 'alert-block error') - ); - } + return sLink($event, 'edit', gTxt('create_'.$event), 'txp-button'); + } - return $out - .n.tag_end('div') // End of .txp-layout-1col. - .n.''; // End of .txp-layout. - } + /** + * Get the Admin\Table $content block. + * + * @param array $data compact('offset', 'limit', 'total', 'criteria', 'crit', 'search_method', 'page', 'sort', 'dir') + * @return HTML Skin list. + */ - $rs = $this->getTableData($criteria, $sortSQL, $offset, $limit); - - if ($rs) { - $out = n.tag_start('form', array( - 'class' => 'multi_edit_form', - 'id' => $event.'_form', - 'name' => 'longform', - 'method' => 'post', - 'action' => 'index.php', - )) - .n.tag_start('div', array('class' => 'txp-listtables')) - .n.tag_start('table', array('class' => 'txp-list')) - .n.tag_start('thead'); - - $ths = hCell( - fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), - '', - ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' + protected function getContentBlock($data) + { + extract($data); + + $event = $this->getEvent(); + $sortSQL = $sort.' '.$dir; + $switchDir = ($dir == 'desc') ? 'asc' : 'desc'; + + if ($total < 1) { + if ($criteria != 1) { + $out = graf( + span(null, array('class' => 'ui-icon ui-icon-info')).' '. + gTxt('no_results_found'), + array('class' => 'alert-block information') ); - - $thIds = array( - 'name' => 'name', - 'title' => 'title', - 'version' => 'version', - 'author' => 'author', - 'section_count' => 'tab_sections', - 'page_count' => 'tab_pages', - 'form_count' => 'tab_forms', - 'css_count' => 'tab_style', + } else { + $out = graf( + span(null, array('class' => 'ui-icon ui-icon-info')).' '. + gTxt('no_'.$event.'_recorded'), + array('class' => 'alert-block error') ); + } - foreach ($thIds as $thId => $thVal) { - $thClass = 'txp-list-col-'.$thId - .($thId == $sort ? ' '.$dir : '') - .($thVal !== $thId ? ' '.$event.'_detail' : ''); + return $out + .n.tag_end('div') // End of .txp-layout-1col. + .n.''; // End of .txp-layout. + } - $ths .= column_head($thVal, $thId, $event, true, $switchDir, $crit, $search_method, $thClass); - } + $rs = $this->getTableData($criteria, $sortSQL, $offset, $limit); + + if ($rs) { + $out = n.tag_start('form', array( + 'class' => 'multi_edit_form', + 'id' => $event.'_form', + 'name' => 'longform', + 'method' => 'post', + 'action' => 'index.php', + )) + .n.tag_start('div', array('class' => 'txp-listtables')) + .n.tag_start('table', array('class' => 'txp-list')) + .n.tag_start('thead'); + + $ths = hCell( + fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), + '', + ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' + ); - $out .= tr($ths) - .n.tag_end('thead') - .n.tag_start('tbody'); + $thIds = array( + 'name' => 'name', + 'title' => 'title', + 'version' => 'version', + 'author' => 'author', + 'section_count' => 'tab_sections', + 'page_count' => 'tab_pages', + 'form_count' => 'tab_forms', + 'css_count' => 'tab_style', + ); - while ($a = nextRow($rs)) { - extract($a, EXTR_PREFIX_ALL, $event); + foreach ($thIds as $thId => $thVal) { + $thClass = 'txp-list-col-'.$thId + .($thId == $sort ? ' '.$dir : '') + .($thVal !== $thId ? ' '.$event.'_detail' : ''); - $editUrl = array( - 'event' => $event, - 'step' => 'edit', - 'name' => $skin_name, - 'sort' => $sort, - 'dir' => $dir, - 'page' => $page, - 'search_method' => $search_method, - 'crit' => $crit, - ); + $ths .= column_head($thVal, $thId, $event, true, $switchDir, $crit, $search_method, $thClass); + } - $tdAuthor = txpspecialchars($skin_author); + $out .= tr($ths) + .n.tag_end('thead') + .n.tag_start('tbody'); + + while ($a = nextRow($rs)) { + extract($a, EXTR_PREFIX_ALL, $event); + + $editUrl = array( + 'event' => $event, + 'step' => 'edit', + 'name' => $skin_name, + 'sort' => $sort, + 'dir' => $dir, + 'page' => $page, + 'search_method' => $search_method, + 'crit' => $crit, + ); - empty($skin_author_uri) or $tdAuthor = href($tdAuthor, $skin_author_uri); + $tdAuthor = txpspecialchars($skin_author); - $tds = td(fInput('checkbox', 'selected[]', $skin_name), '', 'txp-list-col-multi-edit') - .hCell( - href(txpspecialchars($skin_name), $editUrl, array('title' => gTxt('edit'))), - '', - array( - 'scope' => 'row', - 'class' => 'txp-list-col-name', - ) - ) - .td(txpspecialchars($skin_title), '', 'txp-list-col-title') - .td(txpspecialchars($skin_version), '', 'txp-list-col-version') - .td($tdAuthor, '', 'txp-list-col-author'); - - $countNames = array('section', 'page', 'form', 'css'); - - foreach ($countNames as $name) { - if (${$event.'_'.$name.'_count'} > 0) { - if ($name === 'section') { - $linkParams = array( - 'event' => 'section', - 'search_method' => $event, - 'crit' => '"'.$skin_name.'"', - ); - } else { - $linkParams = array( - 'event' => $name, - $event => $skin_name, - ); - } + empty($skin_author_uri) or $tdAuthor = href($tdAuthor, $skin_author_uri); - $tdVal = href( - ${$event.'_'.$name.'_count'}, - $linkParams, - array( - 'title' => gTxt( - $event.'_count_'.$name, - array('{num}' => ${$event.'_'.$name.'_count'}) - ) - ) + $tds = td(fInput('checkbox', 'selected[]', $skin_name), '', 'txp-list-col-multi-edit') + .hCell( + href(txpspecialchars($skin_name), $editUrl, array('title' => gTxt('edit'))), + '', + array( + 'scope' => 'row', + 'class' => 'txp-list-col-name', + ) + ) + .td(txpspecialchars($skin_title), '', 'txp-list-col-title') + .td(txpspecialchars($skin_version), '', 'txp-list-col-version') + .td($tdAuthor, '', 'txp-list-col-author'); + + $countNames = array('section', 'page', 'form', 'css'); + + foreach ($countNames as $name) { + if (${$event.'_'.$name.'_count'} > 0) { + if ($name === 'section') { + $linkParams = array( + 'event' => 'section', + 'search_method' => $event, + 'crit' => '"'.$skin_name.'"', ); } else { - $tdVal = 0; + $linkParams = array( + 'event' => $name, + $event => $skin_name, + ); } - $tds .= td($tdVal, '', 'txp-list-col-'.$name.'_count'); + $tdVal = href( + ${$event.'_'.$name.'_count'}, + $linkParams, + array( + 'title' => gTxt( + $event.'_count_'.$name, + array('{num}' => ${$event.'_'.$name.'_count'}) + ) + ) + ); + } else { + $tdVal = 0; } - $out .= tr($tds, array('id' => $this->getTable().'_'.$skin_name)); + $tds .= td($tdVal, '', 'txp-list-col-'.$name.'_count'); } - return $out - .n.tag_end('tbody') - .n.tag_end('table') - .n.tag_end('div'); + $out .= tr($tds, array('id' => $this->getTable().'_'.$skin_name)); } + + return $out + .n.tag_end('tbody') + .n.tag_end('table') + .n.tag_end('div'); } + } - /** - * Get the Admin\Table $foot block. - * - * @param array $data compact('limit', 'numPages', 'total', 'crit', 'search_method', 'page', 'sort', 'dir') - * @return HTML Multi-edit form, pagination and navigation form. - */ + /** + * Get the Admin\Table $foot block. + * + * @param array $data compact('limit', 'numPages', 'total', 'crit', 'search_method', 'page', 'sort', 'dir') + * @return HTML Multi-edit form, pagination and navigation form. + */ - protected function getFootBlock($data) - { - extract($data); + protected function getFootBlock($data) + { + extract($data); - return self::getMultiEditForm($page, $sort, $dir, $crit, $search_method) - .$this->getPaginator()->render() - .nav_form($this->getEvent(), $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit); - } + return self::getMultiEditForm($page, $sort, $dir, $crit, $search_method) + .$this->getPaginator()->render() + .nav_form($this->getEvent(), $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit); + } - /** - * Render a multi-edit form widget. - * - * @param int $page The current page number - * @param string $sort The current sorting value - * @param string $dir The current sorting direction - * @param string $crit The current search criteria - * @param string $search_method The current search method - * @return HTML - */ - - protected function getMultiEditForm($page, $sort, $dir, $crit, $search_method) - { - $event = $this->getEvent(); - $pref = 'synchronize'; - - $sync = checkbox2('sync', get_pref($pref, true), 0, 'sync') - .n.tag(gtxt($event.'_'.$pref), 'label', array('for' => 'sync')) - .popHelp($event.'_'.$pref); - - $methods = array( - 'import' => array('label' => gTxt('override'), 'html' => $sync), - 'duplicate' => gTxt('duplicate'), - 'export' => array('label' => gTxt('export'), 'html' => $sync), - 'delete' => array('label' => gTxt('delete'), 'html' => $sync), - ); + /** + * Render a multi-edit form widget. + * + * @param int $page The current page number + * @param string $sort The current sorting value + * @param string $dir The current sorting direction + * @param string $crit The current search criteria + * @param string $search_method The current search method + * @return HTML + */ + + protected function getMultiEditForm($page, $sort, $dir, $crit, $search_method) + { + $event = $this->getEvent(); + $pref = 'synchronize'; - return multi_edit($methods, $this->getEvent(), 'multi_edit', $page, $sort, $dir, $crit, $search_method); - } + $sync = checkbox2('sync', get_pref($pref, true), 0, 'sync') + .n.tag(gtxt($event.'_'.$pref), 'label', array('for' => 'sync')) + .popHelp($event.'_'.$pref); - /** - * Get the edit form. - * - * @param mixed $message - * @return HTML - */ + $methods = array( + 'import' => array('label' => gTxt('override'), 'html' => $sync), + 'duplicate' => gTxt('duplicate'), + 'export' => array('label' => gTxt('export'), 'html' => $sync), + 'delete' => array('label' => gTxt('delete'), 'html' => $sync), + ); - protected function getEditForm($message = '') - { - global $step; + return multi_edit($methods, $this->getEvent(), 'multi_edit', $page, $sort, $dir, $crit, $search_method); + } - $event = $this->getEvent(); + /** + * Get the edit form. + * + * @param mixed $message + * @return HTML + */ - require_privs($event.'.edit'); + protected function getEditForm($message = '') + { + global $step; - !$message or pagetop(gTxt('tab_'.$event), $message); + $event = $this->getEvent(); - extract(gpsa(array( - 'page', - 'sort', - 'dir', - 'crit', - 'search_method', - 'name', - ))); + require_privs($event.'.edit'); - $fields = array('name', 'title', 'version', 'description', 'author', 'author_uri'); + !$message or pagetop(gTxt('tab_'.$event), $message); - if ($name) { - $rs = $this->setName($name)->getRow(); + extract(gpsa(array( + 'page', + 'sort', + 'dir', + 'crit', + 'search_method', + 'name', + ))); - if (!$rs) { - return $this->main(); - } + $fields = array('name', 'title', 'version', 'description', 'author', 'author_uri'); - $caption = gTxt('edit_'.$event); - $extraAction = href( - ' '.gTxt('duplicate'), - '#', - array( - 'class' => 'txp-clone', - 'data-form' => $event.'_form', - ) - ); - } else { - $rs = array_fill_keys($fields, ''); - $caption = gTxt('create_'.$event); - $extraAction = ''; + if ($name) { + $rs = $this->setName($name)->getRow(); + + if (!$rs) { + return $this->main(); } - extract($rs, EXTR_PREFIX_ALL, $event); - pagetop(gTxt('tab_'.$event)); + $caption = gTxt('edit_'.$event); + $extraAction = href( + ' '.gTxt('duplicate'), + '#', + array( + 'class' => 'txp-clone', + 'data-form' => $event.'_form', + ) + ); + } else { + $rs = array_fill_keys($fields, ''); + $caption = gTxt('create_'.$event); + $extraAction = ''; + } - $content = hed($caption, 2); + extract($rs, EXTR_PREFIX_ALL, $event); + pagetop(gTxt('tab_'.$event)); - foreach ($fields as $field) { - $current = ${$event.'_'.$field}; + $content = hed($caption, 2); - if ($field === 'description') { - $input = text_area($field, 0, 0, $current, $event.'_'.$field); - } elseif ($field === 'name') { - $input = ''; - } else { - $type = ($field === 'author_uri') ? 'url' : 'text'; - $input = fInput($type, $field, $current, '', '', '', INPUT_REGULAR, '', $event.'_'.$field); - } + foreach ($fields as $field) { + $current = ${$event.'_'.$field}; - $content .= inputLabel($event.'_'.$field, $input, $event.'_'.$field, $event.'_'.$field); + if ($field === 'description') { + $input = text_area($field, 0, 0, $current, $event.'_'.$field); + } elseif ($field === 'name') { + $input = ''; + } else { + $type = ($field === 'author_uri') ? 'url' : 'text'; + $input = fInput($type, $field, $current, '', '', '', INPUT_REGULAR, '', $event.'_'.$field); } - $content .= pluggable_ui($event.'_ui', 'extend_detail_form', '', $rs) - .graf( - $extraAction. - sLink($event, '', gTxt('cancel'), 'txp-button') - .fInput('submit', '', gTxt('save'), 'publish'), - array('class' => 'txp-edit-actions') - ) - .eInput($event) - .sInput('save') - .hInput('old_name', $skin_name) - .hInput('old_title', $skin_title) - .hInput('search_method', $search_method) - .hInput('crit', $crit) - .hInput('page', $page) - .hInput('sort', $sort) - .hInput('dir', $dir); - - return form($content, '', '', 'post', 'txp-edit', '', $event.'_form'); + $content .= inputLabel($event.'_'.$field, $input, $event.'_'.$field, $event.'_'.$field); } + + $content .= pluggable_ui($event.'_ui', 'extend_detail_form', '', $rs) + .graf( + $extraAction. + sLink($event, '', gTxt('cancel'), 'txp-button') + .fInput('submit', '', gTxt('save'), 'publish'), + array('class' => 'txp-edit-actions') + ) + .eInput($event) + .sInput('save') + .hInput('old_name', $skin_name) + .hInput('old_title', $skin_title) + .hInput('search_method', $search_method) + .hInput('crit', $crit) + .hInput('page', $page) + .hInput('sort', $sort) + .hInput('dir', $dir); + + return form($content, '', '', 'post', 'txp-edit', '', $event.'_form'); } } diff --git a/textpattern/vendors/Textpattern/Skin/SkinInterface.php b/textpattern/vendors/Textpattern/Skin/SkinInterface.php index ede2ff0b26..a8c2327afc 100644 --- a/textpattern/vendors/Textpattern/Skin/SkinInterface.php +++ b/textpattern/vendors/Textpattern/Skin/SkinInterface.php @@ -30,129 +30,128 @@ * @package Skin */ -namespace Textpattern\Skin { - - interface SkinInterface - { - /** - * $dirPath property setter. - * - * @param string $path Custom skin directory path. - * Builds the path from the 'path_to_site' + 'skin_dir' if null. - * @return string $this->dirPath - */ - - public function setDirPath($path = null); - - /** - * $assets property setter. - * - * @param array $pages Page names to work with; - * @param array $forms Form names to work with; - * @param array $styles CSS names to work with. - * @return object $this The current class object (chainable). - */ - - public function setAssets($pages = null, $forms = null, $styles = null); - - /** - * $infos and $name properties setter. - * - * @param string $name Skin name; - * @param string $title Skin title; - * @param string $version Skin version; - * @param string $description Skin description; - * @param string $author Skin author; - * @param string $author_uri Skin author URL; - * @return object $this The current class object (chainable). - */ - - public function setInfos( - $name, - $title = null, - $version = null, - $description = null, - $author = null, - $author_uri = null - ); - - /** - * Get a $dir property value related subdirectory path. - * - * @param string $name Directory(/skin) name (default: $this->getName()). - * @return string The Path - */ - - public function getSubdirPath($name = null); - - /** - * Update the txp_section table. - * - * @param string $set The SET clause (default: "skin = '".doSlash($this->getName())."'") - * @param string $where The WHERE clause (default: "skin = '".doSlash($this->getBase())."'") - * @return bool FALSE on error. - */ - - public function updateSections($set = null, $where = null); - - /** - * $uploaded property getter. - * - * @param bool $expanded Set it to false to get a simple associative - * array of skin names and their titles. - * @return array - */ - - public function getUploaded($expanded = true); - - /** - * Create/CreateFrom a single skin (and its related assets) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function create(); - - /** - * Update a single skin (and its related dependencies) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function update(); - - /** - * Duplicate multiple skins (and their related $assets) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function duplicate(); - - /** - * Delete multiple skins (and their related $assets + directories if empty) - * Merges results in the related property. - * - * @return object $this The current object (chainable). - */ - - public function delete($sync = false); - - /** - * Control the admin tab. - */ - - public function admin(); - - /** - * Render (echo) the $step related admin tab. - * - * @param string $step - */ - - public function render($step); - } +namespace Textpattern\Skin; + +interface SkinInterface +{ + /** + * $dirPath property setter. + * + * @param string $path Custom skin directory path. + * Builds the path from the 'path_to_site' + 'skin_dir' if null. + * @return string $this->dirPath + */ + + public function setDirPath($path = null); + + /** + * $assets property setter. + * + * @param array $pages Page names to work with; + * @param array $forms Form names to work with; + * @param array $styles CSS names to work with. + * @return object $this The current class object (chainable). + */ + + public function setAssets($pages = null, $forms = null, $styles = null); + + /** + * $infos and $name properties setter. + * + * @param string $name Skin name; + * @param string $title Skin title; + * @param string $version Skin version; + * @param string $description Skin description; + * @param string $author Skin author; + * @param string $author_uri Skin author URL; + * @return object $this The current class object (chainable). + */ + + public function setInfos( + $name, + $title = null, + $version = null, + $description = null, + $author = null, + $author_uri = null + ); + + /** + * Get a $dir property value related subdirectory path. + * + * @param string $name Directory(/skin) name (default: $this->getName()). + * @return string The Path + */ + + public function getSubdirPath($name = null); + + /** + * Update the txp_section table. + * + * @param string $set The SET clause (default: "skin = '".doSlash($this->getName())."'") + * @param string $where The WHERE clause (default: "skin = '".doSlash($this->getBase())."'") + * @return bool FALSE on error. + */ + + public function updateSections($set = null, $where = null); + + /** + * $uploaded property getter. + * + * @param bool $expanded Set it to false to get a simple associative + * array of skin names and their titles. + * @return array + */ + + public function getUploaded($expanded = true); + + /** + * Create/CreateFrom a single skin (and its related assets) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function create(); + + /** + * Update a single skin (and its related dependencies) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function update(); + + /** + * Duplicate multiple skins (and their related $assets) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function duplicate(); + + /** + * Delete multiple skins (and their related $assets + directories if empty) + * Merges results in the related property. + * + * @return object $this The current object (chainable). + */ + + public function delete($sync = false); + + /** + * Control the admin tab. + */ + + public function admin(); + + /** + * Render (echo) the $step related admin tab. + * + * @param string $step + */ + + public function render($step); }