1
0
mirror of https://github.com/typemill/typemill.git synced 2025-07-25 00:02:28 +02:00

Version 1.2.0 Introducing a Basic Content Editor

This commit is contained in:
Sebastian
2018-06-25 18:04:32 +02:00
parent 9286cfd884
commit cfc8128161
34 changed files with 560 additions and 100 deletions

2
cache/lastCache.txt vendored
View File

@@ -1 +1 @@
1528305569
1529843776

View File

@@ -16,6 +16,6 @@ Whenever you want to publish a finished text work as a website and if you like w
If you want to create a blog, a wiki or a classic corporate website, please use a specialized CMS for that instead of TYPEMILL.
TYPEMILL is under heavy developement and not finished right now. It has an admin panel for settings, but it does not provide an online content editor at the moment. An online editor and different output formats like mobi and ePup are on the roadmap, so stay tuned.
TYPEMILL is under heavy developement and not finished right now. It has an admin panel for settings and a very basic editor for existing content. More options like delete or create new pages and manage your media-files will be added step by step in next weeks. Also different output formats like mobi and ePup are on the roadmap, so stay tuned.
For time being, you can use an offline markdown-editor like Typora and upload your content-files with a FTP software like FileZilla.
You have to use a FTP-software like FileZilla until the basic editing features are ready. Check the roadmap for more informations.

View File

@@ -7,6 +7,7 @@ This is what you can **do with TYPEMILL**:
- Create a website with simple files and folders.
- Use markdown for your content files.
- Use an admin panel to configure your site.
- Use the content editor to edit existing pages.
- Choose themes.
- Activate plugins. Check the [list of plugins](/writers/plugins) for that.
- Create your own theme with HTML, CSS and Twig (a template language for PHP).
@@ -21,4 +22,4 @@ This is, what **TYPEMILL does** for you:
- It adds hierarchic numbers to your chapters and pages.
- It generates a google sitemap, a last modified date and much more.
Right now there is no content editor, so you have to create and edit your content offline with your favourite markdown editor (e.g. Typora) and upload the files with an FTP software. But a content editor will follow soon and we have a great roadmap for TYPEMILL.
Right now there is only a simple content editor that provides basic editing of existing pages. If you want to create new pages or delete existing pages, then you have to use an offline markdown editor like Typora and a FTP software like FileZilla. I will add all basic features for the online-editor step by step within the next weeks.

View File

@@ -4,18 +4,29 @@ There are a lot of plans for future releases of TYPEMILL, but it also follows th
Here are some **milestones** of the past:
- Introduction of TYPEMILL version 1.0.0
- Added a google sitemap with version 1.0.1
- Added a table of contents tag (TOC) with version 1.0.5
- Introduced plugins with version 1.1.0
- Added an author panel for configurations with version 1.1.3
- Added math support (mathjax/katex) with version 1.1.5
- Introduction of TYPEMILL (v. 1.0.0)
- Added a google sitemap (v. 1.0.1)
- Added a table of contents tag (TOC) (v. 1.0.5)
- Introduced plugins (v. 1.1.0)
- Added an author panel for configurations (v. 1.1.3)
- Added math support (mathjax/katex) (v. 1.1.5)
- Added a basic content editor to change existing pages (v. 1.2.0)
And here is the **roadmap** for this year (2018):
- Add a basic markdown editor to manage your content online.
- Add an Image- and media-management.
- Add an advanced WYSIWYG markdown editor for a great writers experience.
- Editor: Delete content / pages (v. 1.2.1)
- Editor: Create new pages (v. 1.2.2)
- Editor: Move pages (v. 1.2.2)
- Editor: Save as draft or publish live (v. 1.2.3)
- Editor: Edit meta-information (v. 1.2.4)
- Editor: Markdown and HTML-preview (v. 1.2.5)
- Editor: Manage images and assets (v. 1.3.0)
- Editor: Add formatting options (v. 1.3.0)
- Editor: Create a solution for direct preview or WYSIWYG (1.3.0)
- Editor: Create the best author- and writing experience you have ever seen (1.4.0)
Other features with lower prio:
- Create additional output formats like mobi, epub and pdf.
- Create a clean API.
- More themes for special publications like documentations, books or lyrics.

