diff --git a/cache/cyanine-custom.css b/cache/cyanine-custom.css new file mode 100644 index 0000000..61ff634 --- /dev/null +++ b/cache/cyanine-custom.css @@ -0,0 +1,3 @@ +.body{ + background-color: green; +} \ No newline at end of file diff --git a/data/security/securitylog.txt b/data/security/securitylog.txt deleted file mode 100644 index b123e63..0000000 --- a/data/security/securitylog.txt +++ /dev/null @@ -1,19 +0,0 @@ -127.0.0.1;2023-12-24 10:46:32;login: invalid data -127.0.0.1;2023-12-24 10:47:01;login: invalid data -127.0.0.1;2023-12-24 10:51:05;login: invalid data -127.0.0.1;2023-12-24 10:57:31;login: authcode wrong or outdated. -127.0.0.1;2023-12-24 10:59:47;login: authcode wrong or outdated. -127.0.0.1;2023-12-24 10:59:51;login: wrong password -127.0.0.1;2023-12-24 10:59:59;login: authcode wrong or outdated. -127.0.0.1;2023-12-25 06:20:18;login: authcode wrong or outdated. -127.0.0.1;2023-12-25 06:20:35;login: user not found -127.0.0.1;2023-12-25 09:12:05;login: wrong password -127.0.0.1;2023-12-27 11:17:43;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:21:21;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:24:01;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:25:07;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:30:28;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:31:36;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:31:52;login: authcode wrong or outdated. -127.0.0.1;2023-12-27 11:32:10;login: authcode wrong or outdated. -127.0.0.1;2024-01-15 13:11:20;login: invalid data diff --git a/system/typemill/Controllers/ControllerApiGlobals.php b/system/typemill/Controllers/ControllerApiGlobals.php index a154450..b781908 100644 --- a/system/typemill/Controllers/ControllerApiGlobals.php +++ b/system/typemill/Controllers/ControllerApiGlobals.php @@ -5,6 +5,8 @@ namespace Typemill\Controllers; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Navigation; +use Typemill\Models\Sitemap; +use Typemill\Models\StorageWrapper; class ControllerApiGlobals extends Controller { @@ -44,6 +46,115 @@ class ControllerApiGlobals extends Controller return $response->withHeader('Content-Type', 'application/json')->withStatus(200); } + public function clearNavigation(Request $request, Response $response) + { + $navigation = new Navigation(); + + $result = $navigation->clearNavigation(); + + $response->getBody()->write(json_encode([ + 'result' => $result + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(200);; + } + + public function showSecurityLog(Request $request, Response $response) + { + $storage = new StorageWrapper('\Typemill\Models\Storage'); + $logfile = $storage->getFile('dataFolder', 'security', 'securitylog.txt'); + + if($logfile) + { + $logfile = trim($logfile); + if($logfile == '') + { + $lines = ['Logfile is empty']; + } + else + { + $lines = preg_split('/\r\n|\n|\r/', $logfile); + } + + $response->getBody()->write(json_encode([ + 'lines' => $lines + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(200);; + + } + + $response->getBody()->write(json_encode([ + 'error' => 'No logfile found' + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + public function deleteSecurityLog(Request $request, Response $response) + { + $storage = new StorageWrapper('\Typemill\Models\Storage'); + $result = $storage->deleteFile('dataFolder', 'security', 'securitylog.txt'); + + $response->getBody()->write(json_encode([ + 'result' => $result + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(200);; + } + + public function deleteCache(Request $request, Response $response) + { + $storage = new StorageWrapper('\Typemill\Models\Storage'); + + $cacheFolder = $storage->getFolderPath('cacheFolder'); + + $iterator = new \RecursiveDirectoryIterator($cacheFolder, \RecursiveDirectoryIterator::SKIP_DOTS); + $files = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST); + + $error = false; + + foreach($files as $file) + { + if ($file->isDir()) + { + if(!rmdir($file->getRealPath())) + { + $error = 'Could not delete some folders.'; + } + } + elseif($file->getExtension() !== 'css') + { + if(!unlink($file->getRealPath()) ) + { + $error = 'Could not delete some files.'; + } + } + } + + $sitemap = new Sitemap(); + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $liveNavigation = $navigation->getLiveNavigation($urlinfo, $this->settings['langattr']); + $sitemap->updateSitemap($liveNavigation, $urlinfo); + + if($error) + { + $response->getBody()->write(json_encode([ + 'error' => $error + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + $response->getBody()->write(json_encode([ + 'result' => true + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(200); + } + + public function getTranslations(Request $request, Response $response) { $response->getBody()->write(json_encode([ diff --git a/system/typemill/Models/User.php b/system/typemill/Models/User.php index 6a60042..de6cf04 100644 --- a/system/typemill/Models/User.php +++ b/system/typemill/Models/User.php @@ -71,7 +71,15 @@ class User public function getFullName() { - return trim($this->user['firstname'] . ' ' . $this->user['lastname']); + $firstname = isset($this->user['firstname']) ? trim($this->user['firstname']) : false; + $lastname = isset($this->user['lastname']) ? trim($this->user['lastname']) : false; + + if($firstname OR $lastname) + { + return trim($this->user['firstname'] . ' ' . $this->user['lastname']); + } + + return $this->user['username']; } public function getError() diff --git a/system/typemill/author/css/output.css b/system/typemill/author/css/output.css index 086ec42..e1b8743 100644 --- a/system/typemill/author/css/output.css +++ b/system/typemill/author/css/output.css @@ -771,21 +771,25 @@ video { margin-bottom: 2rem; } -.my-1 { - margin-top: 0.25rem; - margin-bottom: 0.25rem; -} - .my-3 { margin-top: 0.75rem; margin-bottom: 0.75rem; } +.my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} + .my-4 { margin-top: 1rem; margin-bottom: 1rem; } +.mr-2 { + margin-right: 0.5rem; +} + .mt-6 { margin-top: 1.5rem; } @@ -806,10 +810,6 @@ video { margin-left: 1rem; } -.mr-2 { - margin-right: 0.5rem; -} - .mb-4 { margin-bottom: 1rem; } @@ -942,10 +942,6 @@ video { height: 20rem; } -.h-8 { - height: 2rem; -} - .h-full { height: 100%; } @@ -954,6 +950,10 @@ video { height: 8rem; } +.h-8 { + height: 2rem; +} + .h-0 { height: 0px; } @@ -1506,6 +1506,16 @@ video { padding-right: 0.75rem; } +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + .py-1\.5 { padding-top: 0.375rem; padding-bottom: 0.375rem; @@ -1516,11 +1526,6 @@ video { padding-bottom: 0.25rem; } -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - .py-4 { padding-top: 1rem; padding-bottom: 1rem; @@ -1531,11 +1536,6 @@ video { padding-right: 1rem; } -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - .px-12 { padding-left: 3rem; padding-right: 3rem; @@ -1695,9 +1695,9 @@ video { line-height: 2.5rem; } -.text-base { - font-size: 1rem; - line-height: 1.5rem; +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; } .text-xs { @@ -1705,6 +1705,11 @@ video { line-height: 1rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + .text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -1715,11 +1720,6 @@ video { line-height: 1.75rem; } -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - .text-3xl { font-size: 1.875rem; line-height: 2.25rem; @@ -1730,16 +1730,16 @@ video { line-height: 2rem; } -.font-normal { - font-weight: 400; +.font-bold { + font-weight: 700; } .font-medium { font-weight: 500; } -.font-bold { - font-weight: 700; +.font-normal { + font-weight: 400; } .uppercase { @@ -1773,6 +1773,11 @@ video { color: rgb(250 250 249 / var(--tw-text-opacity)); } +.text-teal-600 { + --tw-text-opacity: 1; + color: rgb(13 148 136 / var(--tw-text-opacity)); +} + .text-rose-300 { --tw-text-opacity: 1; color: rgb(253 164 175 / var(--tw-text-opacity)); @@ -1813,11 +1818,6 @@ video { color: rgb(107 114 128 / var(--tw-text-opacity)); } -.text-teal-600 { - --tw-text-opacity: 1; - color: rgb(13 148 136 / var(--tw-text-opacity)); -} - .text-teal-300 { --tw-text-opacity: 1; color: rgb(94 234 212 / var(--tw-text-opacity)); diff --git a/system/typemill/author/js/vue-kixote.js b/system/typemill/author/js/vue-kixote.js index de7c7bd..4d9645a 100644 --- a/system/typemill/author/js/vue-kixote.js +++ b/system/typemill/author/js/vue-kixote.js @@ -1,77 +1,139 @@ -const promptlist = [ +const getKixoteError = function(error) +{ + console.info(error); + + if(error.response.data.error != undefined) + { + if(Array.isArray(error.response.data.error)) + { + return error.response.data.error; + } + if(typeof error.response.data.error === 'string') + { + return [error.response.data.error]; + } + } + + return ['something went wrong']; +} + + +// publish tree +// unpublish tree +// load page +// save page +// translate page +// translate tree + + +const kixoteCommands = [ { name: 'help', - description: 'List all awailable prompts with a short description.', + description: 'List all available commands with a short description.', method: function() { let result = ['
You can use the following prompts:
', + answer: 'You can use the following commands:
', }, { name: 'exit', description: 'Exit Kixote and close the Kixote window.', }, { - name: 'skip', - description: 'Skip the current task and start a new prompt.', - answer: ['We skipped the current task. Waiting for your next prompt.'], - }, - { - name: 'refresh cache', - description: 'Refresh the cache and recreate the navigation.', + name: 'clear navigation', + description: 'Clear the cached navigation.', method: function() { var self = this; - tmaxios.get('/api/v1/settings',{ + tmaxios.delete('/api/v1/clearnavigation',{ }) .then(function (response) { - eventBus.$emit('answer', ['cache has been refreshed']); + eventBus.$emit('answer', ['navigation has been cleared']); }) .catch(function (error) { - alert("no answer"); + eventBus.$emit('answer', getKixoteError(error)); }); }, - answer: ['Asking server...'], + answer: ['Asking server ...'], + }, + { + name: 'clear cache', + description: 'Clear the cache-folder and delete cached files.', + method: function() + { + var self = this; + + tmaxios.delete('/api/v1/cache',{ + }) + .then(function (response) + { + eventBus.$emit('answer', ['cache has been cleared']); + }) + .catch(function (error) + { + eventBus.$emit('answer', getKixoteError(error)); + }); + }, + answer: ['Asking server ...'], }, { name: 'show security log', - description: 'Not awailable.', + description: 'Show the security log that you can activate in the security tab of the system settings.', method: function() { - eventBus.$emit('nextPrompts', ['delete security log']); - eventBus.$emit('answer', ['security log shown']); + var self = this; + + tmaxios.get('/api/v1/securitylog',{ + }) + .then(function (response) + { + eventBus.$emit('answer', response.data.lines); + eventBus.$emit('nextCommands', ['clear security log']); + }) + .catch(function (error) + { + eventBus.$emit('answer', getKixoteError(error)); + }); }, - answer: ['Loading security log...'], + answer: ['Asking server ...'], }, { - name: 'delete security log', - description: 'Not awailable.', + name: 'clear security log', + description: 'Clear the security log.', method: function() { - eventBus.$emit('answer', ['Security log deleted']); + var self = this; + + tmaxios.delete('/api/v1/securitylog',{ + }) + .then(function (response) + { + eventBus.$emit('answer', ['Security log has been cleared.']); + }) + .catch(function (error) + { + eventBus.$emit('answer', getKixoteError(error)); + }); }, + answer: ['Asking server ...'], }, +/* { - name: 'publish folder', - description: 'Publishes all unpublished and modified pages inside a folder.', - answer: ['Not available yet.'], - }, - { - name: 'unpublish folder', - description: 'Unpublishes all pages inside a folder.', - answer: ['Not available yet.'], + name: 'skip', + description: 'Skip the current task and start a new command.', + answer: ['We skipped the current task. Waiting for your next command.'], }, { name: 'create content', @@ -95,7 +157,7 @@ const promptlist = [ method: function(params) { eventBus.$emit('storable', ['Lorem ipsum in markdown.']); - eventBus.$emit('nextPrompts', ['transform', 'translate', 'save to page']); + eventBus.$emit('nextCommands', ['transform', 'translate', 'save to page']); eventBus.$emit('answer', ['This is the answer from the server. The server can ask an AI service with the collected parameters and return any kind of answer in HTML and preferably in markdown, so that typemill can process the content again (e.g. store, translate, and more).']); }, answer: ['Creating content...'], @@ -110,22 +172,22 @@ const promptlist = [ }, answer: ['Save content...'], }, - ]; +*/ ]; const kixote = Vue.createApp({ template: `Ki> - +
Enter "help" to see a list of prompts
+Enter "help" to see a list of commands