diff --git a/composer.lock b/composer.lock index 8ddfaa8..e829438 100644 --- a/composer.lock +++ b/composer.lock @@ -852,16 +852,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { @@ -873,7 +873,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -924,7 +924,7 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/yaml", diff --git a/content/00-welcome/00-setup.md b/content/00-welcome/00-setup.md index afd7322..f89e142 100644 --- a/content/00-welcome/00-setup.md +++ b/content/00-welcome/00-setup.md @@ -4,6 +4,8 @@ Congratulations! If you see this page, then the setup of the system has worked s If you face any problems during the installation, then please make sure, that your system supports these features: +--- + - PHP version 7+. - Apache server. - The module `mod_rewrite` and `htaccess`. diff --git a/system/Controllers/ArticleApiController.php b/system/Controllers/ArticleApiController.php index c0deeca..2f1ea7f 100644 --- a/system/Controllers/ArticleApiController.php +++ b/system/Controllers/ArticleApiController.php @@ -973,19 +973,24 @@ class ArticleApiController extends ContentController # fix footnotes in parsedown, might break with complicated footnotes $parsedown->setVisualMode(); + # flag for TOC + $toc = false; + + $tocMarkup = false; + # if content is not an array, then transform it if(!is_array($content)) { # turn markdown into an array of markdown-blocks $content = $parsedown->markdownToArrayBlocks($content); + + # build toc here to avoid duplicated toc for live content + $tocMarkup = $parsedown->buildTOC($parsedown->headlines); } # needed for ToC links $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - # flag for TOC - $toc = false; - # loop through mardkown-array and create html-blocks foreach($content as $key => $block) { @@ -1003,7 +1008,11 @@ class ArticleApiController extends ContentController if($toc) { - $tocMarkup = $parsedown->buildTOC($parsedown->headlines); + if(!$tocMarkup) + { + $tocMarkup = $parsedown->buildTOC($parsedown->headlines); + } + $content[$toc] = ['id' => $toc, 'html' => $tocMarkup]; } diff --git a/system/Controllers/MetaApiController.php b/system/Controllers/MetaApiController.php index b8403b9..a523b45 100644 --- a/system/Controllers/MetaApiController.php +++ b/system/Controllers/MetaApiController.php @@ -129,6 +129,18 @@ class MetaApiController extends ContentController $metascheme[$tabname][$fieldname] = true; $metadata[$tabname][$fieldname] = isset($pagemeta[$tabname][$fieldname]) ? $pagemeta[$tabname][$fieldname] : null; + + # check if there is a selectfield for userroles + if(isset($fielddefinitions['type']) && ($fielddefinitions['type'] == 'select' ) && isset($fielddefinitions['dataset']) && ($fielddefinitions['dataset'] == 'userroles' ) ) + { + $userroles = [null => null]; + foreach($this->c->acl->getRoles() as $userrole) + { + $userroles[$userrole] = $userrole; + } + $metadefinitions[$tabname]['fields'][$fieldname]['options'] = $userroles; + } + /* # special treatment for customfields if(isset($fielddefinitions['type']) && ($fielddefinitions['type'] == 'customfields' ) && $metadata[$tabname][$fieldname] ) @@ -189,7 +201,7 @@ class MetaApiController extends ContentController } # if item is a folder - if($this->item->elementType == "folder") + if($this->item->elementType == "folder" && isset($this->item->contains)) { $pagemeta['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $this->item->contains; @@ -217,6 +229,18 @@ class MetaApiController extends ContentController } else { + + if($fieldDefinition && isset($fieldDefinition['type']) && ($fieldDefinition['type'] == 'select' ) && isset($fieldDefinition['dataset']) && ($fieldDefinition['dataset'] == 'userroles' ) ) + { + $userroles = [null => null]; + foreach($this->c->acl->getRoles() as $userrole) + { + $userroles[$userrole] = $userrole; + } + $fieldDefinition['options'] = $userroles; + } + + # validate user input for this field $result = $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition); diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index c970a2a..42d5f64 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -241,7 +241,7 @@ class PageController extends Controller $shortenedPage = $this->cutRestrictedContent($markdownBlocks); # check if there is customized content - $restrictionnotice = ( isset($this->settings['restrictionnotice']) && $this->settings['restrictionnotice'] != '' ) ? $this->settings['restrictionnotice'] : 'You are not allowed to access this content.'; + $restrictionnotice = $this->prepareRestrictionNotice(); # add notice to shortened content $shortenedPage[] = $restrictionnotice; @@ -478,7 +478,8 @@ class PageController extends Controller # check if page is restricted to certain user if(isset($meta['alloweduser']) && $meta['alloweduser'] && $meta['alloweduser'] !== '' ) { - if(isset($_SESSION['user']) && $_SESSION['user'] == $meta['alloweduser']) + $alloweduser = array_map('trim', explode(",", $meta['alloweduser'])); + if(isset($_SESSION['user']) && in_array($_SESSION['user'], $alloweduser)) { # user has access to the page, so there are no restrictions return false; @@ -539,4 +540,37 @@ class PageController extends Controller return $restrictedMarkdown; } + + protected function prepareRestrictionNotice() + { + if( isset($this->settings['restrictionnotice']) && $this->settings['restrictionnotice'] != '' ) + { + $restrictionNotice = $this->settings['restrictionnotice']; + } + else + { + $restrictionNotice = 'You are not allowed to access this content.'; + } + + if( isset($this->settings['wraprestrictionnotice']) && $this->settings['wraprestrictionnotice'] ) + { + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $restrictionNotice); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + $restrictionNotice = ''; + + foreach($lines as $key => $line) + { + $restrictionNotice .= "!!!! " . $line . "\n"; + } + } + + return $restrictionNotice; + } } \ No newline at end of file diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index 24cd5e7..ad57c29 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -95,6 +95,7 @@ class SettingsController extends Controller 'pageaccess' => isset($newSettings['pageaccess']) ? true : null, 'hrdelimiter' => isset($newSettings['hrdelimiter']) ? true : null, 'restrictionnotice' => $newSettings['restrictionnotice'], + 'wraprestrictionnotice' => isset($newSettings['wraprestrictionnotice']) ? true : null, 'headlineanchors' => isset($newSettings['headlineanchors']) ? $newSettings['headlineanchors'] : null, 'displayErrorDetails' => isset($newSettings['displayErrorDetails']) ? true : null, 'twigcache' => isset($newSettings['twigcache']) ? true : null, diff --git a/system/Settings.php b/system/Settings.php index bc1f7ff..728d568 100644 --- a/system/Settings.php +++ b/system/Settings.php @@ -82,7 +82,7 @@ class Settings 'editor' => 'visual', 'formats' => ['markdown', 'headline', 'ulist', 'olist', 'table', 'quote', 'notice', 'image', 'video', 'file', 'toc', 'hr', 'definition', 'code'], 'contentFolder' => 'content', - 'version' => '1.4.3', + 'version' => '1.4.4', 'setup' => true, 'welcome' => true, 'images' => ['live' => ['width' => 820], 'thumbs' => ['width' => 250, 'height' => 150]], @@ -162,6 +162,7 @@ class Settings 'pageaccess' => true, 'hrdelimiter' => true, 'restrictionnotice' => true, + 'wraprestrictionnotice' => true, 'headlineanchors' => true, 'theme' => true, 'editor' => true, @@ -236,10 +237,6 @@ class Settings $acl->addResource(new Resource($resource)); } - # add administrator role - $acl->addRole(new Role('administrator')); - $acl->allow('administrator'); - # add all other roles dynamically foreach($roles as $role) { @@ -251,6 +248,10 @@ class Settings } } + # add administrator role + $acl->addRole(new Role('administrator')); + $acl->allow('administrator'); + return $acl; } } \ No newline at end of file diff --git a/system/author/js/typemillutils.js b/system/author/js/typemillutils.js index f0891fc..60e3c2e 100644 --- a/system/author/js/typemillutils.js +++ b/system/author/js/typemillutils.js @@ -19,13 +19,13 @@ let typemillUtilities = { addYoutubePlayButton: function(element) { - element.parentNode.classList.add("video-container"); - + element.classList.add("video-container"); + var youtubePlaybutton = document.createElement("button"); youtubePlaybutton.classList.add("play-video"); youtubePlaybutton.value = "Play"; - element.parentNode.appendChild(youtubePlaybutton); + element.appendChild(youtubePlaybutton); }, listenToClick: function(){ @@ -34,7 +34,7 @@ let typemillUtilities = { /* listen to youtube */ if (event.target.matches('.play-video')) { - var youtubeID = event.target.parentNode.getElementsByClassName('youtube')[0].id; + var youtubeID = event.target.parentNode.id; event.preventDefault(); event.stopPropagation(); diff --git a/system/author/js/vue-blox.js b/system/author/js/vue-blox.js index f59fa27..9c7d4f6 100644 --- a/system/author/js/vue-blox.js +++ b/system/author/js/vue-blox.js @@ -1,5 +1,16 @@ const eventBus = new Vue(); +Vue.filter('translate', function (value) { + if (!value) return '' + transvalue = value.replace(/[ ]/g,"_").replace(/[.]/g, "_").replace(/[,]/g, "_").replace(/[-]/g, "_").replace(/[,]/g,"_").toUpperCase() + translated_string = labels[transvalue] + if(!translated_string || translated_string.length === 0){ + return value + } else { + return labels[transvalue] + } +}) + const contentComponent = Vue.component('content-block', { props: ['body', 'load'], template: '
' + @@ -19,7 +30,7 @@ const contentComponent = Vue.component('content-block', { '' + '
' + '' + - '
' + + '
' + '' + '' + '
' + diff --git a/system/author/js/vue-meta.js b/system/author/js/vue-meta.js index 42faa5e..4acdb63 100644 --- a/system/author/js/vue-meta.js +++ b/system/author/js/vue-meta.js @@ -13,13 +13,14 @@ Vue.filter('translate', function (value) { Vue.component('tab-meta', { - props: ['saved', 'errors', 'formdata', 'schema'], + props: ['saved', 'errors', 'formdata', 'schema', 'userroles'], template: '
' + '' + '' + @@ -52,6 +53,7 @@ let meta = new Vue({ formErrors: {}, formErrorsReset: {}, item: false, + userroles: false, saved: false, } }, @@ -97,6 +99,8 @@ let meta = new Vue({ self.formData = response.data.metadata; + self.userroles = response.data.userroles; + self.item = response.data.item; if(self.item.elementType == "folder" && self.item.contains == "posts") { diff --git a/system/author/js/vue-navi.js b/system/author/js/vue-navi.js index eb19963..fd61dda 100644 --- a/system/author/js/vue-navi.js +++ b/system/author/js/vue-navi.js @@ -1,6 +1,6 @@ const navcomponent = Vue.component('navigation', { template: '#navigation-template', - props: ['homepage', 'showForm', 'name', 'hide', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'contains', 'element', 'folder', 'level', 'url', 'root', 'freeze'], + props: ['homepage', 'name', 'hide', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'contains', 'element', 'folder', 'level', 'url', 'root', 'freeze'], data: function () { return { showForm: false, @@ -221,7 +221,7 @@ let navi = new Vue({ hideModal: function(e){ this.modalWindow = false; }, - toggleForm : function() + toggleForm: function() { this.showForm = !this.showForm; }, diff --git a/system/author/js/vue-shared.js b/system/author/js/vue-shared.js index 8768785..3593f7d 100644 --- a/system/author/js/vue-shared.js +++ b/system/author/js/vue-shared.js @@ -257,7 +257,7 @@ Vue.component('component-color', { }) Vue.component('component-select', { - props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'label', 'name', 'type', 'options', 'value', 'errors'], + props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'label', 'name', 'type', 'options', 'value', 'errors', 'dataset', 'userroles'], template: '
' + '' + ' - - + + {{ assets.renderCSS() }} @@ -39,16 +39,16 @@
- + - - - - - + + + + + diff --git a/system/author/layouts/layoutAuth.twig b/system/author/layouts/layoutAuth.twig index 866c430..ae51d01 100644 --- a/system/author/layouts/layoutAuth.twig +++ b/system/author/layouts/layoutAuth.twig @@ -17,7 +17,7 @@ - + {{ assets.renderCSS() }} @@ -31,6 +31,6 @@ {% block content %}{% endblock %} - + \ No newline at end of file diff --git a/system/author/layouts/layoutBlank.twig b/system/author/layouts/layoutBlank.twig index 9fcf2d8..f6f2bbc 100644 --- a/system/author/layouts/layoutBlank.twig +++ b/system/author/layouts/layoutBlank.twig @@ -16,7 +16,7 @@ - + diff --git a/system/author/layouts/layoutBlox.twig b/system/author/layouts/layoutBlox.twig index 489b9c6..62731fb 100644 --- a/system/author/layouts/layoutBlox.twig +++ b/system/author/layouts/layoutBlox.twig @@ -17,7 +17,7 @@ - + {{ assets.renderCSS() }} @@ -41,17 +41,17 @@
- + - - - - - - + + + + + + - - - - - - + + + + + + + {{ assets.renderJS() }} diff --git a/system/author/layouts/layoutEditor.twig b/system/author/layouts/layoutEditor.twig index ee55319..09f2a81 100644 --- a/system/author/layouts/layoutEditor.twig +++ b/system/author/layouts/layoutEditor.twig @@ -16,7 +16,7 @@ - + {{ assets.renderCSS() }} @@ -40,16 +40,16 @@
- + - - - - - + + + + + + - - - + + + + {{ assets.renderJS() }} diff --git a/system/author/metatabs.yaml b/system/author/metatabs.yaml index 3077f62..dc0cb0e 100644 --- a/system/author/metatabs.yaml +++ b/system/author/metatabs.yaml @@ -62,18 +62,14 @@ meta: type: select label: For access the user must have this minimum role class: medium + dataset: userroles options: - false: All - member: Member - author: Author - editor: Editor - administrator: Administrator description: Select the lowest userrole. Higher roles will have access too. alloweduser: type: text - label: Only the following user has access + label: Only the following users have access class: medium - description: Only this certain user will have access to this site. + description: Add one or more usernames separated with comma. contains: type: radio label: This folder contains diff --git a/system/author/partials/editorNavi.twig b/system/author/partials/editorNavi.twig index 902433a..811840b 100644 --- a/system/author/partials/editorNavi.twig +++ b/system/author/partials/editorNavi.twig @@ -1,7 +1,7 @@