diff --git a/README.md b/README.md index f11d60e..0322e70 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,10 @@ This plugin is designed to simplify theme customizations without the need of man - Many possibilities of use (grids, animations, content properties, ...) - Clear representation in the backend - Categories and Groups - - Combine and output as tabs [![new](https://img.shields.io/badge/-new-brightgreen?style=flat-square)](#manage-categories) -- Passing variables to the template [![new](https://img.shields.io/badge/-new-brightgreen?style=flat-square)](#passing-style-group-variables-to-a-template) - - Formatting output using predefined methods or your own [![new](https://img.shields.io/badge/-new-brightgreen?style=flat-square)](#passing-style-group-variables-to-a-template) + - Combine and output as tabs +- Passing variables to the template + - Formatting output using predefined methods or your own +- Import / Export [![new](https://img.shields.io/badge/-new-83aa0e?style=flat-square)](#import--export) [![new](https://img.shields.io/badge/-BETA-F38041?style=flat-square)](#import--export) - Available for - Layouts - Pages @@ -24,8 +25,8 @@ This plugin is designed to simplify theme customizations without the need of man - Content-Elements - Forms - Form-Fields - - News [![new](https://img.shields.io/badge/-new-brightgreen?style=flat-square)](#contao-component-style-manager) - - Events [![new](https://img.shields.io/badge/-new-brightgreen?style=flat-square)](#contao-component-style-manager) + - News + - Events - Third-Party plugin support - Rocksolid Custom Elements @@ -119,6 +120,13 @@ $this->styleManager->prepare('myCategoryIdentifier', ['alias1'])->format("data-s ##### Backend view ![Passing Variables: Image 1](https://www.oveleon.de/share/github-assets/contao-component-style-manager/2.0/template-vars-list.png) +## Import / Export +To fill projects with a default setting, the Import and Export functions are available. + +When importing, the categories as well as the CSS groups are only added additively. This allows CSS classes to be added to the actual project without being deleted after an import. + +> Please note that the import completes the records by the identifier (categories) and the alias (CSS groups). So if the aliases are changed in the current project, they are not overwritten / added, but a new group is created after the import. + ## Support Rocksolid Custom Elements see: [Rocksolid Custom Elements](https://github.com/madeyourday/contao-rocksolid-custom-elements) diff --git a/src/Resources/contao/classes/StyleManager.php b/src/Resources/contao/classes/StyleManager.php index 0813f0b..0d484e2 100644 --- a/src/Resources/contao/classes/StyleManager.php +++ b/src/Resources/contao/classes/StyleManager.php @@ -291,7 +291,9 @@ public static function serializeValues($varValue, $strTable){ } // Remove unused classes - $arrValue = array_filter($varValue); + $arrValue = array_filter($varValue, function($v){ + return $v !== false && !is_null($v) && ($v != '' || $v == '0'); + }); // Rebuild array for template variables while($objStyleGroups->next()) diff --git a/src/Resources/contao/classes/Styles.php b/src/Resources/contao/classes/Styles.php index c7f1fe7..fd087c8 100644 --- a/src/Resources/contao/classes/Styles.php +++ b/src/Resources/contao/classes/Styles.php @@ -118,7 +118,7 @@ public function format($format, $method='') { foreach ($this->styles[ $this->currIdentifier ] as $alias => $arrVariable) { - if($value = $this->getGroupValue($arrVariable)) + if(($value = $this->getGroupValue($arrVariable)) !== '') { $arrValues[ $alias ] = $this->parseValueType($value); } @@ -129,7 +129,7 @@ public function format($format, $method='') { foreach ($this->currGroups as $alias) { - if($value = $this->getGroupValue($this->styles[ $this->currIdentifier ][ $alias ])) + if(($value = $this->getGroupValue($this->styles[ $this->currIdentifier ][ $alias ])) !== '') { $arrValues[ $alias ] = $this->parseValueType($value); } diff --git a/src/Resources/contao/classes/Sync.php b/src/Resources/contao/classes/Sync.php new file mode 100644 index 0000000..e847180 --- /dev/null +++ b/src/Resources/contao/classes/Sync.php @@ -0,0 +1,455 @@ +strRootDir = \System::getContainer()->getParameter('kernel.project_dir'); + } + + /** + * Import + * + * @return string + * + * @throws \Exception + */ + public function importStyleManager() + { + \Config::set('uploadTypes', \Config::get('uploadTypes') . ',xml'); + + /** @var \FileUpload $objUploader */ + $objUploader = new \FileUpload(); + + if (\Input::post('FORM_SUBMIT') == 'tl_style_manager_import') + { + if (!\Input::post('confirm')) + { + $arrUploaded = $objUploader->uploadTo('system/tmp'); + + if (empty($arrUploaded)) + { + \Message::addError($GLOBALS['TL_LANG']['ERR']['all_fields']); + $this->reload(); + } + + $arrFiles = array(); + + foreach ($arrUploaded as $strFile) + { + // Skip folders + if (is_dir($this->strRootDir . '/' . $strFile)) + { + \Message::addError(sprintf($GLOBALS['TL_LANG']['ERR']['importFolder'], basename($strFile))); + continue; + } + + $objFile = new \File($strFile); + + // Skip anything but .xml files + if ($objFile->extension != 'xml') + { + \Message::addError(sprintf($GLOBALS['TL_LANG']['ERR']['filetype'], $objFile->extension)); + continue; + } + + $arrFiles[] = $strFile; + } + } + + // Check whether there are any files + if (empty($arrFiles)) + { + \Message::addError($GLOBALS['TL_LANG']['ERR']['all_fields']); + $this->reload(); + } + + $this->importStyleManagerFile($arrFiles); + } + + // Return the form + return \Message::generate() . ' +
+' . $GLOBALS['TL_LANG']['MSC']['backBT'] . ' +
+
+
+ + + + +
+
+

' . $GLOBALS['TL_LANG']['tl_style_manager_archive']['source'][0] . '

' . $objUploader->generateMarkup() . (isset($GLOBALS['TL_LANG']['tl_style_manager_archive']['source'][1]) ? ' +

' . $GLOBALS['TL_LANG']['tl_style_manager_archive']['source'][1] . '

' : '') . ' +
+
+ +
+ +
+ +
+ +
+ +
+
'; + } + + /** + * Import StyleManager data from file + * + * @param $arrFiles + * + * @throws \Exception + */ + public function importStyleManagerFile(array $arrFiles) + { + foreach ($arrFiles as $strFilePath) + { + $objFile = new \File($strFilePath); + + // Load the XML file + if ($objFile->exists()) + { + $xml = new \DOMDocument(); + $xml->preserveWhiteSpace = false; + $xml->loadXML($objFile->getContent()); + + // Continue if there is no XML file + if (!$xml instanceof \DOMDocument) + { + \Message::addError(sprintf($GLOBALS['TL_LANG']['tl_theme']['missing_xml'], basename($strFilePath))); + continue; + } + + $archives = $xml->getElementsByTagName('archives'); + + if($archives->count()){ + // Skip archives node + $archives = $archives->item(0)->childNodes; + } + else + { + return; + } + + // Lock the tables + $arrLocks = array + ( + 'tl_style_manager_archive' => 'WRITE', + 'tl_style_manager' => 'WRITE' + ); + + // Load the DCAs of the locked tables + foreach (array_keys($arrLocks) as $table) + { + $this->loadDataContainer($table); + } + + $this->Database->lockTables($arrLocks); + + // Get the current auto_increment values + $intArchiveId = $this->Database->getNextId('tl_style_manager_archive'); + + // Check if archive exists + $archiveExists = function (string $identifier) : bool + { + return $this->Database->prepare("SELECT identifier FROM tl_style_manager_archive WHERE identifier=?")->execute($identifier)->numRows > 0; + }; + + // Check if children exists + $childrenExists = function (string $alias, string $pid) : bool + { + return $this->Database->prepare("SELECT alias FROM tl_style_manager WHERE alias=? AND pid=?")->execute($alias, $pid)->numRows > 0; + }; + + // Loop through the archives + for ($i=0; $i<$archives->length; $i++) + { + $archive = $archives->item($i)->childNodes; + $identifier = $archives->item($i)->getAttribute('identifier'); + + if(!$archiveExists($identifier)) + { + $objArchive = new StyleManagerArchiveModel(); + $objArchive->id = ++$intArchiveId; + } + else + { + $objArchive = StyleManagerArchiveModel::findByIdentifier($identifier); + } + + // Loop through the archives fields + for ($a=0; $a<$archive->length; $a++) + { + $strField = $archive->item($a)->nodeName; + + if($strField === 'field') + { + $strName = $archive->item($a)->getAttribute('title'); + $strValue = $archive->item($a)->nodeValue; + + if($strName === 'id') + { + continue; + } + + $objArchive->{$strName} = $strValue; + } + elseif($strField === 'children') + { + $children = $archive->item($a)->childNodes; + + // Loop through the archives fields + for ($c=0; $c<$children->length; $c++) + { + $alias = $children->item($c)->getAttribute('alias'); + $fields = $children->item($c)->childNodes; + + if(!$childrenExists($alias, $objArchive->id)) + { + $objChildren = new StyleManagerModel(); + } + else + { + $objChildren = StyleManagerModel::findByAliasAndPid($alias, $objArchive->id); + } + + // Loop through the children fields + for ($f=0; $f<$fields->length; $f++) + { + $strName = $fields->item($f)->getAttribute('title'); + $strValue = $fields->item($f)->nodeValue; + + if($strName === 'id' || !$strValue || strtolower($strValue) === 'null') + { + continue; + } + + switch($strName) + { + case 'pid': + $strValue = $objArchive->id; + break; + case 'cssClasses': + if($objChildren->{$strName}) + { + $arrClasses = \StringUtil::deserialize($objChildren->{$strName}, true); + $arrExists = $this->flattenKeyValueArray($arrClasses); + $arrValues = \StringUtil::deserialize($strValue, true); + + foreach($arrValues as $cssClass) + { + if(array_key_exists($cssClass['key'], $arrExists)) + { + continue; + } + + $arrClasses[] = array( + 'key' => $cssClass['key'], + 'value' => $cssClass['value'] + ); + } + + $strValue = serialize($arrClasses); + } + + break; + case 'modules': + case 'formFields': + case 'contentElements': + $arrElements = \StringUtil::deserialize($objChildren->{$strName}, true); + $arrValues = \StringUtil::deserialize($strValue, true); + + foreach($arrValues as $element) + { + if(in_array($element, $arrElements)) + { + continue; + } + + $arrElements[] = $element; + } + + $strValue = serialize($arrElements); + break; + } + + $objChildren->{$strName} = $strValue; + } + + // Save children data + $objChildren->save(); + } + } + } + + // Save archive data + $objArchive->save(); + } + + // Unlock the tables + $this->Database->unlockTables(); + } + } + } + + /** + * Export StyleManager data + * + * @param \DataContainer $dc + * + * @throws \Exception + */ + public function exportStyleManager(\DataContainer $dc) + { + // Create a new XML document + $xml = new \DOMDocument('1.0', 'UTF-8'); + $xml->formatOutput = true; + + // Archives + $objArchive = StyleManagerArchiveModel::findAll(['order' => 'groupAlias,sorting']); + + // Root element + $archives = $xml->createElement('archives'); + $archives = $xml->appendChild($archives); + + // Add the archives + while($objArchive->next()) + { + $this->addArchiveData($xml, $archives, $objArchive); + } + + // Generate temp name + $strTmp = md5(uniqid(mt_rand(), true)); + + // Create file and open the "save as …" dialogue + $objFile = new \File('system/tmp/' . $strTmp); + $objFile->write($xml->saveXML()); + $objFile->close(); + + $objFile->sendToBrowser('style-manager-export.xml'); + } + + /** + * Add an archive data row to the XML document + * + * @param \DOMDocument $xml + * @param \DOMNode $archives + * @param \Model\Collection $objArchive + */ + protected function addArchiveData(\DOMDocument $xml, \DOMNode $archives, \Model\Collection $objArchive) + { + $this->loadDataContainer('tl_style_manager_archive'); + + // Add archive node + $row = $xml->createElement('archive'); + $row->setAttribute('identifier', $objArchive->identifier); + $row = $archives->appendChild($row); + + // Add field data + $this->addRowData($xml, $row, $objArchive->row()); + + // Add children data + $this->addChildrenData($xml, $row, $objArchive->id); + } + + /** + * Add a children data row to the XML document + * + * @param \DOMDocument $xml + * @param \DOMNode|\DOMElement $archive + * @param int $pid + */ + protected function addChildrenData(\DOMDocument $xml, \DOMElement $archive, int $pid) + { + // Add children node + $children = $xml->createElement('children'); + $children = $archive->appendChild($children); + + $objChildren = StyleManagerModel::findByPid($pid); + + if($objChildren === null) + { + return; + } + + $this->loadDataContainer('tl_style_manager'); + + while($objChildren->next()) + { + $row = $xml->createElement('child'); + $row->setAttribute('alias', $objChildren->alias); + $row = $children->appendChild($row); + + // Add field data + $this->addRowData($xml, $row, $objChildren->row()); + } + } + + /** + * Add field data to the XML document + * + * @param \DOMDocument $xml + * @param \DOMNode $row + * @param array $arrData + */ + protected function addRowData(\DOMDocument $xml, \DOMNode $row, array $arrData) + { + foreach ($arrData as $k=>$v) + { + $field = $xml->createElement('field'); + $field->setAttribute('title', $k); + $field = $row->appendChild($field); + + if ($v === null) + { + $v = 'NULL'; + } + + $value = $xml->createTextNode($v); + $field->appendChild($value); + } + } + + /** + * Flatten Key Value Array + * + * @param $arr + * + * @return array + */ + public function flattenKeyValueArray($arr) + { + if(empty($arr)) + { + return $arr; + } + + $arrTmp = array(); + + foreach ($arr as $item) { + $arrTmp[ $item['key'] ] = $item['value']; + } + + return $arrTmp; + } +} diff --git a/src/Resources/contao/config/config.php b/src/Resources/contao/config/config.php index 8036943..3dee0ba 100644 --- a/src/Resources/contao/config/config.php +++ b/src/Resources/contao/config/config.php @@ -12,7 +12,9 @@ ( 'style_manager' => array ( - 'tables' => array('tl_style_manager_archive', 'tl_style_manager') + 'tables' => array('tl_style_manager_archive', 'tl_style_manager'), + 'export' => array('\\Oveleon\\ContaoComponentStyleManager\\Sync', 'exportStyleManager'), + 'import' => array('\\Oveleon\\ContaoComponentStyleManager\\Sync', 'importStyleManager'), ) ) )); diff --git a/src/Resources/contao/dca/tl_style_manager_archive.php b/src/Resources/contao/dca/tl_style_manager_archive.php index ec9e671..cfe47e8 100644 --- a/src/Resources/contao/dca/tl_style_manager_archive.php +++ b/src/Resources/contao/dca/tl_style_manager_archive.php @@ -47,6 +47,18 @@ ), 'global_operations' => array ( + 'import' => array + ( + 'href' => 'key=import', + 'class' => 'header_style_manager_import', + 'icon' => 'theme_import.svg' + ), + 'export' => array + ( + 'href' => 'key=export', + 'class' => 'header_style_manager_export', + 'icon' => 'theme_export.svg' + ), 'all' => array ( 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], diff --git a/src/Resources/contao/languages/de/tl_form_field.xlf b/src/Resources/contao/languages/de/tl_form_field.xlf new file mode 100644 index 0000000..338fe6a --- /dev/null +++ b/src/Resources/contao/languages/de/tl_form_field.xlf @@ -0,0 +1,16 @@ + + + + + + Style Manager + Style Manager + + + + Style Manager + Style Manager + + + + diff --git a/src/Resources/contao/languages/de/tl_style_manager_archive.xlf b/src/Resources/contao/languages/de/tl_style_manager_archive.xlf index 7cd7293..acfec3e 100644 --- a/src/Resources/contao/languages/de/tl_style_manager_archive.xlf +++ b/src/Resources/contao/languages/de/tl_style_manager_archive.xlf @@ -14,6 +14,22 @@ Add category Neue Kategorie + + Import + Importieren + + + Export + Exportieren + + + Source files + Quelldateien + + + Here you can upload one .xml file to be imported. + Hier können Sie eine .xml-Datei für den Import hochladen. + Edit category Kategorie bearbeiten diff --git a/src/Resources/contao/languages/en/tl_form_field.xlf b/src/Resources/contao/languages/en/tl_form_field.xlf new file mode 100644 index 0000000..d39e8c8 --- /dev/null +++ b/src/Resources/contao/languages/en/tl_form_field.xlf @@ -0,0 +1,14 @@ + + + + + + Style Manager + + + + Style Manager + + + + diff --git a/src/Resources/contao/languages/en/tl_style_manager_archive.xlf b/src/Resources/contao/languages/en/tl_style_manager_archive.xlf index 214b816..0eb6fdc 100644 --- a/src/Resources/contao/languages/en/tl_style_manager_archive.xlf +++ b/src/Resources/contao/languages/en/tl_style_manager_archive.xlf @@ -11,6 +11,18 @@ Add category + + Import + + + Export + + + Source files + + + Here you can upload one .xml file to be imported. + Edit category diff --git a/src/Resources/contao/models/StyleManagerModel.php b/src/Resources/contao/models/StyleManagerModel.php index 6fce210..11d34ff 100644 --- a/src/Resources/contao/models/StyleManagerModel.php +++ b/src/Resources/contao/models/StyleManagerModel.php @@ -129,4 +129,30 @@ public static function findByTable($strTable, array $arrOptions=array()) return null; } } + + /** + * Find one item by alias by their parent ID + * + * @param $alias + * @param $pid + * @param array $arrOptions + * + * @return StyleManagerModel|null + */ + public static function findByAliasAndPid($alias, $pid, $arrOptions=array()) + { + $t = static::$strTable; + + $arrColumns = array( + "$t.alias=?", + "$t.pid=?" + ); + + $arrValues = array( + $alias, + $pid + ); + + return static::findOneBy($arrColumns, $arrValues, $arrOptions); + } }