View File

@@ -1,3 +1,7 @@
#About TYPEMILL
# About TYPEMILL
TYPEMILL is a simple flat file CMS to create a website like this. It transforms a bunch of **text files** (Markdown) into a **website** and generates a **navigation**. TYPEMILL is under construction and provides a simple admin area for settings right now. An online editor and different output formats for e-books like mobi and epub are on their way. If you are a developer: TYPEMILL already supports themes and plugins.
TYPEMILL is a simple flat file CMS to create a website like this. It transforms a bunch of **text files** (Markdown) into a **website** and generates a **navigation**.
TYPEMILL is under construction. Right now it provides only a very basic editor and a simple admin area for settings, plugins and themes. The author-experience will be improved step by step and output formats for e-books like mobi and epub are planned for the future.
If you are a developer, you can already create your own themes and your own plugins.

View File

@@ -13,9 +13,9 @@ Don't forget to make some folders and files writable (set permission to `774`):
- `\settings` folder and files
- `\content` folder and files
All settings and users are stored in the folder `settings`. You can manually edit these files, but it is not recommended because it can crash the system if done wrong.
All settings and users are stored in the folder `settings`. You can manually edit these files, but it is not recommended because it might crash the system if done wrong.
You can configure your system online, but there is no content editor yet. So for time beeing, you have to edit your content offline with a markdown editor and upload the files with an FTP software. If your changes are not immediately visible, press `F5` to refresh the cache.
You can configure your system online, but there is only a simple editor to change existing content right now. So for time beeing, you have to create new content offline with a markdown editor and upload the files with an FTP software. If your changes are not immediately visible, press `F5` to refresh the cache.
If you need more detailed instructions, please read on.
@@ -58,4 +58,4 @@ If you run your website with https (recommended) or if you want to redirect www-
## Run Locally
If you are a developer and if you want to run TYPEMILL locally, then simply download TYPEMILL (zip or git) and visit your local folder like `localhost/typemill`. No additional work is required.
If you are a developer and if you want to run TYPEMILL locally, then simply download TYPEMILL (zip or git) and visit your local folder like `localhost/typemill`. No additional work is required.

View File

@@ -1,13 +1,13 @@
# Settings
As of Version 1.1.3 you can edit all settings in the new authoring panel of TYPEMILL. Just visit the url `yourwebsite.com/tm/login` and go to settings after the login. There you can edit:
As of Version 1.1.3 you can edit all settings in the new author panel of TYPEMILL. Just visit the url `yourwebsite.com/tm/login` and go to settings after the login. There you can edit:
* The system (basic settings).
* Themes (choose themes and configure it).
* Plugins (activate plugins and configure them).
* Users (create, update and delete).
All settings are stored in the `\settings` folder of TYPEMILL. It is not recommended to edit the settings manually, because it can crash the system if done wrong.
All settings are stored in the `\settings` folder of TYPEMILL. It is not recommended to edit the settings manually, because it might crash the system if done wrong.
## Advanced Settings
@@ -17,5 +17,4 @@ There are some settings that are not available via the author panel. Most of the
displayErrorDetails: true
````
Don't forget to set it back to `false` before you deploy the website live. It is not secure to show the world your internal errors and many hosters will turn off all public error reports by default.
Don't forget to set it back to `false` before you deploy the website live. It is not secure to show the world your internal errors and many hosters will turn off all public error reports by default.

View File

@@ -10,4 +10,4 @@ You are a pro and don't want to read the whole manual? No problem, this is a qui
- **F5**: After some changes, use the `F5` key to refresh the navigation manually.
- **Lean back** and let TYPEMILL create a nice website for you.
The TYPEMILL system ships with this user manual in the content folder. So you can check folder and look how the files are written and the folders are organized.
The TYPEMILL system ships with this user manual in the content folder. Check how the files are written and how the folders are organized.

View File

