diff --git a/cache/lastCache.txt b/cache/lastCache.txt index 5c17eb9..89f551d 100644 --- a/cache/lastCache.txt +++ b/cache/lastCache.txt @@ -1 +1 @@ -1577899842 \ No newline at end of file +1578262565 \ No newline at end of file diff --git a/system/Controllers/MetaApiController.php b/system/Controllers/MetaApiController.php index 271d9a4..ef55b19 100644 --- a/system/Controllers/MetaApiController.php +++ b/system/Controllers/MetaApiController.php @@ -30,10 +30,13 @@ class MetaApiController extends ContentController # loop through all plugins foreach($this->settings['plugins'] as $name => $plugin) { - $pluginSettings = \Typemill\Settings::getObjectSettings('plugins', $name); - if($pluginSettings && isset($pluginSettings['metatabs'])) + if($plugin['active']) { - $metatabs = array_merge_recursive($metatabs, $pluginSettings['metatabs']); + $pluginSettings = \Typemill\Settings::getObjectSettings('plugins', $name); + if($pluginSettings && isset($pluginSettings['metatabs'])) + { + $metatabs = array_merge_recursive($metatabs, $pluginSettings['metatabs']); + } } } @@ -155,4 +158,6 @@ class MetaApiController extends ContentController # return with the new metadata return $response->withJson(array('metadata' => $metaData, 'errors' => false)); } -} \ No newline at end of file +} + +# check models -> writeYaml for getPageMeta and getPageMetaDefaults. \ No newline at end of file diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index d09a334..31eadc3 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -463,7 +463,7 @@ class SettingsController extends Controller if($validate->newUser($params, $userroles)) { - $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); + $userdata = array('username' => $params['username'], 'firstname' => $params['firstname'], 'lastname' => $params['lastname'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); $user->createUser($userdata); @@ -511,7 +511,7 @@ class SettingsController extends Controller if($validate->existingUser($params, $userroles)) { - $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']); + $userdata = array('username' => $params['username'], 'firstname' => $params['firstname'], 'lastname' => $params['lastname'], 'email' => $params['email'], 'userrole' => $params['userrole']); if(empty($params['password']) AND empty($params['newpassword'])) { diff --git a/system/Models/Field.php b/system/Models/Field.php index ad481c2..712a1ea 100644 --- a/system/Models/Field.php +++ b/system/Models/Field.php @@ -64,6 +64,7 @@ class Field 'id', 'autocomplete', 'placeholder', + 'maxlength', 'size', 'rows', 'cols', diff --git a/system/Models/User.php b/system/Models/User.php index ded5290..66be810 100644 --- a/system/Models/User.php +++ b/system/Models/User.php @@ -38,6 +38,15 @@ class User extends WriteYaml 'password' => $this->generatePassword($params['password']), 'userrole' => $params['userrole'] ); + + if(isset($params['firstname'])) + { + $userdata['firstname'] = $params['firstname']; + } + if(isset($params['lastname'])) + { + $userdata['lastname'] = $params['lastname']; + } if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata)) { @@ -58,8 +67,20 @@ class User extends WriteYaml $update = array_merge($userdata, $params); $this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update); + + $_SESSION['user'] = $update['username']; + $_SESSION['role'] = $update['userrole']; + + if(isset($update['firstname'])) + { + $_SESSION['firstname'] = $update['firstname']; + } + if(isset($update['lastname'])) + { + $_SESSION['lastname'] = $update['lastname']; + } - return $userdata['username']; + return $userdata['username']; } public function deleteUser($username) @@ -88,6 +109,15 @@ class User extends WriteYaml $_SESSION['user'] = $user['username']; $_SESSION['role'] = $user['userrole']; $_SESSION['login'] = $user['lastlogin']; + + if(isset($user['firstname'])) + { + $_SESSION['firstname'] = $user['firstname']; + } + if(isset($user['lastname'])) + { + $_SESSION['lastname'] = $user['lastname']; + } } } diff --git a/system/Models/Validation.php b/system/Models/Validation.php index 989fff9..5e0d8d9 100644 --- a/system/Models/Validation.php +++ b/system/Models/Validation.php @@ -124,6 +124,10 @@ class Validation $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20"); $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); $v->rule('userAvailable', 'username')->message("User already exists"); + $v->rule('noHTML', 'firstname')->message(" contains HTML"); + $v->rule('lengthBetween', 'firstname', 2, 40); + $v->rule('noHTML', 'lastname')->message(" contains HTML"); + $v->rule('lengthBetween', 'lastname', 2, 40); $v->rule('email', 'email')->message("e-mail is invalid"); $v->rule('in', 'userrole', $userroles); @@ -137,10 +141,14 @@ class Validation $v->rule('alphaNum', 'username')->message("invalid"); $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); $v->rule('userExists', 'username')->message("user does not exist"); + $v->rule('noHTML', 'firstname')->message(" contains HTML"); + $v->rule('lengthBetween', 'firstname', 2, 40); + $v->rule('noHTML', 'lastname')->message(" contains HTML"); + $v->rule('lengthBetween', 'lastname', 2, 40); $v->rule('email', 'email')->message("e-mail is invalid"); $v->rule('in', 'userrole', $userroles); - return $this->validationResult($v); + return $this->validationResult($v); } public function username($username) @@ -329,7 +337,23 @@ class Validation { $v->rule('required', $fieldName); } - + if(isset($fieldDefinitions['maxlength'])) + { + $v->rule('lengthMax', $fieldName, $fieldDefinitions['maxlength']); + } + if(isset($fieldDefinitions['max'])) + { + $v->rule('max', $fieldName, $fieldDefinitions['max']); + } + if(isset($fieldDefinitions['min'])) + { + $v->rule('min', $fieldName, $fieldDefinitions['min']); + } + if(isset($fieldDefinitions['pattern'])) + { + $v->rule('regex', $fieldName, '/^' . $fieldDefinitions['pattern'] . '$/'); + } + switch($fieldDefinitions['type']) { case "select": @@ -350,7 +374,7 @@ class Validation { $v->rule('in', $key, $options); } - break; + break; case "color": $v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'); break; @@ -361,33 +385,35 @@ class Validation $v->rule('date', $fieldName); break; case "checkbox": - $v->rule('accepted', $fieldName); + if(isset($fieldDefinitions['required'])) + { + $v->rule('accepted', $fieldName); + } break; case "url": - $v->rule('lengthMax', $fieldName, 200); $v->rule('url', $fieldName); + $v->rule('lengthMax', $fieldName, 200); break; case "text": - $v->rule('lengthMax', $fieldName, 200); + $v->rule('noHTML', $fieldName); + $v->rule('lengthMax', $fieldName, 500); $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u'); break; case "textarea": - $v->rule('lengthMax', $fieldName, 1000); $v->rule('noHTML', $fieldName); - // $v->rule('regex', $fieldName, '/<[^<]+>/'); + $v->rule('lengthMax', $fieldName, 1000); break; case "paragraph": - $v->rule('lengthMax', $fieldName, 1000); $v->rule('noHTML', $fieldName); + $v->rule('lengthMax', $fieldName, 1000); break; case "password": $v->rule('lengthMax', $fieldName, 100); break; default: $v->rule('lengthMax', $fieldName, 1000); - $v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u'); + $v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u'); } - return $this->validationResult($v, $objectName); } diff --git a/system/Models/WriteYaml.php b/system/Models/WriteYaml.php index 7364220..0254a2b 100644 --- a/system/Models/WriteYaml.php +++ b/system/Models/WriteYaml.php @@ -86,12 +86,27 @@ class WriteYaml extends Write $description = substr($description, 0, $lastSpace); } + $author = $settings['author']; + + if(isset($_SESSION)) + { + if(isset($_SESSION['firstname']) && $_SESSION['firstname'] !='' && isset($_SESSION['lastname']) && $_SESSION['lastname'] != '') + { + $author = $_SESSION['firstname'] . ' ' . $_SESSION['lastname']; + } + elseif(isset($_SESSION['user'])) + { + $author = $_SESSION['user']; + } + } + # create new meta-file $meta = [ 'meta' => [ 'title' => $title, 'description' => $description, - 'author' => $settings['author'], # change to session, extend userdata + 'author' => $author, + 'created' => date("Y-m-d"), ] ]; diff --git a/system/author/css/style.css b/system/author/css/style.css index 8d8027a..1d77906 100644 --- a/system/author/css/style.css +++ b/system/author/css/style.css @@ -1773,7 +1773,6 @@ button.format-item.close:hover{ top: 0; left: 0; transform: translate(-50%, -100%); - transition: 0.2s all; display: flex; justify-content: center; align-items: center; @@ -1809,7 +1808,7 @@ button.format-item.close:hover{ margin-right: 2px; } .urlinput{ - width: 80%; + width: 75%; min-height: auto; background: #555; color: #fff; diff --git a/system/author/editor/editor-raw.twig b/system/author/editor/editor-raw.twig index db69474..22a8d8d 100644 --- a/system/author/editor/editor-raw.twig +++ b/system/author/editor/editor-raw.twig @@ -5,6 +5,27 @@
+
+ + + + + + +
+
diff --git a/system/author/js/vue-blox.js b/system/author/js/vue-blox.js index d6688de..0e0d7ad 100644 --- a/system/author/js/vue-blox.js +++ b/system/author/js/vue-blox.js @@ -393,7 +393,7 @@ const contentComponent = Vue.component('content-block', { }) const inlineFormatsComponent = Vue.component('inline-formats', { - template: '
' + + template: '
' + '
' + '' + '' + @@ -410,10 +410,12 @@ const inlineFormatsComponent = Vue.component('inline-formats', { data: function(){ return { formatBar: false, + formatElements: 0, startX: 0, startY: 0, x: 0, y: 0, + z: 150, textComponent: '', selectedText: '', startPos: false, @@ -500,6 +502,10 @@ const inlineFormatsComponent = Vue.component('inline-formats', { this.y = event.offsetY - 15; + /* calculate the width of the format bar */ + this.formatElements = document.getElementsByClassName('inlineFormatItem').length; + this.z = this.formatElements * 30; + this.showInlineFormat = true; this.selectedText = selectedText; }, @@ -508,30 +514,35 @@ const inlineFormatsComponent = Vue.component('inline-formats', { content = this.textComponent.value; content = content.substring(0, this.startPos) + '**' + this.selectedText + '**' + content.substring(this.endPos, content.length); this.$parent.updatemarkdown(content); + this.showInlineFormat = false; }, formatItalic() { content = this.textComponent.value; content = content.substring(0, this.startPos) + '_' + this.selectedText + '_' + content.substring(this.endPos, content.length); this.$parent.updatemarkdown(content); + this.showInlineFormat = false; }, formatCode() { content = this.textComponent.value; content = content.substring(0, this.startPos) + '`' + this.selectedText + '`' + content.substring(this.endPos, content.length); - this.$parent.updatemarkdown(content); + this.$parent.updatemarkdown(content); + this.showInlineFormat = false; }, formatMath() { content = this.textComponent.value; content = content.substring(0, this.startPos) + '$' + this.selectedText + '$' + content.substring(this.endPos, content.length); this.$parent.updatemarkdown(content); + this.showInlineFormat = false; }, formatLink() { if(this.url == "") { - this.link = false; + this.link = false; + this.showInlineFormat = false; return; } content = this.textComponent.value; @@ -543,11 +554,15 @@ const inlineFormatsComponent = Vue.component('inline-formats', { openLink() { this.link = true; + this.url = ''; + this.z = 200; this.$nextTick(() => this.$refs.urlinput.focus()); }, closeLink() { this.link = false; + this.url = ''; + this.showInlineFormat = false; } } }) diff --git a/system/author/js/vue-meta.js b/system/author/js/vue-meta.js index 340877c..b55a452 100644 --- a/system/author/js/vue-meta.js +++ b/system/author/js/vue-meta.js @@ -1,26 +1,21 @@ const FormBus = new Vue(); Vue.component('component-text', { - props: ['class', 'placeholder', 'label', 'name', 'type', 'size', 'value', 'errors'], + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], template: '
' + '' + - '' + - '{{ errors[name] }}' + - '
', - methods: { - update: function($event, name) - { - FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); - }, - }, -}) - -Vue.component('component-date', { - props: ['class', 'placeholder', 'readonly', 'label', 'name', 'type', 'size', 'value', 'errors'], - template: '
' + - '' + - '' + + '' + '{{ errors[name] }}' + + '{{ description }}' + '
', methods: { update: function($event, name) @@ -31,11 +26,195 @@ Vue.component('component-date', { }) Vue.component('component-textarea', { - props: ['class', 'placeholder', 'label', 'name', 'type', 'size', 'value', 'errors'], + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], template: '
' + - '' + - '' + + '' + + '' + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-url', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-number', { + props: ['class', 'id', 'description', 'min', 'max', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-email', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-tel', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-password', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-date', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + + '
', + methods: { + update: function($event, name) + { + FormBus.$emit('forminput', {'name': name, 'value' : $event.target.value}); + }, + }, +}) + +Vue.component('component-color', { + props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '' + + '{{ errors[name] }}' + + '{{ description }}' + '
', methods: { update: function($event, name) @@ -46,13 +225,20 @@ Vue.component('component-textarea', { }) Vue.component('component-select', { - props: ['class', 'placeholder', 'label', 'name', 'type', 'size', 'options', 'value', 'errors'], + props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'label', 'name', 'type', 'options', 'value', 'errors'], template: '
' + '' + - '' + - '{{ errors[name] }}' + + '{{ errors[name] }}' + + '{{ description }}' + '
', methods: { update: function($event, name) @@ -63,13 +249,21 @@ Vue.component('component-select', { }) Vue.component('component-checkbox', { - props: ['class', 'label', 'checkboxlabel', 'name', 'type', 'value', 'errors'], + props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'label', 'checkboxlabel', 'name', 'type', 'value', 'errors'], template: '
' + '' + '' + '
', methods: { @@ -80,14 +274,50 @@ Vue.component('component-checkbox', { }, }) +Vue.component('component-checkboxlist', { + props: ['class', 'description', 'readonly', 'required', 'disabled', 'label', 'checkboxlabel', 'options', 'name', 'type', 'value', 'errors'], + template: '
' + + '' + + '
', + methods: { + update: function($event, value, optionvalue, name) + { + /* if value (array) for checkboxlist is not initialized yet */ + if(value === true || value === false) + { + value = [optionvalue]; + } + FormBus.$emit('forminput', {'name': name, 'value' : value}); + }, + }, +}) + Vue.component('component-radio', { - props: ['label', 'options', 'name', 'type', 'value', 'errors'], - template: '
' + + props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'options', 'label', 'name', 'type', 'value', 'errors'], + template: '
' + '' + '' + '
', methods: { @@ -167,12 +397,14 @@ let meta = new Vue({ .then(function (response) { var formdefinitions = response.data.metadefinitions; + for (var key in formdefinitions) { if (formdefinitions.hasOwnProperty(key)) { self.tabs.push(key); self.formErrors[key] = false; } } + self.formErrorsReset = self.formErrors; self.formDefinitions = formdefinitions; diff --git a/system/author/layouts/layoutEditor.twig b/system/author/layouts/layoutEditor.twig index 50750e1..6070f31 100644 --- a/system/author/layouts/layoutEditor.twig +++ b/system/author/layouts/layoutEditor.twig @@ -20,6 +20,9 @@ + + {{ assets.renderCSS() }} + @@ -56,13 +59,25 @@
+ + + + {{ assets.renderEditorJS() }} + + + + {{ assets.renderJS() }} + \ No newline at end of file diff --git a/system/author/metatabs.yaml b/system/author/metatabs.yaml index bfa68b7..4928300 100644 --- a/system/author/metatabs.yaml +++ b/system/author/metatabs.yaml @@ -3,19 +3,30 @@ meta: title: type: text label: Meta title - size: 60 + maxlength: 60 class: large description: type: textarea label: Meta description size: 160 class: large + description: If not filled, the description is extracted from content. author: type: text label: author class: large + description: Taken from your user account if set. + manualdate: + type: date + label: Manual date modified: type: date - label: Last modified at (readonly) + label: Last modified live (readonly) readonly: readonly - class: large \ No newline at end of file + class: medium + description: Used as fallback when no manual date is set. + created: + type: date + label: Created at (readonly) + readonly: readonly + class: medium \ No newline at end of file diff --git a/system/author/settings/user.twig b/system/author/settings/user.twig index a6740a7..391267a 100644 --- a/system/author/settings/user.twig +++ b/system/author/settings/user.twig @@ -23,6 +23,22 @@ {{ errors.username | first }} {% endif %}
+ +
+ + + {% if errors.firstname %} + {{ errors.firstname | first }} + {% endif %} +
+ +
+ + + {% if errors.lastname %} + {{ errors.lastname | first }} + {% endif %} +
@@ -48,7 +64,7 @@
- + {% if errors.password %} {{ errors.password | first }} {% endif %} @@ -56,7 +72,7 @@
- + {% if errors.newpassword %} {{ errors.newpassword | first }} {% endif %} diff --git a/system/author/settings/usernew.twig b/system/author/settings/usernew.twig index 585becb..0e103b9 100644 --- a/system/author/settings/usernew.twig +++ b/system/author/settings/usernew.twig @@ -22,7 +22,23 @@ {{ errors.username | first }} {% endif %}
- + +
+ + + {% if errors.firstname %} + {{ errors.firstname | first }} + {% endif %} +
+ +
+ + + {% if errors.lastname %} + {{ errors.lastname | first }} + {% endif %} +
+
@@ -45,7 +61,7 @@
- + {% if errors.password %} {{ errors.password | first }} {% endif %} diff --git a/themes/typemill/page.twig b/themes/typemill/page.twig index f8a83fb..ea9cb64 100644 --- a/themes/typemill/page.twig +++ b/themes/typemill/page.twig @@ -1,3 +1,5 @@ +{% set published = metatabs.meta.manualdate ? metatabs.meta.manualdate : metatabs.meta.modified %} + {% if content is empty %}

{{ item.name }}

@@ -9,10 +11,10 @@ {% if (settings.themes.typemill.socialPosition.top or settings.themes.typemill.modifiedPosition.top or settings.themes.typemill.authorPosition.top or settings.themes.typemill.gitPosition.top) %}
{% if settings.themes.typemill.authorPosition.top %} - {{ settings.themes.typemill.authorIntro }}: {{ settings.author }} + {{ settings.themes.typemill.authorIntro }}: {{ metatabs.meta.author|default(settings.author) }} {% endif %} {% if settings.themes.typemill.modifiedPosition.top %} - {{ settings.themes.typemill.modifiedText }}: {{ metatabs.meta.modified|date(settings.themes.typemill.modifiedFormat) }} + {{ settings.themes.typemill.modifiedText }}: {{ published|date(settings.themes.typemill.modifiedFormat) }} {% endif %} {% if settings.themes.typemill.socialPosition.top %}