1
0
mirror of https://github.com/typemill/typemill.git synced 2025-10-16 15:16:14 +02:00

Version 1.2.17: Format inline elements

This commit is contained in:
trendschau
2019-11-03 15:47:40 +01:00
parent a52dc8a166
commit 7dccfdbfcb
14 changed files with 278 additions and 58 deletions

View File

@@ -87,7 +87,7 @@ class ContentApiController extends ContentController
}
public function unpublishArticle(Request $request, Response $response, $args)
{
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri();
@@ -129,6 +129,19 @@ class ContentApiController extends ContentController
}
}
# check if it is a folder and if the folder has published pages.
$message = false;
if($this->item->elementType == 'folder')
{
foreach($this->item->folderContent as $folderContent)
{
if($folderContent->status == 'published')
{
$message = 'There are published pages within this folder. The pages are not visible on your website anymore.';
}
}
}
# update the file
$delete = $this->deleteContentFiles(['md']);
@@ -143,7 +156,7 @@ class ContentApiController extends ContentController
# dispatch event
$this->c->dispatcher->dispatch('onPageUnpublished', new OnPageUnpublished($this->item));
return $response->withJson(['success'], 200);
return $response->withJson(['success' => ['message' => $message]], 200);
}
else
{

View File

@@ -45,7 +45,7 @@ class SettingsController extends Controller
$params = $request->getParams();
$newSettings = isset($params['settings']) ? $params['settings'] : false;
$validate = new Validation();
if($newSettings)
{
/* make sure only allowed fields are stored */
@@ -54,6 +54,7 @@ class SettingsController extends Controller
'author' => $newSettings['author'],
'copyright' => $newSettings['copyright'],
'year' => $newSettings['year'],
'language' => $newSettings['language'],
'startpage' => isset($newSettings['startpage']) ? true : false,
'editor' => $newSettings['editor'],
);
@@ -562,6 +563,13 @@ class SettingsController extends Controller
if($validate->username($params['username']))
{
$user->deleteUser($params['username']);
# if user deleted his own account
if($_SESSION['user'] == $params['username'])
{
session_destroy();
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
$this->c->flash->addMessage('info', 'Say goodbye, the user is gone!');
return $response->withRedirect($this->c->router->pathFor('user.list'));

View File

@@ -15,16 +15,36 @@ class SetupController extends Controller
$checkFolder = new Write();
$systemcheck = array();
# check folders and create them if possible
try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
# check php-version
if (version_compare(phpversion(), '7.0.0', '<')) {
$systemcheck['error'][] = 'The PHP-version of your server is ' . phpversion() . ' and Typemill needs at least 7.0.0';
}
# check if mod rewrite is enabled
$modules = apache_get_modules();
if(!in_array('mod_rewrite', $modules))
{
$systemcheck['error'][] = 'The apache module "mod_rewrite" is not enabled.';
}
# check if GD extension is enabled
if(!extension_loaded('gd')){
$systemcheck['error'][] = 'The php-extension GD for image manipulation is not enabled.';
}
$setuperrors = empty($systemcheck) ? false : 'Some system requirements for Typemill are missing.';
$systemcheck = empty($systemcheck) ? false : $systemcheck;
return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
return $this->render($response, 'auth/setup.twig', array( 'messages' => $setuperror, 'systemcheck' => $systemcheck ));
}
public function create($request, $response, $args)

View File

@@ -4,9 +4,19 @@
{% block content %}
<div class="setupWrapper">
{% if systemcheck %}
<h2>Missing Requirements</h2>
<ul style="color:red;padding: 0 14px">
{% for systemerror in systemcheck %}
<li style="margin: 5px 0">{{ systemerror }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="authformWrapper">
<form method="POST" action="{{ path_for('setup.create') }}" autocomplete="off">
<fieldset class="auth">
<div class="formElement{{ errors.username ? ' errors' : '' }}">
<label for="username">Username <abbr title="required">*</abbr></label>

View File

@@ -11,7 +11,7 @@
<h1>Hurra!</h1>
<p>Your account has been created and you are logged in now.</p>
<p><strong>Next step:</strong> Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.</p>
<p><strong>New:</strong> Typemill ships with a new search plugin now. Just activate the plugin and enjoy!!</p>
<p><strong>New:</strong>Not sure how to add strong, emphasis and inline-code with Markdown? We have buttons for that now!!</p>
<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="https://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/typemill/typemill"><i class="icon-link-ext"></i> github</a>.</p>
</div>
<a class="button" href="{{ path_for('settings.show') }}">Configure your website</a>

View File

@@ -1660,6 +1660,49 @@ button.format-item.close:hover{
border: 1px solid #cc4146;
}
/************************
** INLINE FORMATG BAR **
************************/
/* format menu */
.inlineFormatBar {
height: 30px;
padding: 5px 10px;
background: #333;
border-radius: 3px;
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -100%);
transition: 0.2s all;
display: flex;
justify-content: center;
align-items: center;
}
/* Triangle below format popup */
.inlineFormatBar:after {
content: '';
position: absolute;
left: 50%;
bottom: -5px;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #333;
}
.inlineFormatItem {
color: #FFF;
cursor: pointer;
}
.inlineFormatItem:hover {
color: #1199ff;
}
.inlineFormatItem + .inlineFormatItem {
margin-left: 10px;
}
/************************
** BLOX EDITOR CONTENT **
************************/

View File

@@ -78,7 +78,7 @@ const contentComponent = Vue.component('content-block', {
this.compmarkdown = $event;
this.$nextTick(function () {
this.$refs.preview.style.minHeight = this.$refs.component.offsetHeight + 'px';
});
});
},
switchToEditMode: function()
{
@@ -86,7 +86,7 @@ const contentComponent = Vue.component('content-block', {
eventBus.$emit('closeComponents');
self = this;
self.$root.$data.freeze = true; /* freeze the data */
self.$root.$data.sortdisabled = true; /* disable sorting */
self.$root.$data.sortdisabled = true; /* disable sorting */
this.preview = 'hidden'; /* hide the html-preview */
this.edit = true; /* show the edit-mode */
this.compmarkdown = self.$root.$data.blockMarkdown; /* get markdown data */
@@ -392,6 +392,123 @@ const contentComponent = Vue.component('content-block', {
},
})
const inlineFormatsComponent = Vue.component('inline-formats', {
template: '<div><div :style="{ left: `${x}px`, top: `${y}px` }" @mousedown.prevent="" v-show="showInlineFormat" id="formatBar" class="inlineFormatBar">' +
'<span class="inlineFormatItem" @mousedown.prevent="formatBold"><i class="icon-bold"></i></span>' +
'<span class="inlineFormatItem" @mousedown.prevent="formatItalic"><i class="icon-italic"></i></span>' +
'<span class="inlineFormatItem" @mousedown.prevent="formatCode"><i class="icon-code"></i></span>' +
'</div><slot></slot></div>',
data: function(){
return {
formatBar: false,
startX: 0,
startY: 0,
x: 0,
y: 0,
textComponent: '',
selectedText: '',
startPos: false,
endPos: false,
showInlineFormat: false,
}
},
mounted: function() {
this.formatBar = document.getElementById('formatBar');
window.addEventListener('mouseup', this.onMouseup),
window.addEventListener('mousedown', this.onMousedown)
},
beforeDestroy: function() {
window.removeEventListener('mouseup', this.onMouseup),
window.removeEventListener('mousedown', this.onMousedown)
},
computed: {
highlightableEl () {
return this.$slots.default[0].elm
}
},
methods: {
onMousedown: function(event) {
this.startX = event.offsetX;
this.startY = event.offsetY;
},
onMouseup: function(event) {
/* if click is on format popup */
if(this.formatBar.contains(event.target))
{
return;
}
/* if click is outside the textarea */
if(!this.highlightableEl.contains(event.target))
{
this.showInlineFormat = false;
return;
}
this.textComponent = document.getElementsByClassName("mdcontent")[0];
/* grab the selected text */
if (document.selection != undefined)
{
this.textComponent.focus();
var sel = document.selection.createRange();
selectedText = sel.text;
}
/* Mozilla version */
else if (this.textComponent.selectionStart != undefined)
{
this.startPos = this.textComponent.selectionStart;
this.endPos = this.textComponent.selectionEnd;
selectedText = this.textComponent.value.substring(this.startPos, this.endPos)
}
var trimmedSelection = selectedText.replace(/\s/g, '');
if(trimmedSelection.length == 0)
{
this.showInlineFormat = false;
return;
}
/* determine the width of selection to position the format bar */
if(event.offsetX > this.startX)
{
var width = event.offsetX - this.startX;
this.x = event.offsetX - (width/2);
}
else
{
var width = this.startX - event.offsetX;
this.x = event.offsetX + (width/2);
}
this.y = event.offsetY - 15;
this.showInlineFormat = true;
this.selectedText = selectedText;
},
formatBold()
{
content = this.textComponent.value;
content = content.substring(0, this.startPos) + '**' + this.selectedText + '**' + content.substring(this.endPos, content.length);
this.$parent.updatemarkdown(content);
},
formatItalic()
{
content = this.textComponent.value;
content = content.substring(0, this.startPos) + '_' + this.selectedText + '_' + content.substring(this.endPos, content.length);
this.$parent.updatemarkdown(content);
},
formatCode()
{
content = this.textComponent.value;
content = content.substring(0, this.startPos) + '`' + this.selectedText + '`' + content.substring(this.endPos, content.length);
this.$parent.updatemarkdown(content);
}
}
})
const titleComponent = Vue.component('title-component', {
props: ['compmarkdown', 'disabled'],
template: '<div><input type="text" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></div>',
@@ -411,16 +528,18 @@ const markdownComponent = Vue.component('markdown-component', {
props: ['compmarkdown', 'disabled'],
template: '<div>' +
'<div class="contenttype"><i class="icon-paragraph"></i></div>' +
'<textarea class="mdcontent" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' +
'</div>',
'<inline-formats>' +
'<textarea id="activeEdit" class="mdcontent" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown(event.target.value)"></textarea>' +
'</inline-formats>' +
'</div>',
mounted: function(){
this.$refs.markdown.focus();
autosize(document.querySelectorAll('textarea'));
},
methods: {
updatemarkdown: function(event)
updatemarkdown: function(value)
{
this.$emit('updatedMarkdown', event.target.value);
this.$emit('updatedMarkdown', value);
},
},
})
@@ -509,8 +628,10 @@ const quoteComponent = Vue.component('quote-component', {
template: '<div>' +
'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +
'<div class="contenttype"><i class="icon-quote-left"></i></div>' +
'<textarea class="mdcontent" ref="markdown" v-model="quote" :disabled="disabled" @input="createmarkdown"></textarea>' +
'</div>',
'<inline-formats>' +
'<textarea class="mdcontent" ref="markdown" v-model="quote" :disabled="disabled" @input="updatemarkdown(event.target.value)"></textarea>' +
'</inline-formats>' +
'</div>',
data: function(){
return {
quote: ''
@@ -521,7 +642,7 @@ const quoteComponent = Vue.component('quote-component', {
if(this.compmarkdown)
{
var quote = this.compmarkdown.replace("> ", "");
quote = this.compmarkdown.replace(">", "");
quote = this.compmarkdown.replace(">", "").trim();
this.quote = quote;
}
this.$nextTick(function () {
@@ -529,14 +650,10 @@ const quoteComponent = Vue.component('quote-component', {
});
},
methods: {
createmarkdown: function(event)
{
this.quote = event.target.value;
var quote = '> ' + event.target.value;
this.updatemarkdown(quote);
},
updatemarkdown: function(quote)
updatemarkdown: function(value)
{
this.quote = value;
var quote = '> ' + value;
this.$emit('updatedMarkdown', quote);
},
},
@@ -546,7 +663,9 @@ const ulistComponent = Vue.component('ulist-component', {
props: ['compmarkdown', 'disabled'],
template: '<div>' +
'<div class="contenttype"><i class="icon-list-bullet"></i></div>' +
'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' +
'<inline-formats>' +
'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown(event.target.value)"></textarea>' +
'</inline-formats>' +
'</div>',
mounted: function(){
this.$refs.markdown.focus();
@@ -581,9 +700,9 @@ const ulistComponent = Vue.component('ulist-component', {
});
},
methods: {
updatemarkdown: function(event)
updatemarkdown: function(value)
{
this.$emit('updatedMarkdown', event.target.value);
this.$emit('updatedMarkdown', value);
},
},
})
@@ -592,7 +711,9 @@ const olistComponent = Vue.component('olist-component', {
props: ['compmarkdown', 'disabled'],
template: '<div>' +
'<div class="contenttype"><i class="icon-list-numbered"></i></div>' +
'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' +
'<inline-formats>' +
'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown(event.target.value)"></textarea>' +
'</inline-formats>' +
'</div>',
mounted: function(){
this.$refs.markdown.focus();
@@ -605,9 +726,9 @@ const olistComponent = Vue.component('olist-component', {
});
},
methods: {
updatemarkdown: function(event)
updatemarkdown: function(value)
{
this.$emit('updatedMarkdown', event.target.value);
this.$emit('updatedMarkdown', value);
},
},
})
@@ -911,7 +1032,7 @@ const definitionComponent = Vue.component('definition-component', {
template: '<div class="definitionList">' +
'<div class="contenttype"><i class="icon-colon"></i></div>' +
'<draggable v-model="definitionList" :animation="150" @end="moveDefinition">' +
'<div class="definitionRow" v-for="(definition, dindex) in definitionList" :key="definition.id">' +
'<div class="definitionRow" v-for="(definition, dindex) in definitionList" :key="definition.id">' +
'<i class="icon-resize-vertical"></i>' +
'<input type="text" class="definitionTerm" placeholder="term" :value="definition.term" :disabled="disabled" @input="updateterm($event,dindex)" @blur="updateMarkdown">' +
'<i class="icon-colon"></i>' +
@@ -1337,6 +1458,7 @@ let editor = new Vue({
'table-component': tableComponent,
'definition-component': definitionComponent,
'math-component': mathComponent,
'inline-formats' : inlineFormatsComponent,
},
data: {
root: document.getElementById("main").dataset.url,
@@ -1580,7 +1702,7 @@ let editor = new Vue({
renderMathInElement(document.getElementById("blox-"+elementid));
});
}
if (typeof MathJax !== false) {
if (typeof MathJax !== 'undefined') {
self.$nextTick(function () {
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"blox-"+elementid]);
});

View File

@@ -203,24 +203,24 @@ let publishController = new Vue({
self.publishResult = "fail";
self.errors.message = "You are probably logged out. Please backup your changes, login and then try again."
}
else if(httpStatus != 200)
{
self.publishDisabled = false;
self.publishResult = "fail";
self.errors.message = "Something went wrong, please refresh the page and try again."
}
else if(response)
{
var result = JSON.parse(response);
self.modalWindow = false;
if(httpStatus != 200)
{
self.publishDisabled = false;
self.publishResult = "fail";
self.errors.message = "Something went wrong, please refresh the page and try again.";
}
if(result.errors)
{
self.modalWindow = "modal";
if(result.errors.message){ self.errors.message = result.errors.message };
}
else if(result.url)
{
self.modalWindow = "modal";
window.location.replace(result.url);
}
}

View File

@@ -3,6 +3,8 @@
{% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %}
{% set linebreaks = old.settings.linebreaks ? old.settings.linebreaks : settings.linebreaks %}
{% set year = settings.year ? settings.year : "now"|date("Y") %}
{% set mylang = settings.language ? settings.language : locale %}
{% set mycopy = settings.copyright ? settings.copyright : "@" %}
{% block content %}
@@ -36,7 +38,7 @@
<label for="settings[copyright]">Copyright/Licence</label>
<select name="settings[copyright]" id="copyright">
{% for copy in copyright %}
<option value="{{ copy }}"{% if copy == old.settings.copyright %} selected{% endif %}>{{ copy }}</option>
<option value="{{ copy }}"{% if copy == old.settings.copyright or copy == mycopy %} selected{% endif %}>{{ copy }}</option>
{% endfor %}
</select>
{% if errors.settings.copyright %}
@@ -52,7 +54,7 @@
<label for="settings[language]">Language</label>
<select name="settings[language]" id="language">
{% for key,lang in languages %}
<option value="{{ key }}"{% if (key == old.settings.language or key == locale) %} selected{% endif %}>{{ lang }}</option>
<option value="{{ key }}"{% if (key == old.settings.language or key == mylang) %} selected{% endif %}>{{ lang }}</option>
{% endfor %}
</select>
{% if errors.settings.language %}

View File

@@ -14,7 +14,7 @@
{% for themeName, theme in themes %}
<form method="POST" action="{{ path_for('themes.save') }}">
<form method="POST" id="theme-{{ themeName }}" action="{{ path_for('themes.save') }}">
<fieldset class="card{{ errors[themeName] ? ' errors' : '' }}">
@@ -67,10 +67,10 @@
<input type="hidden" name="theme" value="{{ themeName }}">
<div class="medium">
<button type="button" class="theme-button fc-settings{{ (settings.theme == themeName) ? ' active' : '' }}{{ theme.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ theme.forms.fields|length > 0 ? 'Settings <span class="button-arrow"></span>' : 'No Settings'}}</button>
<button type="button" id="{{themeName}}-toggle" class="theme-button fc-settings{{ (settings.theme == themeName) ? ' active' : '' }}{{ theme.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ theme.forms.fields|length > 0 ? 'Settings <span class="button-arrow"></span>' : 'No Settings'}}</button>
</div>
<div class="medium">
<input type="submit" value="Save Theme" />
<input type="submit" id="{{themeName}}-submit" value="Save Theme" />
</div>
</div>
</div>