@@ -8,12 +8,26 @@ https://yourwebsite.net/tm/login
You can also use the url `https://yourwebsite.net/setup` that redirects to the login screen.
## The Content Editor
In the **content area** of the author panel you can:
* Navigate through your existing content.
* Edit all existing content pages with markdown syntax.
There are several **limitations** right now:
* You cannot use HTML, JavaScript or any other code in the editor, only markdown-syntax is allowed.
* You cannot delete, create or reorder the pages right now (use FTP for this), but these features are on the way.
* There is no media-management right now.
The content editor has highest priority in the roadmap, so you can expect al lot of improvements in the next weeks.
## Settings, Themes and Plugins
In the **settings area** of the author panel you can:
* Configure your **system**.
* Choose and configure a **theme**.
* Activate and configure **plugins**.
* Manage **users**.
the **content area** of the author-panel is not ready yet, but it is on it's way and will be published in near future. Then you can create and manage all the content of your website online. For time being you have to create your content offline e.g. with a markdown editor and upload your content files with a FTP software.
* Manage **users**.

View File

@@ -1,3 +1,3 @@
# Google Sitemap
As of version 1.0.1, TYPEMILL creates a google sitemap in the cache folder. You can reach the sitemap with the url `https://yourwebsite.net/cache/sitemap.xml` and add the sitemap to the google search console. The sitemap will update once a day. You can also trigger a manual update with the F5 key (Windows) that refreshs the cache of your browser and the cache of TYPEMILL.
As of version 1.0.1, TYPEMILL creates a google sitemap in the cache folder. You can find the url for the sitemap in the system settings (basically it is something like `https://yourwebsite.net/cache/sitemap.xml`). Simply add the url to the google search console and you are donw. The sitemap will update once a day. You can also trigger a manual update with the F5 key (Windows) that refreshs the cache of your browser and the cache of TYPEMILL.

View File

@@ -1,12 +1,33 @@
#Release Notes
# Release Notes
This is the version history with some release notes.
## Version 1.2.0: Introducing a Basic Content Editor
_Release date: 25.06.2018_
**Please follow the instruction for simple updates** in the [documentation](/gettings-started/update), so simply update the `system` folder.
Version 1.2.0 introduces a very basic content editor and is a major milestone for the developement of TYPEMILL as a full CMS. With the editor, the author can only edit existing content with markdown syntax right now. It is not possible to delete content or to create new content. These features will be added very soon.
There are quite a lot of changes in the background:
* IMPORTANT: HTML and other code is now completely disabled. All code is disallowed in the content editor and all code-syntax will be escaped in the frontend. You can use markdown syntax for fenced code blocks and for inline code to display code-examples on pages.
* Vue.js is added.
* A content navigation is added.
* Save functionality is added with ajax.
* API-routes are added for managing content.
* The content of the editor is validated (might cause problems with lot of code-syntax).
* Errors are displayed in frontend.
* Appropriate server status is send.
* The twig-cache is disabled again. It might become an optional feature in future.
* URL for xml-sitemap is displayed correctly now.
## Version 1.1.7: Improved Session Management
_Release date: 04.06.2018_
**Please follow the instructions for minor updates** in the [documentation](/gettings-started/update). Please also update the Typemill theme.
**Please follow the instructions for simple updates** in the [documentation](/gettings-started/update). Please also update the Typemill theme.
- URL to google sitemap is not displayed in settings.
- Session Cookies are only set when authentication is required.

View File

@@ -1 +1,3 @@
TYPEMILL is a small flat file cms designed for **writers**. It creates websites based on markdown files and fits perfectly for text-works like studies, manuals or documentations. TYPEMILL is simple, lightweight and open source. Just download and start.
# Typemill
TYPEMILL is a small flat file cms designed for **writers**. It creates websites based on markdown files and is a perfect solution for text-works like studies, manuals or documentations. TYPEMILL is simple, lightweight and open source. Just download and start.

10
settings/README.md Normal file
View File

@@ -0,0 +1,10 @@
Settings are generated during the setup of the system.
If you are a developer, you can manually add the settings
````
displayErrorDetails: true
````
This will display the php errors during the developement process. Do not forget to disable the settings for the live environment (delete it or set it to false).

View File

