1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-10 08:04:43 +02:00

Version 1.4.6: Small fixes and additions

This commit is contained in:
trendschau
2021-05-18 21:28:55 +02:00
parent 812e4b1c26
commit 84bf68f690
49 changed files with 390 additions and 132 deletions

View File

@@ -133,7 +133,7 @@ class AuthController extends Controller
}
# if user is allowed to view content-area
if($this->c->acl->isAllowed($userdata['userrole'], 'content', 'view'))
if($this->c->acl->hasRole($userdata['userrole']) && $this->c->acl->isAllowed($userdata['userrole'], 'content', 'view'))
{
$settings = $this->c->get('settings');
$editor = (isset($settings['editor']) && $settings['editor'] == 'visual') ? 'visual' : 'raw';

View File

@@ -11,6 +11,7 @@ use Typemill\Models\ProcessFile;
use Typemill\Models\ProcessImage;
use Typemill\Events\OnUserfieldsLoaded;
use Typemill\Events\OnSystemnaviLoaded;
use Typemill\Events\OnUserDeleted;
class SettingsController extends Controller
{
@@ -479,8 +480,24 @@ class SettingsController extends Controller
}
else
{
# fetch the original settings from the folder to get the field definitions
$originalSettings = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
# check if the plugin has dependencies
if(isset($userInput[$pluginName]['active']) && isset($originalSettings['dependencies']))
{
foreach($originalSettings['dependencies'] as $dependency)
{
if(!isset($userInput[$dependency]['active']) OR !$userInput[$dependency]['active'])
{
$this->c->flash->addMessage('error', 'Activate the plugin ' . $dependency . ' before you activate the plugin ' . $pluginName);
return $response->withRedirect($this->c->router->pathFor('plugins.show'));
}
}
}
/* validate the user-input */
$imageFields = $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate);
$imageFields = $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate, $originalSettings);
/* use the input data */
$pluginSettings[$pluginName] = $userInput[$pluginName];
@@ -615,6 +632,11 @@ class SettingsController extends Controller
# set navigation active
$navigation['Users']['active'] = true;
if(isset($userdata['lastlogin']))
{
$userdata['lastlogin'] = date("d.m.Y H:i:s", $userdata['lastlogin']);
}
return $this->render($response, 'settings/user.twig', array(
'settings' => $settings,
@@ -876,13 +898,22 @@ class SettingsController extends Controller
if($_SESSION['user'] !== $params['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.account'));
}
}
}
if($validate->username($params['username']))
{
$userdata = $user->getSecureUser($params['username']);
if(!$userdata)
{
$this->c->flash->addMessage('error', 'Ups, we did not find that user');
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
$user->deleteUser($params['username']);
$this->c->dispatcher->dispatch('onUserDeleted', new OnUserDeleted($userdata));
# if user deleted his own account
if($_SESSION['user'] == $params['username'])
{
@@ -894,7 +925,7 @@ class SettingsController extends Controller
return $response->withRedirect($this->c->router->pathFor('user.list'));
}
$this->c->flash->addMessage('error', 'Ups, we did not find that user');
$this->c->flash->addMessage('error', 'Ups, it is not a valid username');
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
}
@@ -937,6 +968,12 @@ class SettingsController extends Controller
private function getUserFields($role)
{
# if a plugin with a role has been deactivated, then users with the role throw an error, so set them back to member...
if(!$this->c->acl->hasRole($role))
{
$role = 'member';
}
$fields = [];
$fields['username'] = ['label' => 'Username (read only)', 'type' => 'text', 'readonly' => true];
$fields['firstname'] = ['label' => 'First Name', 'type' => 'text'];

View File

@@ -0,0 +1,14 @@
<?php
namespace Typemill\Events;
use Symfony\Component\EventDispatcher\Event;
/**
* Event for the page tree.
*/
class OnUserConfirmed extends BaseEvent
{
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Typemill\Events;
use Symfony\Component\EventDispatcher\Event;
/**
* Event for the page tree.
*/
class OnUserDeleted extends BaseEvent
{
}

View File

@@ -71,7 +71,8 @@ class Field
'min',
'max',
'class',
'pattern'
'pattern',
'steps'
);
/* defines additional data, that are allowed for fields */

View File

@@ -21,6 +21,9 @@ class User extends WriteYaml
$usernames[] = str_replace('.yaml', '', $userfile);
}
usort($usernames, 'strnatcasecmp');
return $usernames;
}
@@ -235,8 +238,13 @@ class User extends WriteYaml
{
if(file_exists($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt'))
{
# read and return the file
$usermailindex = file($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt');
# unserialize and return the file
$usermailindex = unserialize(file_get_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt'));
if($usermailindex)
{
return $usermailindex;
}
}
$usernames = $this->getUsers();
@@ -249,7 +257,7 @@ class User extends WriteYaml
$usermailindex[$userdata['email']] = $username;
}
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt', var_export($usermailindex, TRUE));
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt', serialize($usermailindex));
return $usermailindex;
}
@@ -304,8 +312,12 @@ class User extends WriteYaml
{
if(file_exists($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt'))
{
# read and return the file
$userroleindex = file($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt');
# unserialize and return the file
$userroleindex = unserialize(file_get_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt'));
if($userroleindex)
{
return $userroleindex;
}
}
$usernames = $this->getUsers();
@@ -318,7 +330,7 @@ class User extends WriteYaml
$userroleindex[$userdata['userrole']][] = $username;
}
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt', var_export($userroleindex, TRUE));
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt', serialize($userroleindex));
return $userroleindex;
}

View File

@@ -8,9 +8,13 @@ use Typemill\Models\WriteYaml;
use Typemill\Extensions\ParsedownExtension;
abstract class Plugin implements EventSubscriberInterface
{
{
protected $container;
protected $path;
protected $adminpath = false;
/**
* Constructor
*
@@ -19,6 +23,13 @@ abstract class Plugin implements EventSubscriberInterface
public function __construct($container)
{
$this->container = $container;
$this->path = trim($this->container['request']->getUri()->getPath(),"/");
if(substr($this->path, 0, 3) === "tm/")
{
$this->adminpath = true;
}
}
protected function isXhr()

View File

@@ -73,9 +73,33 @@ a, a:link, a:visited, a:focus, a:hover, a:active, .link, button, .button, .tab-b
.hover-bg-white.active{
background-color: #fff;
}
.tm-gray{
color:#f9f8f6;
}
.bg-tm-gray{
background:#f9f8f6;
}
.b--tm-gray{
border-color:#f9f8f6;
}
.hover-tm-gray:hover{
color:#f9f8f6;
}
.hover-bg-tm-gray:hover{
background:#f9f8f6;
}
.hover-b--tm-gray:hover,.hover-b--tm-gray.active{
border-color:#f9f8f6;
}
/*
.w-100{
width:100%!important;
}
*/
.w-100{
width:100%;
}
.w-15{
width: 15%;
}

View File

@@ -39,7 +39,7 @@
<div v-if="bloxOverlay" class="blox-overlay"></div>
</transition>
<div id="initial-content">
<div id="initial-content" v-pre>
<div class="blox title">{{title}}</div>

View File

@@ -1633,7 +1633,7 @@ const imageComponent = Vue.component('image-component', {
publishController.errors.message = "Looks like you are logged out. Please login and try again.";
}
*/
if(error.response.data.errors.message)
if(error.response)
{
publishController.errors.message = error.response.data.errors.message;
}
@@ -1809,7 +1809,7 @@ const fileComponent = Vue.component('file-component', {
})
.catch(function (error)
{
if(error.response.data.errors.message)
if(error.response)
{
publishController.errors.message = error.response.data.errors.message;
}
@@ -1960,8 +1960,9 @@ let editor = new Vue({
})
.catch(function (error)
{
if(error)
publishController.publishDisabled = false;
if(error.response.data.errors.message)
if(error.response)
{
publishController.errors.message = error.response.data.errors.message;
}

View File

@@ -4,8 +4,6 @@ Vue.component('searchbox', {
return {
filter: 'username',
searchterm: '',
searchmode: true,
clearmode: false,
userroles: userroles,
}
},
@@ -20,8 +18,8 @@ Vue.component('searchbox', {
'<option v-for="role in userroles">{{role}}</option>' +
'</select>' +
'<input v-else type="text" class="usersearch" v-model="searchterm">' +
'<button v-if="searchmode" class="searchbutton search" @click.prevent="startSearch()">Search</button>' +
'<button v-if="clearmode" class="searchbutton clear" @click.prevent="clearSearch()">Clear</button>' +
'<button class="searchbutton search" @click.prevent="startSearch()">Search</button>' +
'<button class="searchbutton clear" @click.prevent="clearSearch()">Clear</button>' +
'</div>' +
'<div v-if="error" class="error pt1 f6">{{error}}</div>' +
'<div v-if="this.filter == \'usermail\'" class="description pt1">You can use the asterisk (*) wildcard to search for name@* or *@domain.com.</div>' +
@@ -38,8 +36,6 @@ Vue.component('searchbox', {
this.$root.error = 'Please enter at least 3 characters';
return;
}
this.searchmode = false;
this.clearmode = true;
this.$root.search(this.searchterm, this.filter);
}
},
@@ -47,14 +43,11 @@ Vue.component('searchbox', {
{
this.$root.error = false;
this.searchterm = '';
this.searchmode = true;
this.clearmode = false;
this.$root.clear(this.filter);
},
setFilter: function(filter)
{
this.clearSearch();
this.searchterm = '';
this.filter = filter;
if(filter == 'userrole')
{
@@ -72,16 +65,22 @@ Vue.component('searchbox', {
}
})
Vue.component('userrow', {
props: ['user'],
template: '<li class="row">' +
'<ul>' +
'<li class="col username">{{ user.username }}</li>' +
'<li class="col userrole">{{ user.userrole }}</li>' +
'<li class="col email">{{ user.email }}</li>' +
'<li class="col edit"><a :href="getEditLink(user.username)">edit</a></li>' +
'</ul>' +
'</li>',
Vue.component('usertable', {
props: ['userdata'],
template: '<table class="w-100 mw8" cellspacing="0">' +
'<tr class="white">' +
'<th class="pa3 bg-tm-green ba b--white normal tl">Username</th>' +
'<th class="pa3 bg-tm-green ba b--white normal tl">Userrole</th>' +
'<th class="pa3 bg-tm-green ba b--white normal tl">E-Mail</th>' +
'<th class="pa3 bg-tm-green ba b--white normal tl">Edit</th>' +
'</tr>' +
'<tr v-for="user,index in userdata" key="username">' +
'<td class="pa3 bg-tm-gray ba b--white tl">{{ user.username }}</td>' +
'<td class="pa3 bg-tm-gray ba b--white tl">{{ user.userrole }}</td>' +
'<td class="pa3 bg-tm-gray ba b--white tl">{{ user.email }}</td>' +
'<td class="pa3 bg-tm-gray ba b--white tl"><a :href="getEditLink(user.username)" class="link tm-red no-underline underline-hover">edit</a></td>' +
'</tr>' +
'</table>',
methods: {
getEditLink: function(username){
return this.$root.$data.root + '/tm/user/' + username;
@@ -191,7 +190,7 @@ let userlist = new Vue({
{
if(filter == 'username')
{
let result = this.filterItems(this.usernames, term);
let result = this.filterItems(this.holdusernames, term);
this.usernames = result;

View File

@@ -16,77 +16,77 @@ ADD_ROW_ABOVE: add row above
ADD_ROW_BELOW: add row below
ADMINISTRATOR: administrator
ALL: all
ALL_USERS: All users
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Alternative Text for the hero image
ALT_TEXT: Alt-Text
AUTHOR: Author
AUTHOR_DESCRIPTION_(MARKDOWN): Author-Description (Markdown)
ALL_USERS: all users
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: alternative text for the hero image
ALT_TEXT: alt-text
AUTHOR: author
AUTHOR_DESCRIPTION_(MARKDOWN): author-description (markdown)
BACK_TO_STARTPAGE: back to startpage
BOLD: bold
BOTTOM: Bottom
BOTTOM: bottom
BROWSE: BROWSE
BULLET_LIST: Bullet List
BULLET_LIST: bullet list
BY: by
CANCEL: cancel
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Can be used for author line in frontend.
CAPTION: Caption
CAPTION: caption
CELL: cell
CENTER: Center
CENTER: center
CHECK: check
CHOOSE_FILE: Choose file
CLASS: Class
CLOSE_LIBRARY: Close Library
CODE: Code
CHOOSE_FILE: choose file
CLASS: class
CLOSE_LIBRARY: close library
CODE: code
COG: cog
CONTENT: Content
COPYRIGHT: Copyright
CONTENT: content
COPYRIGHT: copyright
CREATED_AT_(READONLY): Created at (read only)
CREATED_AT_(READ_ONLY): Created at (readonly)
CREATE_NEW_USER: Create New User
CREATE_USER: Create User
CREATE_NEW_USER: Create new user
CREATE_USER: Create user
CROSS: cross
CUSTOM_CSS: Custom CSS
DEFINITION: Definition List
DEFINITION_LIST: Definition List
DEFINITION: definition list
DEFINITION_LIST: definition list
DELETE: delete
DELETE_CLOSE: delete/close
DELETE_COLUMN: delete column
DELETE_CONTENT_BLOCK: delete content-block
DELETE_PAGE: Delete page
DELETE_PAGE: delete page
DELETE_ROW: delete row
DELETE_USER: Delete User
DELETE_USER: delete user
DESCRIPTION: description
DISCARD: Discard
DISCARD_CHANGES: Discard Changes
DISCARD: discard
DISCARD_CHANGES: discard changes
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Do you really want to delete the user
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Do you really want to delete this page?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Do you want to discard your changes and set the content back to the live version?
DRAFT: Draft
DRAFT: draft
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: upload an image
DUTCH__FLEMISH: Dutch, Flemish
EDIT: edit
EDITOR: editor
EDIT_USER: Edit User
EDIT_USER: edit user
ENGLISH: English
EXTERNAL_LINK: external-link
E_G_: e.g.
E_MAIL: E-Mail
FAVICON: Favicon
FILE: File
E_MAIL: e-mail
FAVICON: favicon
FILE: file
FILES: Files
FIRST_NAME: First Name
FIRST_NAME: first name
FOLDER: folder
FORGOT_PASSWORD: Forgot password
FORMAT: Format
FORGOT_PASSWORD: forgot password
FORMAT: format
FRENCH: French
GENERAL_PRESENTATION: General Presentation
GERMAN: German
GOOGLE_SITEMAP: Google Sitemap
GOOGLE_SITEMAP: google sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Has edit rights for this article.
HEAD: Head
HEADLINE: Headline
HEADLINE_ANCHORS: Headline Anchors
HERO_IMAGE: Hero Image
HEADLINE_ANCHORS: Headline anchors
HERO_IMAGE: Hero image
HIDE: Hide
HIDE_PAGE_FROM_NAVIGATION: Hide page from navigation
HOME: home

View File

@@ -41,6 +41,15 @@
</ul>
</div>
<div class="cardInner cardFields{{ errors[pluginName] ? ' open' : '' }}">
{% if plugin.dependencies %}
<div class="w-100 mt3 pa3 pb4 bg-tm-green white">
<p>Please make sure that you have installed and activated the following plugins:</p>
{% for dependency in plugin.dependencies %}
<span class="pa2 mr2 br1 bg-tm-gray black">{{ dependency }}</span>
{% endfor %}
</div>
{% endif %}
{% for field in plugin.forms.fields %}

View File

@@ -16,10 +16,11 @@
<header class="headline">
<h1>{{ __('System') }} <span class="version-number">v. {{settings.version}}</span></h1>
<p>New here? Then watch the <a class="link tm-red underline-hover" href="https://www.youtube.com/watch?v=7yvlwXJL9dc" target="_blank">video-tutorial</a> about configurations for typemill on YouTube.</p>
</header>
<div id="typemill" class="fc-system-version update-banner">{{ settings.version ? settings.version : 'Unknown' }}</div>
<fieldset>
<div class="medium{{ errors.settings.title ? ' error' : '' }}">
@@ -142,6 +143,7 @@
<header class="headline">
<h2>{{ __('Access Control') }}</h2>
<p>{{ __('Limit the access for the whole website or for each page individually. If you activate the website restriction or the page restrictions, then sessions will be used in frontend.') }}</p>
<p>New here? Then watch the <a class="link tm-red underline-hover" href="https://www.youtube.com/watch?v=UW_m-4g1kAA" target="_blank">video-tutorial</a> about access rights for typemill on YouTube.</p>
</header>
<div class="large{{ errors.settings.access ? ' error' : '' }}">
<label for="settings[access]">{{ __('Website Restriction') }}</label>

View File

@@ -42,9 +42,3 @@
</div>
{% endblock %}
{% block javascript %}
<script src="{{ base_url }}/system/author/js/vue-userlist.js"></script>
{% endblock %}

View File

@@ -2,39 +2,24 @@
{% block title %}{{ __('User') }}{% endblock %}
{% block content %}
<div class="formWrapper">
<section class="pa3">
<section id="users" class="settings">
<div class="mh3">
<header class="headline">
<h1>{{ __('All Users') }} </h1>
</header>
<h1>{{ __('All Users') }} </h1>
<div id="userlist" v-cloak>
<div class="usersearchbox">
<div class="mb3">
<searchbox :error="error"></searchbox>
</div>
<ul class="userlist">
<li class="row header">
<ul>
<li class="col username">{{ __('Username') }}</li><li
class="col userrole">{{ __('Role') }}</li><li
class="col email">{{ __('E-Mail') }}</li><li
class="col edit">{{ __('Edit') }}</li>
</ul>
</li>
<userrow
v-for="user in userdata"
v-bind:key="user.name"
v-bind:user="user"
></userrow>
</ul>
<ul class="pagination" v-if="showpagination">
<div class="overflow-auto">
<usertable :userdata="userdata"></usertable>
</div>
<ul class="list pl0" v-if="showpagination">
<pagination
v-for="page in pages"
v-bind:key="page"
@@ -43,16 +28,17 @@
</ul>
</div>
<div class="medium">
<a class="button" href="{{ path_for('user.new') }}">{{ __('Create New User') }}</a>
<div class="mv4">
<a class="button dib" href="{{ path_for('user.new') }}">{{ __('Create New User') }}</a>
</div>
</section>
</div>
<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
{{ csrf_field() | raw }}
</div>
</section>
{% endblock %}