#120 Theme import

This commit is contained in:
Daniel Brendel
2024-03-05 12:03:02 +01:00
parent a6e28b5641
commit ff2a1ce8a5
9 changed files with 147 additions and 5 deletions
+1
View File
@@ -91,6 +91,7 @@ return [
array('/admin/media/background', 'POST', 'admin@upload_media_background'),
array('/admin/media/overlay/alpha', 'POST', 'admin@save_overlay_alpha'),
array('/admin/mail/save', 'POST', 'admin@save_mail_settings'),
array('/admin/themes/import', 'POST', 'admin@import_theme'),
array('/admin/cronjob/token', 'POST', 'admin@generate_cronjob_token'),
/** Cronjob Controller */
+23
View File
@@ -405,4 +405,27 @@ class AdminController extends BaseController {
]);
}
}
/**
* Handles URL: /admin/themes/import
*
* @param Asatru\Controller\ControllerArg $request
* @return Asatru\View\JsonHandler
*/
public function import_theme($request)
{
try {
$themes = ThemeModule::startImport();
return json([
'code' => 200,
'themes' => $themes
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => $e->getMessage()
]);
}
}
}
+3 -1
View File
@@ -266,5 +266,7 @@ return [
'all_in_good_standing' => 'Alles Ok',
'theme' => 'Theme',
'themes' => 'Themes',
'confirm_generate_new_token' => 'Soll ein neues Token erstellt werden? Das vorherige Token wird dadurch ungültig.'
'confirm_generate_new_token' => 'Soll ein neues Token erstellt werden? Das vorherige Token wird dadurch ungültig.',
'themes_hint' => 'Hier können neue Themes importiert werden. Wähle das ZIP-Archiv mit deinem Theme aus und sobald der Import erfolgreich war, ist das Theme dann für alle im Arbeitsraum verfügbar. Hinweis: Falls das ZIP-Archiv mehrere Themes enthält, wird das System versuchen, alle zu importieren.',
'theme_import_successful' => 'Der Import war erfolgreich. Importierte Themes: {count}'
];
+3 -1
View File
@@ -266,5 +266,7 @@ return [
'all_in_good_standing' => 'All fine',
'theme' => 'Theme',
'themes' => 'Themes',
'confirm_generate_new_token' => 'Generating a new token will invalidate the previous one. Do you want to proceed?'
'confirm_generate_new_token' => 'Generating a new token will invalidate the previous one. Do you want to proceed?',
'themes_hint' => 'Here you can import themes. Select a zip-file of your theme you want to import and once import succeeded, the theme will be available to all users. Note: If the zip-archive contains multiple themes, then the system will try to import all of them.',
'theme_import_successful' => 'The import was successful. Imported themes: {count}'
];
+50
View File
@@ -119,4 +119,54 @@ class ThemeModule {
throw $e;
}
}
/**
* @return array
* @throws \Exception
*/
public static function startImport()
{
try {
if ((!isset($_FILES['theme'])) || ($_FILES['theme']['error'] !== UPLOAD_ERR_OK) || (strpos($_FILES['theme']['type'], 'zip') === false)) {
throw new \Exception('Failed to upload file or invalid file uploaded');
}
$result = [];
$import_file = 'theme_import_' . date('Y-m-d_H-i-s');
move_uploaded_file($_FILES['theme']['tmp_name'], public_path() . '/themes/' . $import_file . '.zip');
$zip = new ZipArchive();
if ($zip->open(public_path() . '/themes/' . $import_file . '.zip')) {
$zip->extractTo(public_path() . '/themes/' . $import_file);
$zip->close();
$folders = scandir(public_path() . '/themes/' . $import_file);
foreach ($folders as $folder) {
if (substr($folder, 0, 1) !== '.') {
if (!is_dir(public_path() . '/themes/' . $folder)) {
rename(public_path() . '/themes/' . $import_file . '/' . $folder, public_path() . '/themes/' . $folder);
static::load(public_path() . '/themes/' . $folder);
if (!static::ready()) {
throw new \Exception('Failed to load theme: ' . $folder);
}
$result[] = $folder;
}
}
}
UtilsModule::clearFolder(public_path() . '/themes/' . $import_file);
}
unlink(public_path() . '/themes/' . $import_file . '.zip');
return $result;
} catch (\Exception $e) {
throw $e;
}
}
}
+21 -1
View File
@@ -669,7 +669,7 @@ window.vue = new Vue({
},
selectAdminTab: function(tab) {
const tabs = ['environment', 'media', 'users', 'locations', 'mail', 'backup', 'info'];
const tabs = ['environment', 'media', 'users', 'locations', 'mail', 'themes', 'backup', 'info'];
let selEl = document.querySelector('.admin-' + tab);
if (selEl) {
@@ -804,6 +804,26 @@ window.vue = new Vue({
});
},
startThemeImport: function(file, button) {
let oldText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>&nbsp;' + oldText;
let formData = new FormData();
formData.append('theme', file.files[0]);
window.vue.ajaxRequest('post', window.location.origin + '/admin/themes/import', formData, function(response) {
button.innerHTML = oldText;
if (response.code == 200) {
let import_result = document.getElementById('themes-import-result');
if (import_result) {
import_result.innerText = import_result.innerText.replace('{count}', response.themes.length);
import_result.classList.remove('is-hidden');
}
}
});
},
copyToClipboard: function(text) {
const el = document.createElement('textarea');
el.value = text;
+23
View File
@@ -2027,6 +2027,29 @@ fieldset .field {
background-color: rgb(50, 50, 50);
}
.admin-themes p {
color: rgb(150, 150, 150);
margin-bottom: 30px;
font-size: 1.2em;
}
.admin-themes input, .admin-themes select {
color: rgb(150, 150, 150);
background-color: rgb(50, 50, 50);
}
.admin-themes-result {
position: relative;
padding: 10px;
border-radius: 5px;
border: 1px solid rgb(255, 255, 255);
background-color: rgb(185, 255, 164);
}
.admin-themes-result i {
color: rgb(50, 50, 50);
}
.admin-backup label, .admin-backup span {
color: rgb(150, 150, 150);
}
+21
View File
@@ -9,6 +9,7 @@
<li class="admin-tab-users {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'users')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.selectAdminTab('users');">{{ __('app.users') }}</a></li>
<li class="admin-tab-locations {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'locations')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.selectAdminTab('locations');">{{ __('app.locations') }}</a></li>
<li class="admin-tab-mail {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'mail')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.selectAdminTab('mail');">{{ __('app.mail') }}</a></li>
<li class="admin-tab-themes {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'themes')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.selectAdminTab('themes');">{{ __('app.themes') }}</a></li>
<li class="admin-tab-backup {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'backup')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.selectAdminTab('backup');">{{ __('app.backup') }}</a></li>
<li class="admin-tab-info {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'info')) ? 'is-active' : ''}}">
<a href="javascript:void(0);" onclick="window.vue.selectAdminTab('info');">
@@ -346,6 +347,26 @@
</form>
</div>
<div class="admin-themes {{ ((!isset($_GET['tab'])) || ($_GET['tab'] !== 'themes')) ? 'is-hidden' : ''}}">
<h2>{{ __('app.themes') }}</h2>
<p>{{ __('app.themes_hint') }}</p>
<div class="field">
<div class="control">
<input type="file" class="input" id="theme_import_file" accept=".zip"/>
</div>
</div>
<div class="field">
<div class="control">
<button type="button" class="button is-link" onclick="window.vue.startThemeImport(document.getElementById('theme_import_file'), this);">{{ __('app.import') }}</button>
</div>
</div>
<div class="admin-themes-result is-hidden" id="themes-import-result">{{ __('app.theme_import_successful') }}</div>
</div>
<div class="admin-backup {{ ((!isset($_GET['tab'])) || ($_GET['tab'] !== 'backup')) ? 'is-hidden' : ''}}">
<h2>{{ __('app.export') }}</h2>
+2 -2
View File
File diff suppressed because one or more lines are too long