@@ -16,7 +16,7 @@ class AuthController extends Controller
{
if(isset($_SESSION['login']))
{
return $response->withRedirect($this->c->router->pathFor('settings.show'));
return $response->withRedirect($this->c->router->pathFor('content.show'));
}
else
{
@@ -125,7 +125,7 @@ class AuthController extends Controller
$yaml->updateYaml('settings/users', '.logins', $logins);
}
return $response->withRedirect($this->c->router->pathFor('settings.show'));
return $response->withRedirect($this->c->router->pathFor('content.show'));
}
}

View File

@@ -5,10 +5,16 @@ namespace Typemill\Controllers;
use Slim\Views\Twig;
use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Models\Validation;
use Typemill\Models\Folder;
use Typemill\Models\Write;
use Typemill\Models\WriteYaml;
use Typemill\Models\WriteCache;
use \Symfony\Component\Yaml\Yaml;
use Typemill\Models\Helpers;
use Typemill\Extensions\ParsedownExtension;
use \Parsedown;
class ContentController extends Controller
{
@@ -51,6 +57,13 @@ class ContentController extends Controller
{
/* check, if there is an index-file in the root of the content folder */
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
/* if there is content (index.md), then add a marker for frontend, so ajax calls for homepage-index-urls work */
if($contentMD)
{
$item = new \stdClass;
$item->urlRel = 'is_homepage_index';
}
}
else
{
@@ -95,7 +108,104 @@ class ContentController extends Controller
$title = trim($contentParts[0], "# \t\n\r\0\x0B");
$content = trim($contentParts[1]);
}
return $this->render($response, 'content/content.twig', array('navigation' => $structure, 'title' => $title, 'content' => $content, 'item' => $item, 'settings' => $settings ));
}
}
public function updateArticle(Request $request, Response $response, $args)
{
/* Extract the parameters from get-call */
$params = $request->getParams();
/* validate input */
$validate = new Validation();
$vResult = $validate->editorInput($params);
if(is_array($vResult))
{
return $response->withJson(['errors' => $vResult], 422);
}
/* initiate variables and objects that we need */
$settings = $this->c->get('settings');
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
$uri = $request->getUri();
$base_url = $uri->getBaseUrl();
$write = new writeCache();
/* we will use the cached structure to find the url for the page-update. It acts as whitelist and is more secure than a file-path, for example. */
$structure = $write->getCache('cache', 'structure.txt');
/* if there is no structure, create a fresh structure */
if(!$structure)
{
$structure = $this->getFreshStructure($pathToContent, $write, $uri);
if(!$structure)
{
return $response->withJson(['errors' => ['content folder is empty']], 404);
}
}
/* if it is the homepage */
if($params['url'] == 'is_homepage_index')
{
$item = new \stdClass;
$item->elementType = 'folder';
$item->path = '';
}
else
{
/* search for the url in the structure */
$item = Folder::getItemForUrl($structure, $params['url']);
}
if(!$item)
{
return $response->withJson(['errors' => ['requested page-url not found']], 404);
}
if($item->elementType == 'folder')
{
$path = $item->path . DIRECTORY_SEPARATOR . 'index.md';
}
elseif($item->elementType == 'file')
{
$path = $item->path;
}
/* get the markdown file */
$mdFile = $write->getFile($settings['contentFolder'], $path);
if($mdFile)
{
/* merge title with content for complete markdown document */
$updatedContent = '# ' . $params['title'] . "\r\n\r\n" . $params['content'];
/* update the file */
$write->writeFile($settings['contentFolder'], $path, $updatedContent);
return $response->withJson(['success'], 200);
}
return $response->withJson(['errors' => ['requested markdown-file not found']], 404);
}
protected function getFreshStructure($pathToContent, $cache, $uri)
{
/* scan the content of the folder */
$structure = Folder::scanFolder($pathToContent);
/* if there is no content, render an empty page */
if(count($structure) == 0)
{
return false;
}
/* create an array of object with the whole content of the folder */
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
/* cache navigation */
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
return $structure;
}
}

View File

@@ -122,6 +122,7 @@ class PageController extends Controller
/* initialize parsedown */
$parsedown = new ParsedownExtension();
$parsedown->setSafeMode(true);
/* parse markdown-file to content-array */
$contentArray = $parsedown->text($contentMD);

View File

