diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6c31a04..24356341b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -* **Build 22x** (2015-03-xx) +* **Build 226** (2015-03-16) - Form Tabs now support specifying a default tab using the `defaultTab` option (see Backend > Forms docs). - - Improved the Theme management features: Edit properties, download, duplicate and delete. + - Improved the Theme management features: Edit properties, import, export, duplicate and delete. * **Build 222** (2015-03-11) - Form fields can now use a simpler interface for using the Input preset converter (see Backend > Forms docs). diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index 17b58ce59..d960a8189 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -997,7 +997,7 @@ this.hide() var indicator=$('
') indicator.append($('
').text(this.options.text)) indicator.append($('')) -if(this.options.opaque!==undefined&&this.options.opaque){indicator.addClass('is-opaque')} +if(this.options.opaque!==undefined){indicator.addClass('is-opaque')} this.$el.prepend(indicator) this.$el.addClass('in-progress') this.tally++} diff --git a/modules/backend/assets/js/october.loadindicator.js b/modules/backend/assets/js/october.loadindicator.js index 3d45d8c11..9a87d2575 100644 --- a/modules/backend/assets/js/october.loadindicator.js +++ b/modules/backend/assets/js/october.loadindicator.js @@ -44,7 +44,7 @@ var indicator = $('
') indicator.append($('
').text(this.options.text)) indicator.append($('')) - if (this.options.opaque !== undefined && this.options.opaque) { + if (this.options.opaque !== undefined) { indicator.addClass('is-opaque') } diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php index 3fba8270a..7638d61a8 100644 --- a/modules/backend/classes/Controller.php +++ b/modules/backend/classes/Controller.php @@ -317,7 +317,8 @@ class Controller extends Extendable // Execute the action $result = call_user_func_array([$this, $actionName], $parameters); - if ($result instanceof RedirectResponse) { + // Expecting \Response and \RedirectResponse + if ($result instanceof \Symfony\Component\HttpFoundation\Response) { return $result; } @@ -402,10 +403,10 @@ class Controller extends Extendable */ if ($result instanceof RedirectResponse) { $responseContents['X_OCTOBER_REDIRECT'] = $result->getTargetUrl(); + } /* * No redirect is used, look for any flash messages */ - } elseif (Flash::check()) { $responseContents['#layout-flash-messages'] = $this->makeLayoutPartial('flash_messages'); } diff --git a/modules/backend/classes/NavigationManager.php b/modules/backend/classes/NavigationManager.php index 1e9dc3769..17f1cd620 100644 --- a/modules/backend/classes/NavigationManager.php +++ b/modules/backend/classes/NavigationManager.php @@ -226,7 +226,7 @@ class NavigationManager $this->items[$itemKey] = $item; if ($sideMenu !== null) { - $this->addSideMenuItems($sideMenu); + $this->addSideMenuItems($owner, $code, $sideMenu); } } diff --git a/modules/cms/assets/css/october.theme-selector.css b/modules/cms/assets/css/october.theme-selector.css index 6cf246c36..0b2f99cbb 100644 --- a/modules/cms/assets/css/october.theme-selector.css +++ b/modules/cms/assets/css/october.theme-selector.css @@ -95,6 +95,10 @@ .theme-selector-layout .layout-row.links .theme-description { border-bottom: 1px solid #f2f3f4; } +.theme-selector-layout .create-new-theme { + margin-bottom: 10px; +} +.theme-selector-layout .create-new-theme, .theme-selector-layout .find-more-themes { background: #ecf0f1; color: #2b3e50; @@ -105,6 +109,7 @@ -moz-border-radius: 4px; border-radius: 4px; } +.theme-selector-layout .create-new-theme:hover, .theme-selector-layout .find-more-themes:hover { background: #1795f1; color: white; diff --git a/modules/cms/assets/less/october.theme-selector.less b/modules/cms/assets/less/october.theme-selector.less index ee4bc042b..7e0a6cb23 100644 --- a/modules/cms/assets/less/october.theme-selector.less +++ b/modules/cms/assets/less/october.theme-selector.less @@ -112,6 +112,11 @@ } } + .create-new-theme { + margin-bottom: 10px; + } + + .create-new-theme, .find-more-themes { background: #ecf0f1; color: #2b3e50; diff --git a/modules/cms/classes/Theme.php b/modules/cms/classes/Theme.php index b66c65cc5..01bdc3780 100644 --- a/modules/cms/classes/Theme.php +++ b/modules/cms/classes/Theme.php @@ -287,7 +287,7 @@ class Theme public function writeConfig($values = [], $overwrite = false) { if (!$overwrite) { - $values = $values + $this->getConfig(); + $values = $values + (array) $this->getConfig(); } $path = $this->getPath().'/theme.yaml'; diff --git a/modules/cms/classes/theme/fields.yaml b/modules/cms/classes/theme/fields.yaml index 408ee75fd..09d6df74f 100644 --- a/modules/cms/classes/theme/fields.yaml +++ b/modules/cms/classes/theme/fields.yaml @@ -7,33 +7,41 @@ tabs: fields: name: - label: Name - placeholder: New theme name + label: cms::lang.theme.name_label + placeholder: cms::lang.theme.name_create_placeholder span: auto + required: true attributes: default-focus: 1 - directory_name: - label: Directory name + dir_name@create: + label: cms::lang.theme.dir_name_label + placeholder: cms::lang.theme.dir_name_create_label + span: auto + preset: name + required: true + + dir_name@update: + label: cms::lang.theme.dir_name_label disabled: true span: auto description: - label: Description - placeholder: Theme description + label: cms::lang.theme.description_label + placeholder: cms::lang.theme.description_placeholder type: textarea size: tiny author: - label: Author - placeholder: Person or company name + label: cms::lang.theme.author_label + placeholder: cms::lang.theme.author_placeholder span: auto homepage: - label: Homepage - placeholder: Website URL + label: cms::lang.theme.homepage_label + placeholder: cms::lang.theme.homepage_placeholder span: auto code: - label: Code - placeholder: A unique code for this theme used for distribution + label: cms::lang.theme.code_label + placeholder: cms::lang.theme.code_placeholder diff --git a/modules/cms/controllers/Themes.php b/modules/cms/controllers/Themes.php index 7baf9f946..4815dc37d 100644 --- a/modules/cms/controllers/Themes.php +++ b/modules/cms/controllers/Themes.php @@ -1,15 +1,20 @@ pageTitle = 'cms::lang.theme.settings_menu'; BackendMenu::setContext('October.System', 'system', 'settings'); SettingsManager::setContext('October.Cms', 'theme'); + + /* + * Enable AJAX for Form widgets + */ + if (post('mode') == 'import') { + $this->makeImportFormWidget($this->findThemeObject())->bindToController(); + } } public function index() @@ -57,41 +69,153 @@ class Themes extends Controller ]; } + public function index_onDelete() + { + $theme = $this->findThemeObject(); + + if ($theme->isActiveTheme()) { + throw new ApplicationException(trans('cms::lang.theme.delete_active_theme_failed')); + } + + $themePath = $theme->getPath(); + if (File::isDirectory($themePath)) { + File::deleteDirectory($themePath); + } + + Flash::success(trans('cms::lang.theme.delete_theme_success')); + return Redirect::refresh(); + } + // // Theme properties // - public function index_onLoadThemeFieldsForm() + public function index_onLoadFieldsForm() { $theme = $this->findThemeObject(); - $this->vars['widget'] = $this->makeThemeFieldsFormWidget($theme); + $this->vars['widget'] = $this->makeFieldsFormWidget($theme); $this->vars['themeDir'] = $theme->getDirName(); return $this->makePartial('theme_fields_form'); } - public function index_onSaveThemeFields() + public function index_onSaveFields() { $theme = $this->findThemeObject(); - $widget = $this->makeThemeFieldsFormWidget($theme); + $widget = $this->makeFieldsFormWidget($theme); $theme->writeConfig($widget->getSaveData()); return ['#themeListItem-'.$theme->getId() => $this->makePartial('theme_list_item', ['theme' => $theme])]; } - protected function makeThemeFieldsFormWidget($theme) + protected function makeFieldsFormWidget($theme) { $widgetConfig = $this->makeConfig('~/modules/cms/classes/theme/fields.yaml'); $widgetConfig->alias = 'form'.studly_case($theme->getDirName()); $widgetConfig->model = $theme; $widgetConfig->data = $theme->getConfig(); - $widgetConfig->data['directory_name'] = $theme->getDirName(); + $widgetConfig->data['dir_name'] = $theme->getDirName(); $widgetConfig->arrayName = 'Theme'; + $widgetConfig->context = 'update'; $widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig); return $widget; } + // + // Create theme + // + + public function index_onLoadCreateForm() + { + $this->vars['widget'] = $this->makeCreateFormWidget(); + return $this->makePartial('theme_create_form'); + } + + public function index_onCreate() + { + $widget = $this->makeCreateFormWidget(); + $data = $widget->getSaveData(); + $newDirName = trim(array_get($data, 'dir_name')); + $destinationPath = themes_path().'/'.$newDirName; + + $data = array_except($data, 'dir_name'); + + if (!strlen(trim(array_get($data, 'name')))) { + throw new ValidationException(['name' => trans('cms::lang.theme.create_theme_required_name')]); + } + + if (!preg_match('/^[a-z0-9\_\-]+$/i', $newDirName)) { + throw new ValidationException(['dir_name' => trans('cms::lang.theme.dir_name_invalid')]); + } + + if (File::isDirectory($destinationPath)) { + throw new ValidationException(['dir_name' => trans('cms::lang.theme.dir_name_taken')]); + } + + File::makeDirectory($destinationPath); + File::makeDirectory($destinationPath.'/assets'); + File::makeDirectory($destinationPath.'/content'); + File::makeDirectory($destinationPath.'/layouts'); + File::makeDirectory($destinationPath.'/pages'); + File::makeDirectory($destinationPath.'/partials'); + File::put($destinationPath.'/theme.yaml', ''); + + $theme = CmsTheme::load($newDirName); + $theme->writeConfig($data); + + Flash::success(trans('cms::lang.theme.create_theme_success')); + return Redirect::refresh(); + } + + protected function makeCreateFormWidget() + { + $widgetConfig = $this->makeConfig('~/modules/cms/classes/theme/fields.yaml'); + $widgetConfig->alias = 'formCreateTheme'; + $widgetConfig->model = new CmsTheme; + $widgetConfig->arrayName = 'Theme'; + $widgetConfig->context = 'create'; + + $widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig); + return $widget; + } + + // + // Duplicate + // + + public function index_onLoadDuplicateForm() + { + $theme = $this->findThemeObject(); + $this->vars['themeDir'] = $theme->getDirName(); + + return $this->makePartial('theme_duplicate_form'); + } + + public function index_onDuplicateTheme() + { + $theme = $this->findThemeObject(); + $newDirName = trim(post('new_dir_name')); + $sourcePath = $theme->getPath(); + $destinationPath = themes_path().'/'.$newDirName; + + if (!preg_match('/^[a-z0-9\_\-]+$/i', $newDirName)) { + throw new ValidationException(['new_dir_name' => trans('cms::lang.theme.dir_name_invalid')]); + } + + if (File::isDirectory($destinationPath)) { + throw new ValidationException(['new_dir_name' => trans('cms::lang.theme.dir_name_taken')]); + } + + File::copyDirectory($sourcePath, $destinationPath); + $newTheme = CmsTheme::load($newDirName); + $newName = $newTheme->getConfigValue('name') . ' - Copy'; + $newTheme->writeConfig(['name' => $newName]); + + Flash::success(trans('cms::lang.theme.duplicate_theme_success')); + return Redirect::refresh(); + } + // // Theme customization // @@ -149,6 +273,90 @@ class Themes extends Controller } } + // + // Theme export + // + + public function index_onLoadExportForm() + { + $theme = $this->findThemeObject(); + $this->vars['widget'] = $this->makeExportFormWidget($theme); + $this->vars['themeDir'] = $theme->getDirName(); + + return $this->makePartial('theme_export_form'); + } + + public function index_onExport() + { + $theme = $this->findThemeObject(); + $widget = $this->makeExportFormWidget($theme); + + $model = new ThemeExport; + $file = $model->export($theme, $widget->getSaveData()); + + return Backend::redirect('cms/themes/download/'.$file.'/'.$theme->getDirName().'.zip'); + } + + public function download($name, $outputName = null) + { + try { + $this->pageTitle = 'Download theme export archive'; + return ThemeExport::download($name, $outputName); + } + catch (Exception $ex) { + $this->handleError($ex); + } + } + + protected function makeExportFormWidget($theme) + { + $widgetConfig = $this->makeConfig('~/modules/cms/models/themeexport/fields.yaml'); + $widgetConfig->alias = 'form'.studly_case($theme->getDirName()); + $widgetConfig->model = new ThemeExport; + $widgetConfig->model->theme = $theme; + $widgetConfig->arrayName = 'ThemeExport'; + + $widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig); + return $widget; + } + + // + // Theme import + // + + public function index_onLoadImportForm() + { + $theme = $this->findThemeObject(); + $this->vars['widget'] = $this->makeImportFormWidget($theme); + $this->vars['themeDir'] = $theme->getDirName(); + + return $this->makePartial('theme_import_form'); + } + + public function index_onImport() + { + $theme = $this->findThemeObject(); + $widget = $this->makeImportFormWidget($theme); + + $model = new ThemeImport; + $model->import($theme, $widget->getSaveData(), $widget->getSessionKey()); + + Flash::success(trans('cms::lang.theme.import_theme_success')); + return Redirect::refresh(); + } + + protected function makeImportFormWidget($theme) + { + $widgetConfig = $this->makeConfig('~/modules/cms/models/themeimport/fields.yaml'); + $widgetConfig->alias = 'form'.studly_case($theme->getDirName()); + $widgetConfig->model = new ThemeImport; + $widgetConfig->model->theme = $theme; + $widgetConfig->arrayName = 'ThemeImport'; + + $widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig); + return $widget; + } + // // Helpers // diff --git a/modules/cms/controllers/themes/_theme_create_form.htm b/modules/cms/controllers/themes/_theme_create_form.htm new file mode 100644 index 000000000..5ec6184b7 --- /dev/null +++ b/modules/cms/controllers/themes/_theme_create_form.htm @@ -0,0 +1,54 @@ + 'themeCreateForm', + 'data-popup-load-indicator' => true +]) ?> + + + + fatalError): ?> + + + + + + + + + + + + + + diff --git a/modules/cms/controllers/themes/_theme_duplicate_form.htm b/modules/cms/controllers/themes/_theme_duplicate_form.htm new file mode 100644 index 000000000..bcaa7512a --- /dev/null +++ b/modules/cms/controllers/themes/_theme_duplicate_form.htm @@ -0,0 +1,75 @@ + 'themeDuplicateForm', + 'data-popup-load-indicator' => true, +]) ?> + + + + + + fatalError): ?> + + + + + + + + + + + + + + diff --git a/modules/cms/controllers/themes/_theme_export_form.htm b/modules/cms/controllers/themes/_theme_export_form.htm new file mode 100644 index 000000000..8d3e77873 --- /dev/null +++ b/modules/cms/controllers/themes/_theme_export_form.htm @@ -0,0 +1,63 @@ + 'themeExportForm', + 'data-popup-load-indicator' => true, + 'data-request-success' => 'closeExportThemePopup()' +]) ?> + + + + + + fatalError): ?> + + + + + + + + + + + + + + diff --git a/modules/cms/controllers/themes/_theme_fields_form.htm b/modules/cms/controllers/themes/_theme_fields_form.htm index 39586b2a9..eb8ea1e56 100644 --- a/modules/cms/controllers/themes/_theme_fields_form.htm +++ b/modules/cms/controllers/themes/_theme_fields_form.htm @@ -1,4 +1,4 @@ - 'themeFieldsForm', 'data-popup-load-indicator' => true ]) ?> @@ -7,7 +7,7 @@ fatalError): ?> @@ -19,7 +19,7 @@ + + + + fatalError): ?> + + + + + + + + + + + + + + diff --git a/modules/cms/controllers/themes/_theme_list.htm b/modules/cms/controllers/themes/_theme_list.htm index a221e4b92..f28a3f298 100644 --- a/modules/cms/controllers/themes/_theme_list.htm +++ b/modules/cms/controllers/themes/_theme_list.htm @@ -14,6 +14,15 @@
+ + + - Manage + -
diff --git a/modules/cms/controllers/themes/download.htm b/modules/cms/controllers/themes/download.htm new file mode 100644 index 000000000..e67bf3bd4 --- /dev/null +++ b/modules/cms/controllers/themes/download.htm @@ -0,0 +1,13 @@ + + + + +fatalError): ?> + +