@@ -401,7 +401,18 @@ class ParsedownExtension extends \ParsedownExtra
return $Block;
}
}
/*
protected function blockFencedCodeComplete($Block)
{
$text = $Block['element']['element']['text'];
unset($Block['element']['element']['text']);
$Block['element']['element']['rawHtml'] = "<p>$text</p>";
$Block['element']['element']['allowRawHtmlInSafeMode'] = true;
return $Block;
}
*/
#
# Fenced MathJax
protected function blockFencedMathJaxLaTeX($Line)
@@ -449,5 +460,5 @@ class ParsedownExtension extends \ParsedownExtra
$text = $Block['element']['text'];
$Block['element']['text'] = "\$\$\n" . $text . "\n\$\$";
return $Block;
}
}
}

View File

@@ -63,6 +63,22 @@ class Validation
}
return false;
}, 'contains html');
Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields)
{
/* strip out code blocks and blockquotes */
$value = preg_replace('/[````][\s\S]+?[````]/', '', $value);
$value = preg_replace('/[```][\s\S]+?[```]/', '', $value);
$value = preg_replace('/[``][\s\S]+?[``]/', '', $value);
$value = preg_replace('/`[\s\S]+?`/', '', $value);
$value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value);
if ( $value == strip_tags($value) )
{
return true;
}
return false;
}, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.');
}
/**
@@ -172,9 +188,35 @@ class Validation
return $this->validationResult($v, $name);
}
/**
* validation for content editor
*
* @param array $params with form data.
* @return true or $v->errors with array of errors to use in json-response
*/
public function editorInput(array $params)
{
$v = new Validator($params);
$v->rule('required', ['title', 'content', 'url']);
$v->rule('lengthBetween', 'title', 2, 40);
$v->rule('noHTML', 'title');
$v->rule('markdownSecure', 'content');
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
/**
* validation for dynamic settings (themes and plugins)
* validation for dynamic fields ( settings for themes and plugins)
*
* @param string $fieldName with the name of the field.
* @param array or string $fieldValue with the values of the field.

View File

@@ -50,7 +50,7 @@ class Write
return true;
}
protected function writeFile($folder, $file, $data)
public function writeFile($folder, $file, $data)
{
if($this->checkPath($folder))
{

View File

@@ -1,5 +1,10 @@
<?php
use Typemill\Controllers\SettingsController;
use Typemill\Controllers\ContentController;
use Typemill\Middleware\RedirectIfUnauthenticated;
use Typemill\Middleware\RedirectIfAuthenticated;
use Typemill\Middleware\RedirectIfNoAdmin;
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes');
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes');
$app->put('/api/v1/article', ContentController::class . ':updateArticle')->setName('api.article.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));

View File

@@ -8,18 +8,26 @@
<section>
<form>
<fieldset>
<div class="large">
<label for="title">Title*</label>
<input name="title" type="text" value="{{ title }}" required />
</div>
<div class="large">
<label for="content">Text*</label>
<textarea name="content" rows="20" required>{{ content }}</textarea>
<div id="editor" class="editor">
<div class="large">
<label for="title">Title*</label>
<input name="title" id="title" type="text" value="{{ title }}" required />
</div>
<input name="url" id="url" type="hidden" value="{{ item.urlRel }}" required />
<div class="large">
<label for="content">Content*</label>
<resizable-textarea>
<textarea id="content" v-model="markdown" name=="content" required></textarea>
</resizable-textarea>
</div>
<div class="large">
<button v-on:click="saveMarkdown">Save</button>
<div id="message" class="message"></div>
</div>
</div>
</fieldset>
</form>
<textarea id="origContent" style="display:none">{{ content }}</textarea>
</section>
</div>

View File

@@ -2,7 +2,7 @@
* TRANSITION *
**********************/
a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group, .sidebar-menu, .menu-action, .button-arrow{
a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group, .sidebar-menu, .sidebar-menu--content, .menu-action, .button-arrow{
-webkit-transition: all 0.2s ease;
-moz-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
@@ -22,6 +22,8 @@ body{
background: #f9f8f6;
color: #444;
font-size: 16px;
}
body,input,select,textarea{
font-family: Helvetica, Calibri, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -125,7 +127,7 @@ aside.sidebar{
* MENU *
************************/
.sidebar-menu{
.sidebar-menu,.sidebar-menu--content{
max-height: 40px;
padding: 0px 20px;
overflow: hidden;
@@ -136,6 +138,9 @@ aside.sidebar{
.sidebar-menu.expand{
max-height: 500px;
}
.sidebar-menu--content.expand{
max-height: 3000px;
}
.menu-action{
display: block;
width: 100%;
@@ -186,6 +191,11 @@ header input[type="submit"]{
box-sizing:border-box;
border: 0px;
}
h1 .version-number{
text-transform: uppercase;
font-size: 0.5em;
font-weight: 300;
}
/********************
* SETUP FORM *
@@ -210,7 +220,6 @@ header input[type="submit"]{
margin-bottom: 0px;
}
.setupWrapper input[type="submit"]:disabled{
pointer: cursor;
cursor: default;
background: #f9f8f6;
border: 2px solid #f9f8f6;
@@ -1047,6 +1056,92 @@ label .help, .label .help{
}
/********************
* EDITOR *
********************/
.editor .large{
position: relative;
}
.editor input[name="title"]{
font-size: 1.8em;
padding: 10px 20px;
}
.editor textarea{
font-size: 1em;
padding: 20px;
line-height: 1.4em;
}
.editor span.error{
position: absolute;
left:20px;
bottom: 0px;
}
.editor .message{
display: inline-block;
width: 70%;
}
.editor .message span.error{
position: relative;
left: 0;
width: 100%;
padding: 15px 20px;
color: #e0474c;
}
.editor button{
position: relative;
border-radius: 3px;
color: #f9f8f6;
border: 2px solid #e0474c;
background: #e0474c;
padding:10px;
min-width: 200px;
}
.editor button:hover{
border: 2px solid #cc4146;
background: #cc4146;
}
.editor button:disabled, .editor button[disabled]{
border: 2px solid #cc4146;
background: #cc4146;
color: #eee;
cursor: default;
}
.editor button:disabled:after,
.editor button[disabled]:after,
.editor button.success:after,
.editor button.fail:after{
position: absolute;
right: 8px;
top: 8px;
width: 8px;
height: 8px;
border-radius: 50%;
content: '';
}
.editor button:disabled:after,
.editor button[disabled]:after{
border: 8px solid #eee;
border-top: 8px solid #ccc;
background: #ccc;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.editor button.success:after,
.editor button.fail:after{
border: 8px solid #eee;
}
.editor button.success:after{
background: #00cc00;
}
.editor button.fail:after{
background: #e0474c;
}
@media only screen and (min-width: 600px) {
header.headline{
padding: 0px 20px;
@@ -1102,7 +1197,7 @@ label .help, .label .help{
vertical-align: top;
background: transparent;
}
.sidebar-menu{
.sidebar-menu, .sidebar-menu--content{
max-height: 2000px;
padding: 0px 20px 0 0;
overflow: hidden;
@@ -1110,6 +1205,12 @@ label .help, .label .help{
font-size: 1em;
text-align: left;
}
.sidebar-menu{
max-height: 2000px;
}
.sidebar-menu--content{
max-height: 3000px;
}
.menu-action{
display: none;
width: 0px;
@@ -1127,28 +1228,12 @@ label .help, .label .help{
.sidebar-menu, .sidebar-menu--content{
font-size: 0.9em;
}
.sidebar-menu--content li.level-1{
.sidebar-menu--content li.level-1, .sidebar-menu--content li.level-0 {
font-weight: 700;
}
/*
.menu-item a, .menu-item a:link, .menu-item a:visited{
background: transparent;
width: auto;
height: 0;
padding: 0px 10px;
line-height: 2px;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-left: 10px solid white;
}
.menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active{
color: #fff;
color: #e0474c;
background: transparent;
border-left: 10px solid #e0474c;
}
*/
.menu-list.margin-bottom{
margin-bottom: 40px;
}
.menu-item a, .menu-item a:link, .menu-item a:visited{
position: relative;
width: auto;

View File

@@ -21,10 +21,8 @@ if(wait)
loginbtn.disabled = false;
loginbtn.value = 'Login';
var countdown = document.getElementById("counter");
// var flash = document.getElementById("flash-message");
countdown.parentNode.removeChild(countdown);
// flash.parentNode.removeChild(flash);
clearInterval(counter);
}

View File

@@ -1,5 +1,3 @@
(function ()
{
/**********************************
** Global HttpRequest-Function **
** for AJAX-Requests **
@@ -56,26 +54,20 @@
else
{
var httpRequest = prepareHttpRequest();
httpRequest.open(getPost, url, true);
httpRequest.open(getPost, url, true);
}
httpRequest.onreadystatechange = function(e)
{
if (this.readyState == 4)
{
if(this.status == 200)
{
if(httpRequest.response && callback)
{
if(httpRequest.responseText && callback)
{
callback(httpRequest.response);
}
}
else
{
console.log('connection error, status '+this.status);
}
callback(httpRequest.response, this.status);
}
}
};
// httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// httpRequest.setRequestHeader('Content-Type', 'text/plain');
httpRequest.setRequestHeader('Content-Type', 'application/json');
@@ -309,5 +301,4 @@
})(thisTarget);
}
})();
}

View File

@@ -0,0 +1,136 @@
const root = document.getElementById("main").dataset.url;
Vue.component('resizable-textarea', {
methods: {
resizeTextarea (event) {
event.target.style.height = 'auto'
event.target.style.height = (event.target.scrollHeight) + 'px'
},
},
mounted () {
this.$nextTick(() => {
this.$el.setAttribute('style', 'height:' + (this.$el.scrollHeight) + 'px;overflow-y:hidden;')
})
this.$el.addEventListener('input', this.resizeTextarea)
},
beforeDestroy () {
this.$el.removeEventListener('input', this.resizeTextarea)
},
render () {
return this.$slots.default[0]
},
});
new Vue({
el: '#editor',
data: {
markdown: document.getElementById("origContent").value
},
methods: {
saveMarkdown: function(e){
e.preventDefault();
e.target.disabled = true;
e.target.classList.remove("success", "fail");
deleteErrors();
var getPost = 'PUT',
url = root + '/api/v1/article',
contentData = {'url': document.getElementById("url").value, 'title': document.getElementById("title").value, 'content': document.getElementById("content").value };
sendJson(function(response, httpStatus)
{
if(response)
{
e.target.disabled = false;
var result = JSON.parse(response);
if(result.errors)
{
e.target.classList.add('fail');
processErrors(result.errors, httpStatus);
}
else
{
e.target.classList.add('success');
}
}
else
{
e.target.disabled = false;
e.target.classList.add('fail');
console.info('no response');
}
}, getPost, url, contentData );
}
}
})
function processErrors(errors, httpStatus)
{
if(errors.length == 0) return;
var message = '';
if(httpStatus == "404")
{
message = errors[0];
}
if(httpStatus == "422")
{
var fields = '';
for (var key in errors)
{
fields = fields + ' "' + key + '"';
if(key == 'url' || !errors.hasOwnProperty(key)) continue;
var errorMessages = errors[key],
fieldElement = document.getElementById(key),
fieldMessage = document.createElement("span"),
fieldWrapper = fieldElement.parentElement;
fieldWrapper.classList.add("error");
fieldMessage.className = "error";
fieldMessage.innerHTML = errorMessages[0];
fieldWrapper.classList.add("error");
fieldWrapper.appendChild(fieldMessage);
}
message = 'Please correct the errors in these Fields: ' + fields.toUpperCase() + '. ';
}
var messageWrapper = document.getElementById("message"),
messageSpan = document.createElement("span");
messageSpan.className = "error";
messageSpan.innerHTML = message;
messageWrapper.appendChild(messageSpan);
}
function deleteErrors()
{
var errors = document.querySelectorAll('.error');
if(errors.length == 0) return;
for(var key in errors)
{
if(!errors.hasOwnProperty(key)) continue;
if(errors[key].tagName == "SPAN")
{
errors[key].parentElement.removeChild(errors[key]);
}
else
{
errors[key].classList.remove("error");
}
}
}

6
system/author/js/vue.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -24,7 +24,7 @@
{% include 'partials/navi.twig' %}
</header>
{% include 'partials/flash.twig' %}
<div class="main">
<div class="main" id="main" data-url="{{ base_url }}">
<aside class="sidebar">
{% include 'partials/aside.twig' %}
</aside>

View File

@@ -25,7 +25,7 @@
{% include 'partials/navi.twig' %}
</header>
{% include 'partials/flash.twig' %}
<div class="main">
<div class="main" id="main" data-url="{{ base_url }}">
<aside class="sidebar">
{% include 'partials/contentNavi.twig' %}
</aside>
@@ -34,6 +34,8 @@
</article>
<footer></footer>
</div>
<script src="{{ base_url }}/system/author/js/vue.min.js"></script>
<script src="{{ base_url }}/system/author/js/vue-editor.js"></script>
<script src="{{ base_url }}/system/author/js/author.js"></script>
</body>
</html>

View File

@@ -2,7 +2,7 @@
{% if is_role('administrator') %}
<div id="mobile-menu" class="menu-action">Menu <span class="button-arrow"></span></div>
<h3>Settings</h3>
<ul class="menu-list">
<ul class="menu-list margin-bottom">
<li class="menu-item"><a href="{{ path_for('settings.show') }}"{{ (route == 'settings.show') ? 'class="active"' : '' }}>System</a></li>
<li class="menu-item"><a href="{{ path_for('themes.show') }}"{{ (route == 'themes.show') ? 'class="active"' : '' }}>Themes</a></li>
<li class="menu-item"><a href="{{ path_for('plugins.show') }}"{{ (route == 'plugins.show') ? 'class="active"' : '' }}>Plugins</a></li>

View File

@@ -33,7 +33,9 @@
{% import _self as macros %}
<nav id="sidebar-menu" class="sidebar-menu--content">
<div id="mobile-menu" class="menu-action">Menu <span class="button-arrow"></span></div>
<ul class="menu-list">
<li class="menu-item folder level-0"><a {% if current_url == 'tm/content/' %}class="active"{% endif %} href="{{base_url}}/tm/content/">Startpage</a></li>
{{ macros.loop_over(navigation, base_url) }}
</ul>
</nav>

View File

@@ -1,6 +1,6 @@
<nav class="header-navi">
<div class="logo">
<a href="#">Typemill</a>
<a href="{{ path_for('content.show') }}">Typemill</a>
</div>
<ul class="navi-items">
<li><a href="{{ path_for('content.show') }}"{{ navigation ? 'class="active"' : '' }}><i class="icon-doc-text"></i><span class="nav-label"> Content</span></a></li><li>

View File

@@ -12,7 +12,7 @@
<section id="system" class="settings">
<header class="headline">
<h1>System</h1>
<h1>System <span class="version-number">v. {{settings.version}}</span></h1>
</header>
<div id="typemill" class="fc-system-version update-banner">{{ settings.version ? settings.version : 'Unknown' }}</div>
@@ -59,7 +59,7 @@
{% endif %}
</div><div class="medium">
<label for="settings[sitemap]">Google Sitemap <small>(Readonly)</small></label>
<input type="text" name="settings[sitemap]" id="sitemap" readonly value="{{ base_url }}cache/sitemap.xml" />
<input type="text" name="settings[sitemap]" id="sitemap" readonly value="{{ base_url }}/cache/sitemap.xml" />
</div><div class="medium">
<span class="label">Startpage</span>
<label class="control-group">Startpage is designed as landing-page.

View File

@@ -152,7 +152,7 @@ $container['view'] = function ($container)
$path = array($container->get('settings')['themePath'], $container->get('settings')['authorPath']);
$view = new \Slim\Views\Twig( $path, [
'cache' => $container->get('settings')['cache'] ? $container->get('settings')['cachePath'] : false,
'cache' => false,
'autoescape' => false,
'debug' => true
]);
@@ -164,7 +164,8 @@ $container['view'] = function ($container)
$view->addExtension(new Typemill\Extensions\TwigUserExtension());
/* use {{ base_url() }} in twig templates */
$view['base_url'] = $container['request']->getUri()->getBaseUrl();
$view['base_url'] = $container['request']->getUri()->getBaseUrl();
$view['current_url'] = $container['request']->getUri()->getPath();
/* if session route, add flash messages and csrf-protection */
if($container['flash'])