fatalError) ?>

+

+ + \ No newline at end of file diff --git a/modules/cms/controllers/themes/update.htm b/modules/cms/controllers/themes/update.htm index 104ad9e44..ac53f7f41 100644 --- a/modules/cms/controllers/themes/update.htm +++ b/modules/cms/controllers/themes/update.htm @@ -54,6 +54,6 @@

fatalError) ?>

-

Return to themes list

+

\ No newline at end of file diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php index f046c6f8d..3fe002fe2 100644 --- a/modules/cms/lang/en/lang.php +++ b/modules/cms/lang/en/lang.php @@ -25,10 +25,57 @@ return [ ], 'settings_menu' => 'Front-end theme', 'settings_menu_description' => 'Preview the list of installed themes and select an active theme.', - 'find_more_themes' => 'Find more themes on OctoberCMS Theme Marketplace.', + 'name_label' => 'Name', + 'name_create_placeholder' => 'New theme name', + 'author_label' => 'Author', + 'author_placeholder' => 'Person or company name', + 'description_label' => 'Description', + 'description_placeholder' => 'Theme description', + 'homepage_label' => 'Homepage', + 'homepage_placeholder' => 'Website URL', + 'code_label' => 'Code', + 'code_placeholder' => 'A unique code for this theme used for distribution', + 'dir_name_label' => 'Directory name', + 'dir_name_create_label' => 'The destination theme directory', + 'theme_label' => 'Theme', 'activate_button' => 'Activate', 'active_button' => 'Activate', - 'customize_button' => 'Customize' + 'customize_button' => 'Customize', + 'duplicate_button' => 'Duplicate', + 'duplicate_title' => 'Duplicate theme', + 'duplicate_theme_success' => 'Duplicated theme successfully!', + 'manage_button' => 'Manage', + 'manage_title' => 'Manage theme', + 'edit_properties_title' => 'Theme', + 'edit_properties_button' => 'Edit properties', + 'save_properties' => 'Save properties', + 'import_button' => 'Import', + 'import_title' => 'Import theme', + 'import_theme_success' => 'Imported theme successfully!', + 'import_uploaded_file' => 'Theme archive file', + 'import_overwrite_label' => 'Overwrite existing files', + 'import_overwrite_comment' => 'Untick this box to only import new files', + 'import_folders_label' => 'Folders', + 'import_folders_comment' => 'Please select the theme folders you would like to import', + 'export_button' => 'Export', + 'export_title' => 'Export theme', + 'export_folders_label' => 'Folders', + 'export_folders_comment' => 'Please select the theme folders you would like to import', + 'delete_button' => 'Delete', + 'delete_confirm' => 'Are you sure you want to delete this theme? It cannot be undone!', + 'delete_active_theme_failed' => 'Cannot delete the active theme, try making another theme active first.', + 'delete_theme_success' => 'Deleted theme successfully!', + 'create_title' => 'Create theme', + 'create_button' => 'Create', + 'create_new_blank_theme' => 'Create a new blank theme', + 'create_theme_success' => 'Created theme successfully!', + 'create_theme_required_name' => 'Please specify a name for the theme.', + 'new_directory_name_label' => 'Theme directory', + 'new_directory_name_comment' => 'Provide a new directory name for the duplicated theme.', + 'dir_name_invalid' => 'Name can contain only digits, Latin letters and the following symbols: _-', + 'dir_name_taken' => 'Desired theme directory already exists.', + 'find_more_themes' => 'Find more themes on OctoberCMS Theme Marketplace', + 'return' => 'Return to themes list', ], 'maintenance' => [ 'settings_menu' => 'Maintenance mode', diff --git a/modules/cms/models/ThemeExport.php b/modules/cms/models/ThemeExport.php new file mode 100644 index 000000000..dc9247f25 --- /dev/null +++ b/modules/cms/models/ThemeExport.php @@ -0,0 +1,141 @@ + null, + 'themeName' => null, + 'dirName' => null, + 'folders' => [ + 'assets' => true, + 'pages' => true, + 'layouts' => true, + 'partials' => true, + 'content' => true, + ] + ]; + + public function getFoldersOptions() + { + return [ + 'assets' => 'Assets', + 'pages' => 'Pages', + 'layouts' => 'Layouts', + 'partials' => 'Partials', + 'content' => 'Content', + ]; + } + + public function setThemeAttribute($theme) + { + if (!$theme instanceof CmsTheme) return; + + $this->attributes['themeName'] = $theme->getConfigValue('name', $theme->getDirName()); + $this->attributes['dirName'] = $theme->getDirName(); + $this->attributes['theme'] = $theme; + } + + public function export($theme, $data = []) + { + $this->theme = $theme; + $this->fill($data); + + try { + $themePath = $this->theme->getPath(); + $tempPath = temp_path() . '/'.uniqid('oc'); + $zipName = uniqid('oc'); + $zipPath = temp_path().'/'.$zipName; + + if (!@mkdir($tempPath)) + throw new ApplicationException('Unable to create directory '.$tempPath); + + if (!@mkdir($metaPath = $tempPath . '/meta')) + throw new ApplicationException('Unable to create directory '.$metaPath); + + File::copy($themePath.'/theme.yaml', $tempPath.'/theme.yaml'); + File::copyDirectory($themePath.'/meta', $metaPath); + + foreach ($this->folders as $folder) { + if (!array_key_exists($folder, $this->getFoldersOptions())) continue; + File::copyDirectory($themePath.'/'.$folder, $tempPath.'/'.$folder); + } + + Zip::make($zipPath, $tempPath); + File::deleteDirectory($tempPath); + } + catch (Exception $ex) { + + if (strlen($tempPath) && File::isDirectory($tempPath)) { + File::deleteDirectory($tempPath); + } + + if (strlen($zipPath) && File::isFile($zipPath)) { + File::delete($zipPath); + } + + throw $ex; + } + + return $zipName; + } + + public static function download($name, $outputName = null) + { + if (!preg_match('/^oc[0-9a-z]*$/i', $name)) { + throw new ApplicationException('File not found'); + } + + $zipPath = temp_path() . '/' . $name; + if (!file_exists($zipPath)) { + throw new ApplicationException('File not found'); + } + + $headers = Response::download($zipPath, $outputName)->headers->all(); + $result = Response::make(File::get($zipPath), 200, $headers); + + @unlink($zipPath); + + return $result; + } + +} \ No newline at end of file diff --git a/modules/cms/models/ThemeImport.php b/modules/cms/models/ThemeImport.php new file mode 100644 index 000000000..5e9fa034f --- /dev/null +++ b/modules/cms/models/ThemeImport.php @@ -0,0 +1,184 @@ + ['System\Models\File'] + ]; + + /** + * @var array Make the model's attributes public so behaviors can modify them. + */ + public $attributes = [ + 'theme' => null, + 'themeName' => null, + 'dirName' => null, + 'overwrite' => true, + 'folders' => [ + 'assets' => true, + 'pages' => true, + 'layouts' => true, + 'partials' => true, + 'content' => true, + ] + ]; + + public function getFoldersOptions() + { + return [ + 'assets' => 'Assets', + 'pages' => 'Pages', + 'layouts' => 'Layouts', + 'partials' => 'Partials', + 'content' => 'Content', + ]; + } + + public function setThemeAttribute($theme) + { + if (!$theme instanceof CmsTheme) return; + + $this->attributes['themeName'] = $theme->getConfigValue('name', $theme->getDirName()); + $this->attributes['dirName'] = $theme->getDirName(); + $this->attributes['theme'] = $theme; + } + + public function import($theme, $data = [], $sessionKey = null) + { + @set_time_limit(3600); + + $this->theme = $theme; + $this->fill($data); + + try + { + $file = $this->uploaded_file()->withDeferred($sessionKey)->first(); + if (!$file) { + throw new ApplicationException('There is no file attached to import!'); + } + + $themePath = $this->theme->getPath(); + $tempPath = temp_path() . '/'.uniqid('oc'); + $zipName = uniqid('oc'); + $zipPath = temp_path().'/'.$zipName; + + File::put($zipPath, $file->getContents()); + + if (!@mkdir($tempPath)) + throw new ApplicationException('Unable to create directory '.$tempPath); + + Zip::extract($zipPath, $tempPath); + + // if (File::isFile($tempPath.'/theme.yaml')) { + // File::copy($tempPath.'/theme.yaml', $themePath.'/theme.yaml'); + // } + + if (File::isDirectory($tempPath.'/meta')) { + $this->copyDirectory($tempPath.'/meta', $themePath.'/meta'); + } + + foreach ($this->folders as $folder) { + if (!array_key_exists($folder, $this->getFoldersOptions())) continue; + $this->copyDirectory($tempPath.'/'.$folder, $themePath.'/'.$folder); + } + + File::deleteDirectory($tempPath); + File::delete($zipPath); + $file->delete(); + } + catch (Exception $ex) { + + if (!empty($tempPath) && File::isDirectory($tempPath)) { + File::deleteDirectory($tempPath); + } + + if (!empty($zipPath) && File::isFile($zipPath)) { + File::delete($zipPath); + } + + throw $ex; + } + } + + /** + * Helper for copying directories that supports the ability + * to not overwrite existing files. Inherited from File::copyDirectory + * + * @param string $directory + * @param string $destination + * @return bool + */ + protected function copyDirectory($directory, $destination) + { + // Preference is to overwrite existing files + if ($this->overwrite) { + return File::copyDirectory($directory, $destination); + } + + if (!File::isDirectory($directory)) return false; + + $options = FilesystemIterator::SKIP_DOTS; + + if (!File::isDirectory($destination)) { + File::makeDirectory($destination, 0777, true); + } + + $items = new FilesystemIterator($directory, $options); + + foreach ($items as $item) { + $target = $destination.'/'.$item->getBasename(); + + if ($item->isDir()) { + $path = $item->getPathname(); + + if (!$this->copyDirectory($path, $target)) return false; + } + else { + // Do not overwrite existing files + if (File::isFile($target)) continue; + + if (!File::copy($item->getPathname(), $target)) return false; + } + } + + return true; + } + +} \ No newline at end of file diff --git a/modules/cms/models/themeexport/fields.yaml b/modules/cms/models/themeexport/fields.yaml new file mode 100644 index 000000000..5a9db2ced --- /dev/null +++ b/modules/cms/models/themeexport/fields.yaml @@ -0,0 +1,14 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + themeName: + label: cms::lang.theme.theme_label + disabled: true + + folders: + label: cms::lang.theme.export_folders_label + commentAbove: cms::lang.theme.export_folders_comment + type: checkboxlist diff --git a/modules/cms/models/themeimport/fields.yaml b/modules/cms/models/themeimport/fields.yaml new file mode 100644 index 000000000..bc996eb94 --- /dev/null +++ b/modules/cms/models/themeimport/fields.yaml @@ -0,0 +1,25 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + themeName: + label: cms::lang.theme.theme_label + disabled: true + + uploaded_file: + label: cms::lang.theme.import_uploaded_file + type: fileupload + mode: file + fileTypes: zip + + overwrite: + label: cms::lang.theme.import_overwrite_label + comment: cms::lang.theme.import_overwrite_comment + type: checkbox + + folders: + label: cms::lang.theme.import_folders_label + commentAbove: cms::lang.theme.import_folders_comment + type: checkboxlist diff --git a/modules/system/lang/hu/lang.php b/modules/system/lang/hu/lang.php index 1e240e7df..4d016d31b 100644 --- a/modules/system/lang/hu/lang.php +++ b/modules/system/lang/hu/lang.php @@ -13,6 +13,7 @@ return [ 'fa' => 'Perzsa', 'fr' => 'Francia', 'hu' => 'Magyar', + 'id' => 'Indonéz', 'it' => 'Olasz', 'ja' => 'Japán', 'nl' => 'Holland', @@ -160,7 +161,7 @@ return [ ], 'updates' => [ 'title' => 'Frissítések kezelése', - 'name' => 'Szoftverfrissítés', + 'name' => 'Szoftver frissítése', 'menu_label' => 'Frissítések', 'menu_description' => 'A rendszer és a bővítmények frissítése, valamint új kiegészítők telepítése.', 'check_label' => 'Frissítések keresése', @@ -173,9 +174,10 @@ return [ 'core_build_old' => 'Jelenlegi verzió: :build', 'core_build_new' => 'Verzió: :build', 'core_build_new_help' => 'Elérhető a legújabb hivatalos kiadás.', - 'core_downloading' => 'Alkalmazásfájlok letöltése...', - 'core_extracting' => 'Alkalmazásfájlok kicsomagolása...', + 'core_downloading' => 'Alkalmazás fájlok letöltése...', + 'core_extracting' => 'Alkalmazás fájlok kicsomagolása...', 'plugins' => 'Bővítmények', + 'disabled' => 'Letiltva', 'plugin_downloading' => 'Bővítmény letöltése: :name', 'plugin_extracting' => 'Bővítmény kicsomagolása: :name', 'plugin_version_none' => 'Új bővítmény',