mirror of
https://github.com/typemill/typemill.git
synced 2025-05-05 10:55:23 +02:00
Version 1.2.6: Visual Editor
This commit is contained in:
parent
9b16a17d0b
commit
8179baf8f1
cache
content
00-Welcome
00-typemill
01-getting-started
02-for-writers
00-quick start.md01-author panel.md02-naming files and folders.md03-folder structure.md04-mardown.md05-google sitemap.md06-themes.md07-plugins.md08-forgot password.mdindex.md
03-for-theme-developers
00-quick start.md01-theme structure.md02-theme meta.md03-asset tags.md04-twig.md
05-theme variables
00-content.md01-title.md02-description.md03-image.md04-base url.md05-item.md06-breadcrumb.md07-navigation.md08-settings.mdindex.md
index.md04-for-plugin-developers
00-intro.md
01-tutorial
00-cookie consent plugin.md01-write the yaml file.md02-write the php file.md03-add methods.md04-create the structure.md05-add twig template.md06-add default values.md07-use variables in twig.md08-make variables editable.mdindex.md
02-documentation
00-file structure.md01-configuration file.md02-field overview.md03-basic php file.md04-event overview.md05-method overview.md06-routes.md07-middleware.mdindex.md
index.md05-info
index.mdindex.txtsystem
Controllers
AuthController.phpContentApiController.phpContentBackendController.phpContentController.phpPageController.phpSettingsController.php
Extensions
Middleware
Models
Routes
Settings.phpauthor
2
cache/lastCache.txt
vendored
2
cache/lastCache.txt
vendored
@ -1 +1 @@
|
||||
1538862654
|
||||
1540917193
|
30
content/00-Welcome/00-Write-Content.md
Normal file
30
content/00-Welcome/00-Write-Content.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Write Content
|
||||
|
||||
Typemill is a simple Flat File Content Management System (CMS). We work hard to provide the best author experience with easy and intuitive authoring tools. But Typemill is still in early development and it is likely that not everything will work perfectly out of the box. If you miss something or if you have ideas for improvement, then let me know via mail (trendschau@gmail.com) or via [GitHub](https://github.com/trendschau/typemill/issues).
|
||||
|
||||
## The Navigation
|
||||
|
||||
You can create, structure and reorder all pages with the navigation on the left. To structure your content, you can create new folders and files with the "add item" button. To reorder the pages, just drag an item and drop it wherever you want. Play around with it and you will notice, that it works pretty similar to the folder- and file-system of your laptop. And in fact, this is exactly what Typemill does in the background: It stores your content in files and folders on the server.
|
||||
|
||||
However, there are some limitations when you try to reorder elements, for example, you cannot move a complete folder to another folder. Click on the question-mark at the top of the navigation for detailed informations.
|
||||
|
||||
## The Editor
|
||||
|
||||
You can create and format your content with the Markdown syntax, that is similar to the markup syntax of wikipedia. If you are not familiar with Markdown, then please read the short [Markdown-tutorial](https://typemill.net/) in the documentation of Typemill. You can learn Markdown in less than 10 minutes and there is no easier and faster way to format your webpage. You will love it!
|
||||
|
||||
Typemill provides two edit modes: The **raw mode** and the **visual mode**. You can switch between the modes in the publish-bar at the bottom of each page. The **raw mode** is the most robust way to create your content, because you write raw markdown into a simple input area. The **visual mode** is experimental right now. It uses blocks and transforms each content block into a html-preview immediately, so that you can directly see the formatted result.
|
||||
|
||||
By default Typemill will use the raw mode. But you can change this behavior in the system settings and use the visual editor as default mode.
|
||||
|
||||
## The Publish Bar
|
||||
|
||||
The publish bar of Typemill is pretty intuitiv and sticks at the bottom of the screen so that you have always full control of the status of each page. Simply play around with it and you will quickly understand how it works. In short:
|
||||
|
||||
* The green button "online" indicates, that your page is published and visible for your readers.
|
||||
* You can depublish a page by clicking the green button. The button turns grey with the label "offline" then.
|
||||
* With the red button "Publish" you can publish either a page that is offline or you can publish still unpublished changes.
|
||||
* The publish-button is grey, if the page is online and if there are no unpublished changes.
|
||||
* With the red button "Save Draft" you can safe a draft. You can also work on a draft as long as you want while the old version of the page is still online.
|
||||
* All buttons will change in real time, so you can always exactly see what is going on.
|
||||
* To provide an easy status-overview of the whole website, Typemill marks all unpublished pages or changes red in the navigation on the left side.
|
||||
|
10
content/00-Welcome/01-Get-Help.md
Normal file
10
content/00-Welcome/01-Get-Help.md
Normal file
@ -0,0 +1,10 @@
|
||||
Get Help
|
||||
|
||||
If you need any help, then please read the [documentation on typemill.net](https://typemill.net/typemill) first. Some short video-tutorials are in work right now.
|
||||
|
||||
If you found a bug or if you have a question, then please open a new issue on [GitHub](https://github.com/trendschau/typemill/issues).
|
||||
|
||||
Typemill is open source and a one man project right now, so please understand that I cannot provide individual support.
|
||||
|
||||
Contributions, donations and feedbacks are always welcome.
|
||||
|
1
content/00-Welcome/index.txt
Normal file
1
content/00-Welcome/index.txt
Normal file
@ -0,0 +1 @@
|
||||
["# Welcome to Typemill","Great that you give Typemill a try!! Typemill is a small open source cms and a project in work. You will probably miss some important features, but I am working hard to add everything that is needed for a really productive little writing-system.","Before you start, please read the short introduction about \"writing content\". Or simply play around, I hope that Typemill is already quite intuitive to use..."]
|
@ -1,21 +0,0 @@
|
||||
# Use Cases
|
||||
|
||||
Whenever you want to publish a finished text work as a website and if you like writing with Markdown, then TYPEMILL is a smart and lightweight solution. Possible use cases are …
|
||||
|
||||
- a documentation
|
||||
- a manual
|
||||
- a user guide
|
||||
- a whitepaper
|
||||
- a handbook
|
||||
- a tutorial
|
||||
- a study
|
||||
- a book
|
||||
- a drama
|
||||
- a lyric collection
|
||||
- a collection of articles
|
||||
|
||||
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 and a basic editor for existing content. More options like create new pages and manage your media-files will be added step by step in the next weeks. Also different output formats like mobi and ePup are on the roadmap, so stay tuned.
|
||||
|
||||
For now, you have to use a FTP-software like FileZilla until all basic editing features are ready. Check the roadmap for more informations.
|
@ -1,33 +0,0 @@
|
||||
# Roadmap
|
||||
|
||||
There are a lot of plans for future releases of TYPEMILL, but it also follows the concept of simplicity. To prevent TYPEMILL from becomming a feature soup, it will strictly focus on the writers needs.
|
||||
|
||||
Here are some **milestones** of the past:
|
||||
|
||||
- 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 edit and delete existing pages (v. 1.2.0)
|
||||
- Added a draft management for pages (v. 1.2.2)
|
||||
- Re-order content pages with drag&drop (v. 1.2.3)
|
||||
- Editor: Create new pages (v. 1.2.5)
|
||||
|
||||
And here is the **roadmap** for this year (2018):
|
||||
|
||||
- Editor: Basic WYSIWYG-Preview for markdown blocks (v. 1.2.6)
|
||||
- Editor: Manage images and assets (v. 1.2.7)
|
||||
- Editor: Edit meta-information (v. 1.2.8)
|
||||
- Editor: Add formatting options (v. 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.
|
||||
- Create many useful plugins.
|
||||
|
||||
If you miss a feature or if you found a bug, please report it on [GitHub](https://github.com/trendschau/typemill).
|
@ -1,25 +0,0 @@
|
||||
# Features
|
||||
|
||||
TYPEMILL has a limited set of features right now. It transforms a bunch of **markdown files** into a **website** and generates a list of contents for **navigation**.
|
||||
|
||||
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 create, edit and reorder 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).
|
||||
- Create your own plugins with PHP.
|
||||
|
||||
This is, what **TYPEMILL does** for you:
|
||||
|
||||
- It creates a website based on your files and folders.
|
||||
- It generates a navigation according to the structure of your files and folders.
|
||||
- It adds a paging for navigation.
|
||||
- It adds a breadcrumb for navigation.
|
||||
- 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 only a simple content editor to create, edit and reorder pages. There is no media management yet, so you have to upload images and media files with an FTP software like FileZilla for now.
|
@ -1,7 +0,0 @@
|
||||
# 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. 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.
|
@ -1,25 +0,0 @@
|
||||
# System Requirements
|
||||
|
||||
TYPEMILL is a modern and lightweight software with nearly no requirements. All you need is:
|
||||
|
||||
- **A webserver (Apache)** (to host TYPEMILL)
|
||||
- **PHP 7.0+** (to run TYPEMILL)
|
||||
- **FTP** (to upload your images and media files)
|
||||
|
||||
What you don't need:
|
||||
|
||||
- **A lot of space** (TYPEMILL is lightweight with less than 5MBs)
|
||||
- **A database** (TYPEMILL uses files, not a database)
|
||||
- **Technical skills** (TYPEMILL is easy to use for non-technical people)
|
||||
|
||||
Almost any hosting package provides a webserver with php. If you ever hosted your own website, then chances are high, that you can run TYPEMILL there without any problems.
|
||||
|
||||
Typemill supports the following browsers:
|
||||
|
||||
- Firefox (heavily tested)
|
||||
- Chrome (tested)
|
||||
- Edge (basic tests)
|
||||
- IE11 (basic tests)
|
||||
- Safari (not tested)
|
||||
|
||||
I use Firefox and XAMPP for development. If you use another stack like Safari / OS, then try it, but I have no possibility to test or fix it.
|
@ -1,61 +0,0 @@
|
||||
# Installation
|
||||
|
||||
The installation of TYPEMILL is as simple as that:
|
||||
|
||||
- Go to [typemill.net](http://www.typemill.net) and download the TYPEMILL files.
|
||||
- Upload the files to your server.
|
||||
- Go to `www.your-typemill-website.com/setup` and create an initial user.
|
||||
- Login and configure your system, your themes and your plugins.
|
||||
|
||||
Don't forget to make some folders and files writable (set permission to `774`):
|
||||
|
||||
- `\cache` folder and files
|
||||
- `\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 might crash the system if done wrong.
|
||||
|
||||
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.
|
||||
|
||||
## Download
|
||||
|
||||
There are two ways to copy TYPEMILL to your local computer:
|
||||
|
||||
1. Go to [typemill.net](http://www.typemill.net), download the zip-archive and unzip it.
|
||||
2. **Or** use GitHub and Composer.
|
||||
|
||||
If you use GitHub, then you can find the repository of TYPEMILL on [github/trendschau/typemill](https://github/trendschau/typemill). Just open the command line (git-CLI) and type
|
||||
|
||||
````
|
||||
git clone "https://github.com/trendschau/typemill.git"
|
||||
````
|
||||
|
||||
TYPEMILL uses some nice frameworks and libraries, which can be found in the folder `\system\vendor`. This folder is not included in the git version. If you use the git version, you have to download all the libraries (dependencies) with composer. If you don't have composer installed yet, head over to the [composer website](https://getcomposer.org/) and install it. After that, open your command line, go to your TYPEMILL folder and type:
|
||||
|
||||
````
|
||||
composer update
|
||||
````
|
||||
|
||||
The exact command might vary depending on your local composer installation and configuration. If you face any problems, please check the documentation of composer.
|
||||
|
||||
That's it!
|
||||
|
||||
## Permissions
|
||||
|
||||
The following three folders and all files and folders inside must be writable:
|
||||
|
||||
- `\cache`
|
||||
- `\settings`
|
||||
- `\content`
|
||||
|
||||
To make the folders and files writable, use your ftp programm, click on the folder, choose `permissions` and change the permission to `744`. Use the recursive permission for all containing files and folders. If `744` does not work, try `774`.
|
||||
|
||||
## htaccess
|
||||
|
||||
If you run your website with https (recommended) or if you want to redirect www-urls to non-www urls, then please check the htaccess file in the root folder. There are several use cases already prepared and you can simply uncomment them, if needed.
|
||||
|
||||
## 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.
|
@ -1,21 +0,0 @@
|
||||
# Settings
|
||||
|
||||
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 might crash the system if done wrong.
|
||||
|
||||
## Advanced Settings
|
||||
|
||||
There are some settings that are not available via the author panel. Most of them are not really useful, but if you are a developer and if you develop a theme or a plugin locally, you probably want to display a detailed error report. To do so, simply add the following line to the settings.yaml:
|
||||
|
||||
|
||||
````
|
||||
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.
|
@ -1,41 +0,0 @@
|
||||
# Update
|
||||
|
||||
If your TYPEMILL version or any plugin or theme is not up to date, you will find individual update banners in the author panel.
|
||||
|
||||
## Simple Update
|
||||
|
||||
To update your TYPEMILL version, simply download the latest version of TYPEMILL on [the TYPEMILL website](http://typemill.net). Then delete the old `system` folder on your server and upload the new system folder. All other files and folders can usually stay untouched.
|
||||
|
||||
After you updated your installation, please login to your website and check the settings. Sometimes, there are additional features that you can find there.
|
||||
|
||||
## Major Update
|
||||
|
||||
TYPEMILL is in early stage and there are a lot of basic changes right now. When there are basic changes, then you should update the whole installation like this:
|
||||
|
||||
* Backup your settings folder
|
||||
* Keep your content folder
|
||||
* Delete everything else:
|
||||
* cache
|
||||
* plugins
|
||||
* settings
|
||||
* system
|
||||
* themes
|
||||
* Upload the new folders
|
||||
* Go to `your-typemill-website.com/setup` and create a new user.
|
||||
* Setup your website again in the author panel.
|
||||
|
||||
In many cases you can also use your old settings folder, so it is highly recommended to create a backup and test it. But sometimes, the new version requires a new setup of the system, so if you want to do it the clean way, just start and setup your system again.
|
||||
|
||||
## GitHub and Composer
|
||||
|
||||
If you work with GitHub and Composer, then make sure that you **always** make a `composer update` after you uploaded the new system-folder from GitHub. This is essential, because the GitHub-folder does NOT include the vendor folder with all the dependencies that TYPEMILL uses. If you don't update these dependencies with composer, then the system will not run.
|
||||
|
||||
If you download the TYPEMILL from http://typemill.net, then you don't have to worry about this, because the vendor folder with all dependencies is included there.
|
||||
|
||||
We decided to skip the vendor folder in the GitHub version because it constantly caused errors due to some missing libraries.
|
||||
|
||||
## Old Settings File
|
||||
|
||||
Please do not rename or leave the old settings file in the settings folder, because any files in that folder will cause errors and problems. Instead, backup your old settings file in another folder or on your local machine.
|
||||
|
||||
If it is only a minor update, you can leave your settings folder untouched and change everything in the author-panel after the update.
|
@ -1,3 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
Simply download TYPEMILL and immediately start publishing. TYPEMILL runs with **php7+** on most webservers. **No database** or any other additional technology is required.
|
@ -1,15 +0,0 @@
|
||||
# Quick Start for Writers
|
||||
|
||||
In earlier versions of Typemill authors had to create content files with an offline Markdown editor and upload the files to the server. Since Typemill provides an online editor to create, reorder and edit content pages, a separate offline editor is not needed anymore.
|
||||
|
||||
However, if you still want to use your favourite Markdown editor to create your content files, then please follow this rules:
|
||||
|
||||
- **Setup**: Login to your system and setup the system, the themes and the plugins in the author panel.
|
||||
- **Content**: Organize your content in folders and markdown files and put them in the `\content` folder of TYPEMILL.
|
||||
- **Markdown**: Use the Markdown syntax for your content files. Markdown Extra (e.g. tables, footnotes) is supported, too.
|
||||
- **Naming conventions**: Use prefixes like `01-` or `aa_` to sort your folders and files.
|
||||
- **Index files**: Add an `index.md` file to a folder to create content for the folder itself.
|
||||
- **F5**: After some changes, use the `F5` key to refresh the navigation manually.
|
||||
- **Lean back** and let TYPEMILL create a nice website for you.
|
||||
|
||||
TYPEMILL ships with this user manual in the content folder. Check how the files are written and how the folders are organized.
|
@ -1,35 +0,0 @@
|
||||
# The Author Panel
|
||||
|
||||
You can login to the author panel with this url:
|
||||
|
||||
````
|
||||
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.
|
||||
* Create new pages.
|
||||
* Edit existing pages with markdown syntax.
|
||||
* Re-order existing pages with simple drag&drop.
|
||||
* Save drafts, publish pages, unpublish pages and delete pages.
|
||||
|
||||
There are several **limitations** right now:
|
||||
|
||||
* You cannot use HTML, JavaScript or any other code in the editor, only markdown-syntax is allowed.
|
||||
* There is no media-management right now.
|
||||
|
||||
The content editor has highest priority in the roadmap, so you can expect a 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**.
|
@ -1,24 +0,0 @@
|
||||
# Naming your Files and Folders
|
||||
|
||||
Naming conventions are not very relevant anymore since you can create new pages online with the author panel right now. If you still want to create your content offline, then please follow the rules below.
|
||||
|
||||
To create a clean website with TYPEMILL, you have to follow some naming conventions for your files and folders. A typcial structure for folders and file looks like this:
|
||||
|
||||
````
|
||||
/01_content_folder
|
||||
01_markdown_file.md
|
||||
02_another_markdown_file.md
|
||||
index.md
|
||||
/02_another_folder
|
||||
01_another_content_file.md
|
||||
````
|
||||
|
||||
The rules are simple:
|
||||
|
||||
- **Keep it short**: The names of the files and folders are used to create the navigation, so it is essential to keep them really short and descriptive!!
|
||||
- **Use prefixes**: Please use some kind of sorting-prefix for your files and folders. You can use numbers `01-` or letters `aa-`. The part before the first separator (the prefix) is striped out by TYPEMILL.
|
||||
- **Use Separators**: Please use separators like dashes `-` or underscores `_` to separate words or prefixes in your file names and folder names. **Do not use space**!!!
|
||||
- **Use index.md**: TYPEMILL creates websites for the folders and users can click on folder-names exactly like they click on file-names in the navigation. Folder pages are empty by default, but you can use a file named `index.md` to create content for the folder page.
|
||||
- **Avoid Language Specific Characters**: As of version 1.0.5 the character encoding has been improved, but it is still not perfect. You can try to use german, french or other character sets to name your files and folders. But if you see some errors in the navigation of the website, please use english characters instead. In the content files itself you can use all character sets of course.
|
||||
|
||||
When you name your files and folders, then always keep in mind, that the names are used to generate the navigation and the table of contents. So keep it short. Otherwise it might break the layout and the design.
|
@ -1,5 +0,0 @@
|
||||
# Folder Structure
|
||||
|
||||
You can create any kind of folder structure with TYPEMILL, as long as you follow the naming conventions for folders and files.
|
||||
|
||||
However, if you create a very deep structure, then it might result in an odd design or even in usability errors. Similar to real live, it is always a good idea to keep the hierarchy as flat as possible.
|
@ -1,63 +0,0 @@
|
||||
# Markdown
|
||||
|
||||
Never heard of Markdown? Markdown is a simple syntax for text formatting and very similar to the markup used by wikipedia. Markdown uses a handful special characters like `#` of `*` to format the text.
|
||||
|
||||
The big advantage for writers: They don't have to click a button to format a headline or to create a list. Instead they can format the text during the writing process without an interruption. Once you are familiar with the syntax, you can write cleaner and faster than with any common HTML editor. For a quick start with many examples, read the [markdown reference page](/info/markdown-test).
|
||||
|
||||
## Markdown Example
|
||||
|
||||
This is a short example for a markdown text:
|
||||
|
||||
````
|
||||
# My first level headline
|
||||
|
||||
This is a paragraph and now we create an unordered list:
|
||||
|
||||
- Item
|
||||
- Another item
|
||||
- A last item
|
||||
````
|
||||
|
||||
From a technical point of view, markdown is cleaner, shorter and safer than HTML. At the same time, markdown can be transformed to clean HTML easily. This is why markdown is used by a lot of technology platforms like GitHub or StackOverflow. And Markdown is also entering the non technical mainstream. The press releases of dpa are written in Markdown, for example.
|
||||
|
||||
## Markdown Quick Reference
|
||||
|
||||
This is a quick reference table. For a detailed reference with examples, please check the [markdown reference page](/info/markdown-test).
|
||||
|
||||
| Markdown | Result |
|
||||
| ------------------------------------------------------------ | --------------------------------------- |
|
||||
| `[TOC]` | Table of content |
|
||||
| `#` | Headline first level |
|
||||
| `######` | Headline sixth level |
|
||||
| simple text | Paragraph |
|
||||
| `_italic_` | Italic text |
|
||||
| `*italic*` | Italic text |
|
||||
| `__bold__` | Bold text |
|
||||
| `**bold**` | Bold text |
|
||||
| `- list item` | Unordered list |
|
||||
| `* list item` | Unordered list |
|
||||
| `5. ordered list` | Ordered list (use any number you want). |
|
||||
| `---` | Horizontal rule |
|
||||
| `[link text](http://typemill.net)` | Link |
|
||||
| `` | Image |
|
||||
| `> interesting quote` | Blockquote |
|
||||
| `footnote[^1]`<br /> `[^1]:footnote text` | Footnote |
|
||||
| `*[HTML]:Hyper Text Markup Language` | Abbreviation |
|
||||
| `Apple`<br /> `: Green thingy growing on trees` | Definition list |
|
||||
| `|name |usage |`<br /> `|-----------|-----------|`<br /> `| My Name | For Me |` | Table |
|
||||
| ` | Inline code |
|
||||
| `` | Inline math expression |
|
||||
| ``` | Code block |
|
||||
| ```latex | Math block |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Write Markdown with TYPEMILL
|
||||
|
||||
An online editor for TYPEMILL is on its way, but for time beeing, you have to create your markdown text-files offline and upload them with your favourite FTP software.
|
||||
|
||||
You can create markdown-files with any kind of text editor. Simply create a file with the ending `.md`. You can use any code editor, a simple text editor like the standard "editor" of Microsoft, or you can use a special markdown editor like Typora. Typora is a very clean WYSIWYG-editor that is specialized on distraction free text creation.
|
||||
|
||||
For a quick reference and examples for all markdown formats you can head over to the [markdown reference page](/info/markdown-test).
|
@ -1,3 +0,0 @@
|
||||
# Google Sitemap
|
||||
|
||||
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.
|
@ -1,9 +0,0 @@
|
||||
# Themes
|
||||
|
||||
TYPEMILL ships with the standard theme called "Typemill". Typemill is a univeral theme the fits for documentations as well as for any other kind of text-work. There are plans to add a lot more themes for specific need s in the future.
|
||||
|
||||
You can configure the theme in the settings of the author panel. You will see an update banner, if there is a new version of the theme available.
|
||||
|
||||
To update a theme, simply go to the theme folder of your typemill installation, delete the old folder of your theme (e.g. `/typemill`) and upload the new folder.
|
||||
|
||||
If you are a developer or web-designer, you can easily create your own theme with the template language Twig. Please read the [theme documentation](/theme-developers) for more details.
|
@ -1,16 +0,0 @@
|
||||
# Plugins
|
||||
|
||||
As of version 1.1.0, TYPEMILL supports plugins. You can activate and configure all plugins in the author panel of TYPEMILL. You will see a small update banner if there exists a new version of the plugin.
|
||||
|
||||
To update a plugin, simply go to the plugin folder of TYPEMILL, delete the plugin and upload the new version of the plugin. Then check the plugin configuration in the settings of the author panel.
|
||||
|
||||
Right now all plugins are delivered with the core version of TYPEMILL. But as the number of plugins grows, there will probably be a central place to download and install the plugins in the future.
|
||||
|
||||
The core version ships with the following plugins:
|
||||
|
||||
* **Cookie Consent**: Adds a cookie consent banner to your website.
|
||||
* **Analytics**: Adds Matomo (Piwik) or Google Analytics to your website.
|
||||
* **Highlight**: Highlights code in code-blocks (useful for developers).
|
||||
* **Math**: Write math with markdown and MathJax or KaTeX. Check the [markdown reference page](/info/markdown-test).
|
||||
|
||||
If you are a developer and if you want to create your own plugins, please refer to the [plugin documentation](/plugin-developers).
|
@ -1,21 +0,0 @@
|
||||
# Lost Your Password?
|
||||
|
||||
TYPEMILL does not provide a password recovery, but there are two ways to create a new password.
|
||||
|
||||
## Is there another admin?
|
||||
|
||||
If there is another user with admin rights, then contact him. He can delete your user, create a new one and tell you the new password. Change the password immediately after login.
|
||||
|
||||
## No other admin?
|
||||
|
||||
If you are the only admin user, then please follow these steps:
|
||||
|
||||
* Connect to your website (e.g. via FTP).
|
||||
* Go to the folder `/settings` and backup the file `settings.yaml`.
|
||||
* Then delete the file `settings.yaml` on your server.
|
||||
* Go to `yoursite.com/setup`.
|
||||
* Fill out the form. This will create a new admin user and a fresh settings-file.
|
||||
* Upload your old settings-file, so your old settings are active again.
|
||||
* If not done before e.g. via FTP, delete the old admin-user in the user management now.
|
||||
|
||||
It might look a bit uncomfortable but it makes sure, that you are the owner of the website.
|
@ -1,3 +0,0 @@
|
||||
# Hello Writers!
|
||||
|
||||
If you love **simple text files** (Markdown) and if you like **distraction free websites** without fiddly technology, then TYPEMILL is for you!
|
@ -1,113 +0,0 @@
|
||||
# Quick Start for Theme-Developers
|
||||
|
||||
You are a professional web developer and don't want to read the whole documentation? No problem, this is all you need to know to create your own theme for TYPEMILL.
|
||||
|
||||
## Theme Folder
|
||||
|
||||
You will find all themes in the `theme` folder of TYPEMILL. You can add a new folder for your theme there. The name of your folder is the name of your theme.
|
||||
|
||||
## Change Theme
|
||||
|
||||
You can choose the theme in author panel of TYPEMILL.
|
||||
|
||||
## Theme Structure
|
||||
|
||||
There is no theme structure. There are only two files that are required:
|
||||
|
||||
- `index.twig`: All content files will be rendered with this template.
|
||||
- `404.twig`: This is the template for a not found message.
|
||||
|
||||
There is another optional template:
|
||||
|
||||
- `cover.twig`: Use this name to create a template for a special startpage with a different design.
|
||||
|
||||
There are two other files that are optional, but it is strongly recommended to add them:
|
||||
|
||||
* `themeName.jpg`: A preview picture of your theme with a minimal width of 800px;
|
||||
* `themeName.yaml`: A configuration file with the version, the author name, licence and other informations.
|
||||
|
||||
It is always a good idea to structure your files a bit more. For example, you can create a folder called `partials` with separate files for different layouts (maybe a folder and file layout?), a navigation, a header, a footer or whatever you want. But this decision is completely up to you. The same with CSS, JavaScript and other ressources: It is a good practice to create separate folders for that, but it is up to you.
|
||||
|
||||
## Theme-YAML
|
||||
|
||||
The `themeName.yaml` must have the same name as your theme folder. A basic file looks like this:
|
||||
|
||||
````
|
||||
name: My Theme Name
|
||||
version: 1.0.0
|
||||
description: Write what you want
|
||||
author: Your name here
|
||||
homepage: http://an-info-website-for-the-theme.com
|
||||
licence: MIT
|
||||
````
|
||||
|
||||
You can also add settings for your themesi in the YAML-file like this:
|
||||
|
||||
````
|
||||
settings:
|
||||
chapter: Chapter
|
||||
start: Start
|
||||
````
|
||||
|
||||
The settings are automatically merged with all other TYPEMILL settings and are available on all pages, so you can access your theme variables like this:
|
||||
|
||||
````
|
||||
{{ settings.themes.typemill.chapter }} // prints out "Chapter".
|
||||
````
|
||||
|
||||
Finally you can make your theme variables editable for the user in the author panel. Just add a form definition in your yaml like this:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
chapter:
|
||||
type: text
|
||||
label: chapter
|
||||
placeholder: Add Name for Chapter
|
||||
required: true
|
||||
|
||||
start:
|
||||
type: text
|
||||
label: Start-Button
|
||||
placeholder: Add Label for Start-Button
|
||||
required: true
|
||||
````
|
||||
|
||||
This will create input forms in the author panel. The input forms will be prefilled with the settings-values of your YAML-file.
|
||||
|
||||
## Twig
|
||||
|
||||
TYPEMILL uses Twig as a template language. You are probably familiar with it. If not: Twig is a widespread template language, that is very easy to learn. It is shorter and safer to use than pure PHP.
|
||||
|
||||
## Template Variables
|
||||
|
||||
There are exactly six template variables to fill your templates with dynamic content:
|
||||
|
||||
- `navigation`: This variable is a multidimensional array of objects. Each object represents a file or a folder. You can use this variable to create a navigation with a Twig-macro. A macro in Twig is the same as a recursive function in PHP.
|
||||
- `item`: This variable is an object of the actual page. It contains all the details like the name, the url, the path, the chapter as well as the next and the previous items for a pagination. And guess what? The `navigation` variable mentioned above is just an array, that holds many of these item-objects.
|
||||
- `content`: This variable holds the HTML content of the markdown file. Just print it out.
|
||||
- `description`: This are the first lines of the content of a page. You can use this for the meta description.
|
||||
|
||||
|
||||
- `breadcrumb`: This variable is an one dimensional array. It contains the breadcrumb of the page. Just use a loop like `{% for element in breadcrumb %}` to print it out.
|
||||
- `settings`: In this variable you will find all the settings like the navigation-title, the author, the theme, the theme variables or the copyright.
|
||||
|
||||
You can print out each variable with the twig-tag `{{ dump(navigation) }}` and inspect the content. This is probably the easiest way to familiarize with the possibilities for themes.
|
||||
|
||||
## Asset Tags
|
||||
|
||||
Plugin-developers want to add their own CSS and JavaScript to your theme. You should enable plugin-developers to do so with two Twig-tags:
|
||||
|
||||
* `{{ assets.renderCSS() }}`: Put this before the closing `</head>`-tag of your theme.
|
||||
* `{{ assets.renderJS() }}`: Put this before the closing `</body>`-tag of your theme.
|
||||
|
||||
## Content-Styling
|
||||
|
||||
If you create a theme, make sure that all content types (headlines, paragraphs, tables) are styled properly. You can use the [markdown-test-page](/info/markdown-test) to check the styling of all content-elements.
|
||||
|
||||
## Read more
|
||||
|
||||
If you are not ready to start with these information, then please read the full developer manual. In less than one hour you can develop your own themes for TYPEMILL like a pro.
|
||||
|
||||
Happy coding!
|
@ -1,51 +0,0 @@
|
||||
# Theme Structure
|
||||
|
||||
TYPEMILL requires a minimal structure and a small set of mandatory files:
|
||||
|
||||
````
|
||||
/myTheme
|
||||
- 404.twig
|
||||
- index.twig
|
||||
- cover.twig
|
||||
- myTheme.jpg
|
||||
- myTheme.yaml
|
||||
````
|
||||
|
||||
Some Details:
|
||||
|
||||
- **/myTheme**: A theme folder. The name of the folder is the name of the theme.
|
||||
- **404.twig**: The template for a not found page. It is mandatory.
|
||||
- **index.twig**: The template for all other pages. It is mandatory.
|
||||
- **cover.twig**: The template for a different startpage-design. It is optional.
|
||||
- **myTheme.jpg**: A preview picture of your theme. It is mandatory. The file must be named exactly like the theme folder. Minimum width is 800px.
|
||||
- **myTheme.yaml**: A configuration file for your theme with author, version number and others. This is not mandatory, but highly recommended. The file must be named exactly like the theme folder.
|
||||
|
||||
That's it.
|
||||
|
||||
## Recommendation
|
||||
|
||||
If you want to create a more complex structure, then you can do whatever you want, as long as you follow the basic structure and conventions described above.
|
||||
|
||||
However, if you don't have an idea how to start, then you can follow this example:
|
||||
|
||||
- `/css`
|
||||
- style.css
|
||||
- another.css
|
||||
- `/js`
|
||||
- javascript.js
|
||||
- `/img`
|
||||
- icon.png
|
||||
- favicon.ico
|
||||
- themeLogo.jpg
|
||||
- `/partials`
|
||||
- `layoutStart.twig`: Layout for the static startpage, usually with the html-head, a page structure and other stuff.
|
||||
- `layout.twig`: Layout for all other pages, usually with the html-head, a page structure and other stuff.
|
||||
- `navigation.twig`: The content-navigation of the page. include this into your layouts.
|
||||
- `header.twig`: The head-area of your page. Include this into your layouts.
|
||||
- `footer.twig`: The footer-area of your page. Include this into your layouts.
|
||||
- `cover.twig`: Template with the content for an individual startpage. The cover.twig extends the layoutStart.twig.
|
||||
- `index.twig`: Template for all other pages. The index.twig extends the layout.twig
|
||||
- `404.twig`: Template for the not found page. The 404.twig extends the layout.twig.
|
||||
- `themeName.yaml`: The meta-information with version, author name and other stuff.
|
||||
|
||||
In Twig, you can include and extend templates and create a template hierarchy. Read the twig-chapter to understand how this works.
|
@ -1,100 +0,0 @@
|
||||
# Theme Meta with YAML
|
||||
|
||||
It is highly recommendet to add some meta-information to your theme. This is quickly done with a small YAML-file. The YAML-file must have the same name as your theme folder. The YAML-file has up to three parts and is used for this:
|
||||
|
||||
* Display basic informations in the author-panel and generate update notifications.
|
||||
* Use settings (variables) for your theme if you want.
|
||||
* Let users edit the settings and customize the theme in the author-panel.
|
||||
|
||||
## Add Basic Informations
|
||||
|
||||
The basic informations in the YAML-file look like this:
|
||||
|
||||
```
|
||||
name: My Theme Name
|
||||
version: 1.0.0
|
||||
description: Write what you want
|
||||
author: Your name here
|
||||
homepage: http://an-info-website-for-the-theme.com
|
||||
licence: MIT
|
||||
```
|
||||
|
||||
As you can see the YAML-syntax is simple and readable even for non-technicians. Inside TYPEMILL the YAML-files are converted to one-dimensional or multi-dimensional arrays, so you can think about YAML as a simplified array language, if that helps.
|
||||
|
||||
## Use Settings
|
||||
|
||||
Sometimes you want to use variables in your theme, for example to change the text of a button. With YAML you can easily do this: Just create a new block that starts with `settings` and write all your settings as simple key-value-pairs. Indent them with two spaces like this:
|
||||
|
||||
```
|
||||
settings:
|
||||
chapter: Chapter
|
||||
start: Start
|
||||
```
|
||||
|
||||
The settings are automatically merged with all other TYPEMILL settings and are available in your themes with a simple Twig tag like this:
|
||||
|
||||
```
|
||||
{{ settings.themes.typemill.chapter }} // prints out "Chapter".
|
||||
```
|
||||
|
||||
Replace `typemill` with the name of your theme like this:
|
||||
|
||||
````
|
||||
{{ settings.themes.mytheme.chapter }}
|
||||
````
|
||||
|
||||
## Make Settings Editable
|
||||
|
||||
Finally you can make your theme variables editable for the user in the author panel. To do this, just add another block that starts with `forms` and `fields`. After that, you can define a wide range of input fields with YAML. It starts with the name of the field followed by the field definition.
|
||||
|
||||
```
|
||||
forms:
|
||||
fields:
|
||||
|
||||
chapter:
|
||||
type: text
|
||||
label: chapter
|
||||
placeholder: Add Name for Chapter
|
||||
required: true
|
||||
|
||||
start:
|
||||
type: text
|
||||
label: Start-Button
|
||||
placeholder: Add Label for Start-Button
|
||||
required: true
|
||||
```
|
||||
|
||||
TYPEMILL will use these definitions and generate input fields for the author panel on the fly, so that the user can edit the values and customize the theme. If you have defined settings with the same name as the field name (e.g. `chapter`), then the input field in the author panel will automatically be prefilled with your settings from the YAML-file.
|
||||
|
||||
If you have a lot of fields, you can even group some fields together in a fieldset like this:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
chapter:
|
||||
type: text
|
||||
label: chapter
|
||||
placeholder: Add Name for Chapter
|
||||
required: true
|
||||
|
||||
MyFirstfieldset:
|
||||
type: fieldset
|
||||
legend: Last Modified
|
||||
fields:
|
||||
|
||||
modified:
|
||||
type: checkbox
|
||||
label: Activate Last Modified
|
||||
description: Show last modified date at the end of each page?
|
||||
|
||||
modifiedText:
|
||||
type: text
|
||||
label: Last Modified Text
|
||||
placeholder: Last Updated
|
||||
|
||||
````
|
||||
|
||||
The fields `modified` and `modifiedText` will then be grouped in a fieldset with the legend `Last Modified`.
|
||||
|
||||
If you read the YAML-definition for input fields carefully, then you will notice that the definitions are pretty similar to HTML: You simply define types and attributes like input-type, labels and placeholders. Nearly all valid field-types and field attributes are supported. You can find a detailed list in the [documentation for plugins](/plugin-developers/documentation/field-overview).
|
@ -1,44 +0,0 @@
|
||||
# Asset Tags
|
||||
|
||||
Sometimes, a plugin wants to add some CSS and JavaScript to your theme. For example, the cookieconsent-plugin. It adds a cookie-consent popup to all pages, so that users can agree to the cookie policy of your website.
|
||||
|
||||
There are two Twig-tags, that allow plugins to add JavaScript and CSS. Please make sure that you add these tags to your theme, because otherwise, the plugins won't work:
|
||||
|
||||
- `{{ assets.renderCSS() }}`
|
||||
- `{{ assets.renderJS() }}`
|
||||
|
||||
It is recommended to add the CSS tag after all your css-files and before the closing head-tag. It is also a good practice in Twig, to wrap your ressources in a block-tag. You can read more about this in the Twig-chapter.
|
||||
|
||||
````
|
||||
<html>
|
||||
<head>
|
||||
<title>title</title>
|
||||
....
|
||||
....
|
||||
{% block stylesheets %}
|
||||
<link rel="stylesheet" href="{{ base_url }}/themes/mytheme/css/style.css" />
|
||||
|
||||
{{ assets.renderCSS() }}
|
||||
|
||||
{% endblock %}
|
||||
</head>
|
||||
````
|
||||
|
||||
The same for JavaScript: It is recommended to place all JavaScript at the end of the page before the closing body-tag. And you should wrap all your JavaScript in a block-element, too:
|
||||
|
||||
````
|
||||
<body>
|
||||
...
|
||||
{{ content }}
|
||||
...
|
||||
...
|
||||
|
||||
{% block javascripts %}
|
||||
|
||||
<script src="{{ base_url }}/themes/yourtheme/js/my.js"></script>
|
||||
|
||||
{{ assets.renderJS() }}
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
````
|
@ -1,215 +0,0 @@
|
||||
# Templates with Twig
|
||||
|
||||
Twig is a flexible, fast and secure template engine for PHP. Twig is used by many modern software projects and content management systems like CraftCMS, Statamic and even Drupal. If you have never used a template language before, then there are some good reasons to start with it today:
|
||||
|
||||
- The Twig syntax is **much shorter**, so your templates look cleaner and are easier to maintain.
|
||||
- Twig produces **less errors**. An unknown variable produces an error in PHP, but it does not in Twig. Twig handles most of these cases, so you skip a lot of ugly logic in your theme.
|
||||
- Twig is very **widespread**, so you can work with Twig in many other cms.
|
||||
|
||||
The full Twig documentation for template designers is just one page long, so just head [over to Twig](http://twig.sensiolabs.org/doc/2.x/templates.html) and read it. You can learn the most important essentials for TYPEMILL in the following list.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Basic Twig Syntax
|
||||
|
||||
In a Twig template, you can use ordinary HTML markup. Statements and expressions are written in curly brackets.
|
||||
|
||||
Twig uses two curly brackets **to print out** a variable or expression:
|
||||
|
||||
````
|
||||
<p>{{ variable }}</p>
|
||||
````
|
||||
|
||||
Twig uses one curly bracket with a procent sign **to execute** statements such as loops:
|
||||
|
||||
````
|
||||
<ul>
|
||||
{% for element in breadcrumb %}
|
||||
<li> {{ element.output|e }} </li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
````
|
||||
|
||||
As you can see, the Twig syntax is cleaner and easier than pure PHP:
|
||||
|
||||
- You don't need the long `<?php echo something; ?>` introduction.
|
||||
- You don't need the `$` to mark a variable.
|
||||
- You don't need the `;` to finish a statement.
|
||||
- You don't need the `->` or `['foo']` notation for objects and arrays, just use a dot-notation like `element.name` for everything.
|
||||
- You don't need a lot of `()` like `foreach(a as b)`.
|
||||
- You don't need a syntax like `<?php echo htmlspecialchars($element->output, ENT_QUOTES, 'UTF-8') ?>` for escaping, just use a filter with a pipe notation like this `{{ element.output|e }}`.
|
||||
|
||||
## References
|
||||
|
||||
These are some useful examples and snippets, that you can use for your templates.
|
||||
|
||||
### Simple Variable
|
||||
|
||||
Set a simple variable and print it out:
|
||||
|
||||
````
|
||||
{% set content = "my content" %}
|
||||
{{ content }}
|
||||
````
|
||||
|
||||
### Array
|
||||
|
||||
Set an array and print out a value:
|
||||
|
||||
````
|
||||
{% set content = ['first' => 'one value', 'second' => 'another value'] %}
|
||||
|
||||
{{ content.first }} // prints out 'one value'
|
||||
````
|
||||
|
||||
### Object
|
||||
|
||||
Set an object and print out a value:
|
||||
|
||||
````
|
||||
{% set content = {'first' : 'first value', 'second' : 'another value'} %}
|
||||
|
||||
{{ content.first }} // prints out 'first value'
|
||||
````
|
||||
|
||||
### Loop
|
||||
|
||||
Loop over an object or array and print out the values:
|
||||
|
||||
````
|
||||
{% for value in content %}
|
||||
{{ value }}
|
||||
{% endfor %}
|
||||
````
|
||||
|
||||
Outputs:
|
||||
|
||||
- first value
|
||||
- another value
|
||||
|
||||
### Filters
|
||||
|
||||
Set the first character of the words to uppercase:
|
||||
|
||||
````
|
||||
<ul>
|
||||
{% for value in content %}
|
||||
<li>{{ value|title }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
````
|
||||
|
||||
Output:
|
||||
|
||||
- First Value
|
||||
- Another Value
|
||||
|
||||
You can manipulate variables with filters. Filters are used after a pipe notation. See a list of all filters in the [Twig documentation](http://twig.sensiolabs.org/doc/2.x/filters/index.html).
|
||||
|
||||
### Functions
|
||||
|
||||
Print out content that was created in the last 30 days:
|
||||
|
||||
```
|
||||
{% if date(content.created_at) > date('-30days') %}
|
||||
{{ content.title }}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
'created_at' could be a timestamp of the content file. See a list of all functions in the [Twig documentation](https://twig.sensiolabs.org/doc/2.x/functions/index.html).
|
||||
|
||||
### Include Template
|
||||
|
||||
To include a template, just write:
|
||||
|
||||
````
|
||||
{{ include(sidebar.twig) }}
|
||||
````
|
||||
|
||||
**Example usage**: Your layout-template **includes** other templates like header.twig, footer.twig or sidebar.twig.
|
||||
|
||||
### Extend Template
|
||||
|
||||
To extend a template, just write:
|
||||
|
||||
````
|
||||
{% extends "partials/layout.twig" %}
|
||||
````
|
||||
|
||||
**Example usage:** Your content template (e.g. index.twig) **extends** your layout template. This means, index.twig is rendered within the layout.twig, and the layout.twig includes a header.twig, a footer.twig and a sidebar.twig.
|
||||
|
||||
### Example: Include and Extend
|
||||
|
||||
If you extend a template with another template (e.g. if you extend `layout.twig` with `index.twig`), then you have to define some content areas in the "parent" template that get overwritten with the content of the "child" template. You can use the "block" statement to define such areas.
|
||||
|
||||
Your layout.twig looks like this:
|
||||
|
||||
````
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
<article>{% block content %}{% endblock %}</article>
|
||||
<nav>
|
||||
{{ inlude 'navigation.twig' }}
|
||||
</nav>
|
||||
<footer>
|
||||
{{ include 'footer.twig' }}
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
````
|
||||
|
||||
Your index.twig looks like this:
|
||||
|
||||
````
|
||||
{% extends "layout.twig" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<ul>
|
||||
{% for value in content%}
|
||||
<li>{{ value }}</li>
|
||||
{% endfor }
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
````
|
||||
|
||||
Now, your template `index.twig` extends your template `layout.twig` and the `block content` in your layout template gets replaced by the `block content` defined in your index template. At the same time the layout template includes the navigation and the footer.
|
||||
|
||||
### Macros
|
||||
|
||||
Macros in Twig are like functions in PHP: You can use them for repeating tasks. A typical example is a navigation, where you loop over a comlex and multi-dimensional array recursively. But you can also use macros to render forms and input fields.
|
||||
|
||||
Macros are usually the most complex parts of your theme and they are mostly used for generating a navigation. You can use the macro for the navigation used in the typemill theme as a starting point. In most cases, you won't even touch it, but if you read the code in detail, it will probably pretty easy for you to costumize it for your needs.
|
||||
|
||||
A typical macro code for a navigation looks like this:
|
||||
|
||||
{% macro loop_over(navigation) %}
|
||||
|
||||
{% import _self as macros %}
|
||||
|
||||
{% for element in navigation %}
|
||||
<li>
|
||||
{% if element.elementType == 'folder' %}
|
||||
<a href="{{ element.url }}">{{ element.name|title }}</a>
|
||||
<ul>
|
||||
{{ macros.loop_over(element.folderContent) }}
|
||||
</ul>
|
||||
{% else %}
|
||||
<a href="{{ element.url }}">{{ element.name|title }}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% import _self as macros %}
|
||||
|
||||
<ul class="main-menu">
|
||||
{{ macros.loop_over(navigation) }}
|
||||
</ul>
|
||||
These are only some small examples, how you can use Twig to create templates and themes for TYPEMILL. In fact, you can do a lot more complex stuff with Twig. Just read the [official documentation](https://twig.sensiolabs.org/doc).
|
||||
|
@ -1,6 +0,0 @@
|
||||
# Content
|
||||
|
||||
The content-variable holds the whole content of your Markdown file in HTML. To print out the content of the Markdown file, simply write:
|
||||
|
||||
{{ content }}
|
||||
You can only use Twig filters to manipulate the content, but the possibilities are limited. Usually you should not hack into the content, but if you really need it (e.g. to display adds or to show a subscription form), then you have to create a plugin for this.
|
@ -1,21 +0,0 @@
|
||||
# Title
|
||||
|
||||
The title tag returns the first `<h1>` headline used in the content file. If ther is no headline, it uses the file name.
|
||||
|
||||
````
|
||||
{{ title }}
|
||||
````
|
||||
|
||||
You can use the title for the HTML-title like this:
|
||||
|
||||
````
|
||||
<title>{{ title }}</title>
|
||||
````
|
||||
|
||||
And you can of course manipulate the title with a Twig filter like this:
|
||||
|
||||
````
|
||||
{{ title|title }}
|
||||
````
|
||||
|
||||
This will display the first character of each word in uppercase.
|
@ -1,21 +0,0 @@
|
||||
# Description
|
||||
|
||||
The description variable extracts the first lines out of the content. It is usually about 300 characters long and you can use this for the meta-description, for teasers and for snippets.
|
||||
|
||||
{{ description }}
|
||||
|
||||
Use it for the description meta-tag like this:
|
||||
|
||||
````
|
||||
<meta name="description" content="{{ description }}" />
|
||||
````
|
||||
|
||||
You can also manipulate the description with Twig-filters, if you want. For example the filter...
|
||||
|
||||
````
|
||||
{{ description|slice(0,100) }}
|
||||
````
|
||||
|
||||
... will output the first 100 characters of the description.
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
# Image
|
||||
|
||||
If present, the image tag will return the url and the alt-tag of the first image used in the article:
|
||||
|
||||
````
|
||||
{{ image.img_url }}
|
||||
{{ image.img_alt }}
|
||||
````
|
||||
|
||||
This can be pretty handy if you want to use an header image or if you want to add meta-tags for social media networks. The Typemill standard themes uses meta-tags for twitter and facebook, so that the image get's displayed in the social media posts. It can look like this:
|
||||
|
||||
````
|
||||
<meta property="og:image" content="{{ image.img_url }}">
|
||||
<meta name="twitter:image:alt" content="{{ image.img_alt }}">
|
||||
````
|
||||
|
@ -1,14 +0,0 @@
|
||||
# Base Url
|
||||
|
||||
Whenever you want to create some urls in your theme, you can build them with the base_url tag. The base url always returns the basic url to your application. Usually this is the domain-name, but if you develop on localhost, it can also be something like `http://localhost/your-project-folder`.
|
||||
|
||||
````
|
||||
{{ base_url }}
|
||||
````
|
||||
|
||||
If you develop your theme, the base url is pretty useful if you want to include some assets like CSS or JavaScript. You can reference to these files like this:
|
||||
|
||||
````
|
||||
<link rel="stylesheet" href="{{ base_url }}/themes/typemill/css/style.css" />
|
||||
````
|
||||
|
@ -1,311 +0,0 @@
|
||||
# Item
|
||||
|
||||
The item variable is an object. It provides informations about the actual page like the page title, the url, the slug or the next and the previous page.
|
||||
|
||||
Some informations are only available for the type `folder` while some other informations are specific to the type `file`. But most informations are shared by both.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Example of the {{ item }} variable
|
||||
|
||||
This is an example of an item variable:
|
||||
|
||||
````
|
||||
stdClass Object
|
||||
(
|
||||
[originalName] => 25-navigation.md
|
||||
[elementType] => file
|
||||
[fileType] => md
|
||||
[order] => 25
|
||||
[name] => navigation
|
||||
[slug] => navigation
|
||||
[path] => \3_for-developers\05-theme-variables\25-navigation.md
|
||||
[key] => 4
|
||||
[keyPath] => 3.3.4
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => 3
|
||||
[2] => 4
|
||||
)
|
||||
[chapter] => 4.4.5
|
||||
[urlRel] => /typemill/developers/theme-variables/navigation
|
||||
[urlAbs] => http://localhost/typemill/developers/theme-variables/navigation
|
||||
[active] => 1
|
||||
[modified] => 1525088781
|
||||
[thisChapter] => stdClass Object
|
||||
(
|
||||
[originalName] => 05-theme-variables
|
||||
[elementType] => folder
|
||||
[index] => 1
|
||||
[order] => 05
|
||||
[name] => theme variables
|
||||
[slug] => theme-variables
|
||||
[path] => \3_for-developers\05-theme-variables
|
||||
[urlRel] => /typemill/developers/theme-variables
|
||||
[urlAbs] => http://localhost/typemill/developers/theme-variables
|
||||
[key] => 3
|
||||
[keyPath] => 3.3
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => 3
|
||||
)
|
||||
|
||||
[chapter] => 4.4
|
||||
[active] => 1
|
||||
[activeParent] => 1
|
||||
)
|
||||
|
||||
[prevItem] => stdClass Object
|
||||
(
|
||||
[originalName] => 15-breadcrumb.md
|
||||
[elementType] => file
|
||||
[fileType] => md
|
||||
[order] => 15
|
||||
[name] => breadcrumb
|
||||
[slug] => breadcrumb
|
||||
[path] => \3_for-developers\05-theme-variables\15-breadcrumb.md
|
||||
[key] => 3
|
||||
[keyPath] => 3.3.3
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => 3
|
||||
[2] => 3
|
||||
)
|
||||
|
||||
[chapter] => 4.4.4
|
||||
[urlRel] => /typemill/developers/theme-variables/breadcrumb
|
||||
[urlAbs] => http://localhost/typemill/developers/theme-variables/breadcrumb
|
||||
)
|
||||
|
||||
[nextItem] => stdClass Object
|
||||
(
|
||||
[originalName] => 30-settings.md
|
||||
[elementType] => file
|
||||
[fileType] => md
|
||||
[order] => 30
|
||||
[name] => settings
|
||||
[slug] => settings
|
||||
[path] => \3_for-developers\05-theme-variables\30-settings.md
|
||||
[key] => 5
|
||||
[keyPath] => 3.3.5
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => 3
|
||||
[2] => 5
|
||||
)
|
||||
|
||||
[chapter] => 4.4.6
|
||||
[urlRel] => /typemill/developers/theme-variables/settings
|
||||
[urlAbs] => http://localhost/typemill/developers/theme-variables/settings
|
||||
)
|
||||
|
||||
)
|
||||
````
|
||||
|
||||
## Shared properties
|
||||
|
||||
The following informations (properties) are shared by folders and files. The examples are based on a simple file and folder structure like this:
|
||||
|
||||
- content
|
||||
- 01-my-folder
|
||||
- index.md
|
||||
- 04-my-content-file.md
|
||||
|
||||
### {{ item.elementType }}
|
||||
|
||||
The type of the item. Possible values are:
|
||||
|
||||
- "file"
|
||||
- "folder"
|
||||
|
||||
You can check the elementType and display a folder in a different way than a content file.
|
||||
|
||||
Example: `{% if item.elementType == 'folder' %}`
|
||||
|
||||
### {{ item.urlRel }}
|
||||
|
||||
The relative url of the item without the base url. This is useful if you want to set a link to another internal page.
|
||||
|
||||
Example: `/my-folder/my-content-file`
|
||||
|
||||
### {{ item.urlAbs }}
|
||||
|
||||
The absolute url of the item. This is useful for cannonical links, social media links or permalinks.
|
||||
|
||||
Example: `http://mydomain.com/my-folder/my-content-file`
|
||||
|
||||
### {{ item.slug }}
|
||||
|
||||
The slug of the file or folder. This is the last part of the url.
|
||||
|
||||
Example: `/my-content-file` in the url `www.mywebsite.com/my-folder/my-content-file`.
|
||||
|
||||
### {{ item.name }}
|
||||
|
||||
The human readable name of the file or folder.
|
||||
|
||||
Example: `my content file` for a Mardown file with a name like `01.my-content-file.md`.
|
||||
|
||||
### {{ item.originalName }}
|
||||
|
||||
The original name of the file or folder. You probably don't need it for your theme.
|
||||
|
||||
Example: `04.my-content-file.md` or `1.my-folder`.
|
||||
|
||||
### {{ item.path }}
|
||||
|
||||
The physical path to the item on your server. You probably don't need that, but TYPEMILL uses this information to map the urls with the content files and folders.
|
||||
|
||||
Example: `\1.my-folder\04.my-content-file.md`.
|
||||
|
||||
### {{ item.order }}
|
||||
|
||||
The prefix of the item for ordering. You probably don't need it for your theme.
|
||||
|
||||
Example: `1` for the folder and `04` for the file.
|
||||
|
||||
### {{ item.active }}
|
||||
|
||||
The item.active indicates, if the item is active or not. You probably don't need it in a page content, because the current page is always an active page, too. But you will need this in another context, for example, if you create a navigation.
|
||||
|
||||
### {{ item.key }}
|
||||
|
||||
The key of the item within the navigation array. You probably don't need that.
|
||||
|
||||
Example: `2`.
|
||||
|
||||
### {{ item.keyPath }}
|
||||
|
||||
The full key path of the item within the navigation array. You probably don't need that.
|
||||
|
||||
Example: `1.3.2`
|
||||
|
||||
### {{ item.keyPathArray }}
|
||||
|
||||
The full key path of the item within the navigation array as an array instead of a string.
|
||||
|
||||
Example: `array(0 => 1, 1 => 3, 2 => 2 )`
|
||||
|
||||
This might be useful if you want to determine the depth of the item within the content structure.
|
||||
|
||||
Example: `item.keyPathArray|length` returns `3`, so you know that the page exists in third level of the content structure.
|
||||
|
||||
### {{ item.chapter }}
|
||||
|
||||
The human readable key path of the item as a string. Different to the key path, it starts with `1` instead of `0`. You can use it to print out a chapter number.
|
||||
|
||||
Example: `2.4.3`
|
||||
|
||||
### {{ item.thisChapter }}
|
||||
|
||||
The parent chapter of the current item. If the current item is 1.3.2, then the parent chapter is 1.3.
|
||||
|
||||
The variable `item.thisChapter` is an item object again, so you have access to all the above informations. This way, you can display the parent chapter's name or create a link to the parent chapter on the page.
|
||||
|
||||
Example: `<a href="{{ item.thisChapter.urlRel }}">{{ item.thisChapter.name}}</a>`
|
||||
|
||||
### {{ item.nextItem}}
|
||||
|
||||
The next item. If the current item is 1.3.2, then the next item might be 1.3.3 or 1.4.
|
||||
|
||||
The `item.nextItem` is an item object again, so you have access to all the informations explained above. You can use the nextItem to create a pagination.
|
||||
|
||||
Example: `<a href="{{ item.nextItem.urlRel }}">{{ item.nextItem.name }}</a>`
|
||||
|
||||
### {{ item.prevItem}}
|
||||
|
||||
The previous item. If the current item is 1.3.2, then the previous item is 1.3.1. If the current item is 1.3, then the previous item might be 1.2.8.
|
||||
|
||||
The `item.prevItem` is an item object again, so you have access to all the informations explained above. You can use the prevItem to create a pagination.
|
||||
|
||||
Example: `<a href="{{ item.prevItem.urlRel }}">{{ item.prevItem.name }}</a>`
|
||||
|
||||
### {{ item.modified }}
|
||||
|
||||
Returns the last modified date of the file as a number. You can use this to print out the last modified date in your theme. Use the date-function of Twig to format the date.
|
||||
|
||||
Example: `Last modified: {{ item.modified|date(m/d/Y) }}`
|
||||
|
||||
## Specific to Folders or Files
|
||||
|
||||
The following informations are specific to files or folders
|
||||
|
||||
### {{ item.fileType }}
|
||||
|
||||
This information is only available for **files**. The fileType is `md` for Markdown. You will probably not need it for your theme.
|
||||
|
||||
Example: `{% if item.elementType == 'file' %} {{ item.fileType}} {% endif %}`
|
||||
|
||||
### {{ item.index }}
|
||||
|
||||
This information is only available for **folders**. It indicates, if there exits an `index.md` file in the folder or not. If there is no index file with content for the folder, then you can display an alternative content.
|
||||
|
||||
The whole usecase might look like this:
|
||||
|
||||
```
|
||||
{% if item.elementType == 'folder' %}
|
||||
{% if item.index %}
|
||||
{{ content }}
|
||||
{% else %}
|
||||
<h1>{{ item.name }}</h1>
|
||||
<p>Your alternative static content for a folder.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### {{ item.folderContent }}
|
||||
|
||||
This information is only available for **folders**. It contains the whole content of that folder as a multidimensional array of item objects. You can use it to list the content of a folder.
|
||||
|
||||
To do so, you have two options:
|
||||
|
||||
1. Display only the first level items in the current folder. This can be done with a simple `for` loop.
|
||||
2. Display all nested items and folders within the current folder recursively. You have to write a Twig macro for that.
|
||||
|
||||
The simple solution with all first level items of the current folder looks like this:
|
||||
|
||||
````
|
||||
{% if item.elementType == 'folder' %}
|
||||
<ul>
|
||||
{% for sub in item.folderContent %}
|
||||
<li>{{ sub.itemName }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
````
|
||||
|
||||
To display all items within the current folder you have to create a macro in a separate file like this first:
|
||||
|
||||
````
|
||||
{% macro loop_over(folder) %}
|
||||
{% for element in folder %}
|
||||
{% if element.elementType == 'folder' %}
|
||||
<a href="{{ element.urlRel }}">{{ element.name }}</a>
|
||||
<ul>
|
||||
{{ macros.loop_over(element.folderContent) }}
|
||||
</ul>
|
||||
{% else %}
|
||||
<a href="{{ element.urlRel }}">{{ element.name }}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
````
|
||||
|
||||
Then you have to import the macro into your template and call it like this:
|
||||
|
||||
```
|
||||
{% import 'folderMacro.twig' as macros %}
|
||||
{% if item.elementType == 'folder' %}
|
||||
<ul>
|
||||
{{ macros.loop_over(item.folderContent) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
You will learn more about macros in the chapter about the navigation variable.
|
||||
|
@ -1,11 +0,0 @@
|
||||
# Breadcrumb
|
||||
|
||||
The `{{ breadcrumb }}` variable contains the breadcrumb for the page. It is a simple one dimensional array of item objects. You can loop over the breadcrumb and print the elements out like this:
|
||||
|
||||
<ul class="breadcrumb">
|
||||
{% for element in breadcrumb %}
|
||||
<li><a href="{{ element.urlRel }}">{{ element.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
All informations of the items are available, so check the chapter about the item variable for more details.
|
@ -1,102 +0,0 @@
|
||||
# Navigation
|
||||
|
||||
The variable `{{ navigation }}` represents the structure of the whole content folder and can be used to create a navigation.
|
||||
|
||||
The `{{ navigation }}` variable is a multi dimensional array of item objects. With that array you have access to nearly all informations, that an item object provides. Only the following informations for the paging is not part of the item objects within the navigation variable:
|
||||
|
||||
- thisChapter
|
||||
- nextItem
|
||||
- prevItem
|
||||
|
||||
The chapter about the `{{ item }}` variable lists all informations, that are provided by the item object. Read it, if you haven't done it yet.
|
||||
|
||||
## Example of the {{ navigation }} variable
|
||||
|
||||
This is an example of the `{{ navigation }}` variable containing just one folder and a file:
|
||||
|
||||
Array(
|
||||
[0] => stdClass Object(
|
||||
[originalName] => 0_about-typemill
|
||||
[elementType] => folder
|
||||
[index] => 1
|
||||
[order] => 0
|
||||
[name] => about typemill
|
||||
[slug] => about-typemill
|
||||
[path] => \0_about-typemill
|
||||
[urlRel] => /about-typemill
|
||||
[urlAbs] => http://localhost/about-typemill
|
||||
[key] => 0
|
||||
[keyPath] => 0
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 0
|
||||
)
|
||||
[chapter] => 1
|
||||
[folderContent] => Array
|
||||
(
|
||||
[0] => stdClass Object(
|
||||
[originalName] => 02-what-is-mardown.md
|
||||
[elementType] => file
|
||||
[fileType] => md
|
||||
[order] => 02
|
||||
[name] => what is mardown
|
||||
[slug] => what-is-mardown
|
||||
[path] => \0_about-robodoc\02-what-is-mardown.md
|
||||
[key] => 0
|
||||
[keyPath] => 0.0
|
||||
[keyPathArray] => Array
|
||||
(
|
||||
[0] => 0
|
||||
[1] => 0
|
||||
)
|
||||
[chapter] => 1.1
|
||||
[urlRel] => /about-robodoc/what-is-mardown
|
||||
[urlAbs] => http://localhost/about-robodoc/what-is-mardown
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
## Create a Navigation for Your Theme
|
||||
|
||||
To print out the navigation or a table of contents, you have to loop over `{{ navigation }}` recursively. In Twig, you can do this with a macro.
|
||||
|
||||
In the following example, the macro is integrated in a separate template called "navigation.twig". You can also create a separate file with the macro (e.g. "navMacro.twig") and import it into your navigation template.
|
||||
|
||||
The whole usecase with the macro and the navigation in one template looks like this:
|
||||
|
||||
{# define the macro #}
|
||||
{% macro loop_over(navigation) %}
|
||||
{% import _self as macros %}
|
||||
{% for item in navigation %}
|
||||
<li>
|
||||
{% if item.elementType == 'folder' %}
|
||||
{% if item.index %}
|
||||
<a href="{{ item.urlRel }}">{{ item.name }}</a>
|
||||
{% else %}
|
||||
{{ item.name }}
|
||||
{% endif %}
|
||||
<ul>
|
||||
{{ macros.loop_over(item.folderContent) }}
|
||||
</ul>
|
||||
{% else %}
|
||||
<a href="{{ item.urlRel }}">{{ item.name }}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{# import the macro and use it to create the navigation #}
|
||||
{% import _self as macros %}
|
||||
<nav>
|
||||
<ul class="main-menu">
|
||||
{{ macros.loop_over(navigation) }}
|
||||
</ul>
|
||||
</nav>
|
||||
Just as a recommendation for your theme-structure: Typically you create a separate file like `navigation.twig` with all the code above. Then you place this template in a folder like `partials`. You can include this navigation-file in a `layout.twig` file, so that the navigation is included in all pages of your theme. So the structure might look like this:
|
||||
|
||||
- theme
|
||||
- partials
|
||||
- layout.twig // includes navigation
|
||||
- navigation.twig
|
||||
- index.twig // extends layout.twig
|
@ -1,85 +0,0 @@
|
||||
# Settings
|
||||
|
||||
The `{{ settings }}` variable is a simple array. It contains all settings throughout the TYPEMILL system, in detail:
|
||||
|
||||
* The user settings for the system.
|
||||
* The user settings for the themes.
|
||||
* The user settings for the plugins.
|
||||
|
||||
All these settings are merged and managed by TYPEMILL in the background. This is pretty handy because you can use all settings within one variable.
|
||||
|
||||
## Useful Settings
|
||||
|
||||
The following settings might be useful for your theme:
|
||||
|
||||
### {{ settings.title }}
|
||||
|
||||
The title of the website. The default value is `TYPEMILL`.
|
||||
|
||||
### {{ settings.author }}
|
||||
|
||||
Thee author of the website. The default value is `unknown`.
|
||||
|
||||
### {{ settings.copyright }}
|
||||
|
||||
The copyright of the website. The default value is `copyright`.
|
||||
|
||||
### {{ settings.startpage }}
|
||||
|
||||
Has a separate startpage or not. Default value is `true`.
|
||||
|
||||
### {{ settings.theme }}
|
||||
|
||||
The name of the theme that is in use. Default value is `typemill`.
|
||||
|
||||
### {{ settings.version }}
|
||||
|
||||
The version of TYPEMILL that is in use. A value of the format `0.0.1`.
|
||||
|
||||
## Additional Settings
|
||||
|
||||
There a some more settings that are probably not very useful for your theme:
|
||||
|
||||
### {{ settings.themeFolder }}
|
||||
|
||||
The folder of the theme. The default value is `theme`.
|
||||
|
||||
### {{ settings.contentFolder }}
|
||||
|
||||
The folder of the content. The default value is `content`.
|
||||
|
||||
### {{ settings.rootPath }}
|
||||
|
||||
The full path to the root of the website.
|
||||
|
||||
### {{ settings.themePath }}
|
||||
|
||||
The full path to the theme of the website.
|
||||
|
||||
### {{ settings.authorPath }}
|
||||
|
||||
The full path to the author theme. This theme is actually only in use for the setup path, but might hold an admin dashboard in future.
|
||||
|
||||
### {{ settings.displayErrorDetails }}
|
||||
|
||||
If the error display is off or on. Default value is `false`.
|
||||
|
||||
Some more informations are provided by the Slim framework, that runs under the hood of TYPEMILL. You will probably never use them.
|
||||
|
||||
## Settings for Themes and Plugins
|
||||
|
||||
You have also access to all plugins- and theme-settings. Simply use them like this:
|
||||
|
||||
````
|
||||
{{ settings.themes.mytheme.mythemesetting }}
|
||||
{{ settings.plugins.myplugin.mypluginsetting}}
|
||||
````
|
||||
|
||||
You have to replace `mytheme` and `myplugin` with the name of the theme or plugin and the `mythemesettings` and `mypluginsettings` with the name of the settings. you can find the name of the settings in the YAML-file of the plugin or theme.
|
||||
|
||||
For example: The standard theme of TYPEMILL is called `typemill`. And there you can edit the label of the start button displayed on the startpage. The name of the variable is `start`. So you can simply print it out like this:
|
||||
|
||||
````
|
||||
<button>{{ settings.themes.typemill.start }}</button>
|
||||
````
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Theme Variables
|
||||
|
||||
TYPEMILL provides **9 variables** for your theme right now. They are easy to understand and simple to use.
|
@ -1,3 +0,0 @@
|
||||
# Hello Theme-Developers!
|
||||
|
||||
If you love **lightweight systems**, stupid **simple theming** and the template language **Twig**, then TYPEMILL is for you!
|
@ -1,40 +0,0 @@
|
||||
# Introducing Plugins
|
||||
|
||||
Everybody loves plugins. With plugins, users can add new functionalities to their website without the help of a developer. And developers can write and distribute their own plugins without ever touching or changing the core code of a software. Plugins are a great and widespread concept, so we introduced a plugin-system for TYPEMILL with version 1.1.0.
|
||||
|
||||
## What Plugins Can Do
|
||||
|
||||
With themes, developers can create an individual design for a TYPEMILL-website. With plugins, they can add individual functionality to a TYPEMILL-website. Some examples:
|
||||
|
||||
* A plugin can add a cookie consent banner to your website.
|
||||
* A plugin can integrate an analytics script like Piwik or Google Analytics into your website.
|
||||
* With a plugin you can create a new Twig tag, that can be used by a theme developer in the frontend (similar to a shortcut in WordPress).
|
||||
* A plugin can be really complex and add a complete admin area to TYPEMILL.
|
||||
|
||||
## Your Assumed Knowledge
|
||||
|
||||
Crafting a plugin is usually a bit more complex than crafting a theme. You can create a theme with a good knowledge of HTML, CSS, JavaScript and some basic coding skills in PHP like variables and loops. That's it.
|
||||
|
||||
To create a plugin, you should have a deeper understanding of PHP development. Let's try it that way:
|
||||
|
||||
* You know what objects and classes in PHP are: That's good !
|
||||
* You know what namespacing in PHP is: Great, let's start !!
|
||||
* You know MVC and Controllers in PHP: Not really needed, but good to know !!!
|
||||
* You already worked with the Slim PHP framework or any other PHP framework before: Fantastic !!!!
|
||||
* You know how to work with events in PHP: I think you can skip this tutorial :)
|
||||
|
||||
Object orientation and namespacing are really required, so if you are not familiar with that, then start to learn it today. Object orientation is standard in PHP nowadays and there are dozens of good tutorials.
|
||||
|
||||
A knowledge of the Slim framework or event-driven programming in PHP is not required to start with your first TYPEMILL-plugin. You will learn as much as you need in this tutorial and in the documentation.
|
||||
|
||||
## How To Learn
|
||||
|
||||
If you are familiar with plugin development, flat file systems and event driven programming, than the documentation is enough to get started. TYPEMILL has its own concept, but it still works pretty similar to other modern systems like Statamic, Kirby or Grav.
|
||||
|
||||
If you are new to flat file systems and plugin development, then you should start with the tutorial. In the tutorial, you will learn how to create the cookie consent plugin step by step. The tutorial is pretty basic and detailed, so that beginners have a good chance to master it.
|
||||
|
||||
## Before you start
|
||||
|
||||
In Version 1.1.X, the plugin system is pretty basic and still under development. You will probably have to adjust your plugins as the plugin system of TYPEMILL evoles over the time. If you miss something in the current plugin system (e.g. some events), feel free to post it on github.
|
||||
|
||||
Happy coding!!
|
@ -1,56 +0,0 @@
|
||||
# The Cookie Consent Plugin
|
||||
|
||||
Let's get our hands dirty and look into the cookie consent plugin. The cookie consent plugin adds a little banner to each page of a website, so that the user can agree to the website's cookie policy.
|
||||
|
||||
You might think, that you do not need a plugin for that. And you are right: You can simply visit the [cookieconsent website](https://cookieconsent.insites.com/), configure your cookie consent, copy the code and paste it into your theme. It is only a bit of JavaScript and CSS. The script from the cookie consent website looks like this:
|
||||
|
||||
```
|
||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
|
||||
<script>
|
||||
window.addEventListener("load", function(){
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "#d48484",
|
||||
"text": "#ffffff"
|
||||
},
|
||||
"button": {
|
||||
"background": "#362929",
|
||||
"text": "#fff"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"message": "This website uses cookies to ensure you get the best experience on our website.",
|
||||
"dismiss": "OK",
|
||||
"link": "Learn more"
|
||||
}
|
||||
})});
|
||||
</script>
|
||||
```
|
||||
|
||||
So what is the point to create a plugin just to add this little script to a website?
|
||||
|
||||
## The Problem With Hardcoding
|
||||
|
||||
To hardcode the cookie consent script manually into your TYPEMILL-theme has two downsides:
|
||||
|
||||
- As a developer, I have to touch the templates of the TYPEMILL-theme and add the cookie consent script there. And each time, if I update the theme, I have to add the script again.
|
||||
- As an author or admin, I cannot change the text or the color of the cookie consent in the setup area, and I cannot activate or deactivate it. Instead I have to open the template files in a code editor and work like a developer.
|
||||
|
||||
Wouldn't it be much better to configure the cookie consent in the setup area of TYPEMILL and to add the cookie consent to a theme without even touching it?
|
||||
|
||||
Of course, so let's try it.
|
||||
|
||||
## How The Plugin Should Work
|
||||
|
||||
Before we start, let's describe, how the cookie consent plugin should work:
|
||||
|
||||
- The plugin should add a CSS-file into the html-head of the theme-templates.
|
||||
- The plugin should add a JavaScript-file at the bottom of the theme-templates.
|
||||
- After the JavaScript file, the plugin should add the initial script with the values for the colors and the content.
|
||||
- And finally, the content- and color-values should be editable, so that the user can change them in the plugin settings.
|
||||
|
||||
## Next: Create a File Structure
|
||||
|
||||
In the next chapters, we will learn how we can add a cookie banner easily with a TYPEMILL plugin. Let's start with a file structure.
|
@ -1,77 +0,0 @@
|
||||
# Write the YAML-Configuration-File
|
||||
|
||||
Every plugin should have a basic configuration file with some meta-informations like the version, the licence, and the author.
|
||||
|
||||
The configuration file is located in the root-folder of your plugin and must have the same name as the plugin folder. For the cookie-consent-plugin, this looks like this:
|
||||
|
||||
````
|
||||
/cookieconsent
|
||||
- cookieconsent.yaml
|
||||
- cookieconsent.php
|
||||
````
|
||||
|
||||
The configuration file is written in simple YAML syntax. The basic YAML-file for the cookie consent plugin looks like this:
|
||||
|
||||
````
|
||||
name: Cookie Consent
|
||||
version: 1.0.0
|
||||
description: Enables a cookie consent for websites
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://cookieconsent.insites.com/
|
||||
licence: MIT
|
||||
````
|
||||
|
||||
## YAML Transforms to Arrays
|
||||
|
||||
If you have never heared about YAML: YAML is a simple configuration language. And with a library you can easily transform a YAML file into an array. That is why YAML is so widespread. In TYPEMILL, the basic informations are transformed into a simple one-dimensional array like this:
|
||||
|
||||
````
|
||||
array (
|
||||
'name' => 'Cookie Consent,
|
||||
'version => '1.0.0',
|
||||
'description' => Enables a cookie consent for websites',
|
||||
'author' => 'Sebastian Schürmanns',
|
||||
'homepage' => 'https://cookieconsent.insites.com/',
|
||||
'licence' => 'MIT'
|
||||
)
|
||||
````
|
||||
|
||||
Of course, you can also create a multi-dimensional array. This works with simple indentation. Two spaces indicate the next level. A YAML definition like this:
|
||||
|
||||
````
|
||||
options:
|
||||
first: yellow
|
||||
second: green
|
||||
````
|
||||
|
||||
Would transform to this array:
|
||||
|
||||
````
|
||||
array(
|
||||
'options' => array(
|
||||
'first' => 'yellow',
|
||||
'second' => 'green
|
||||
)
|
||||
)
|
||||
````
|
||||
|
||||
When you write YAML, be careful and only use spaces for indentation. If you use the tab key, then the tranformation will break.
|
||||
|
||||
## More Configurations
|
||||
|
||||
For now, we have only some basic informations in your configuration file. But there are more possibilities and we will learn about them later. For now just remember, that a configuration file can have three parts depending on the plugin's needs:
|
||||
|
||||
* Basic informations about the plugin.
|
||||
* Default values for the plugin.
|
||||
* Definitions for input fields, so that the user can change the default values in the setup.
|
||||
|
||||
## Background
|
||||
|
||||
In theory, a very simple plugin can work without a configuration file. But it is highly recommended to add a configuration file to any plugin. Here are the reasons:
|
||||
|
||||
* TYPMILL uses the version number in the configuration file to check, if there exists a newer version of the plugin. So this is pretty important.
|
||||
* The informations in the configuration file are displayed to the user in the setup of TYPEMILL. So this is pretty important, too.
|
||||
|
||||
## Next: Basic PHP-File
|
||||
|
||||
The configuration file was a pretty easy start. In the next chapter, we will get our hands dirty and write the first PHP-code.
|
@ -1,156 +0,0 @@
|
||||
# Write the Basic PHP-File
|
||||
|
||||
We have our basic configuration file, so let's start with our basic php file now. As mentioned before, the basic PHP file must have the same name as the plugin folder:
|
||||
|
||||
```
|
||||
/cookieconsent
|
||||
- cookieconsent.yaml
|
||||
- cookieconsent.php
|
||||
```
|
||||
|
||||
TYPEMILL uses object oriented PHP, so let us create a class for our plugin.
|
||||
|
||||
## Write The Plugin Class
|
||||
|
||||
Open the `cookieconsent.php`-file and add the following code:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace plugins\cookieconsent;
|
||||
|
||||
use \typemill\plugin;
|
||||
|
||||
class cookieconsent extends plugin
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Instead of `use \typemill\plugin;` you can also write:
|
||||
|
||||
```
|
||||
class cookieconsent extends \typemill\plugin
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Let us check, what is happening here:
|
||||
|
||||
- **namespace**: In the first line, we define the namespace for our plugin. The namespace always starts with `plugin\` followed by the plugin-name, so it is `plugin\cookieconsent`. Again: the namespace must have the same name as the folder and the file of your plugin.
|
||||
- **use namespace**: In the next line, we import the namespace for the plugin-class of TYPEMILL with `Use \typemill\plugins`.
|
||||
- **class**: In the next line, we create the plugin-class. Again: the class must have the same name as the plugin, so it is `class cookieconsent`.
|
||||
- **extends**: The class always extends the plugin base class of TYPEMILL, so it is `extends plugin` or, if you did not import the namespace with use before, then `extends \typemill\plugin`.
|
||||
|
||||
In case you are not familiar with the keyword `extend`: This means, that your plugin class extends the plugin class of TYPEMILL and this means, that your plugin class will incorporate a lot of useful methods from that TYPEMILL plugin class. We will work with these useful helper methods later in this tutorial.
|
||||
|
||||
## Add The First Method
|
||||
|
||||
Now we will add the first method. **Every plugin** must provide a static method called `getSubscribedEvents`. The method looks like this:
|
||||
|
||||
````
|
||||
<?php
|
||||
|
||||
namespace plugins\cookieconsent;
|
||||
|
||||
use \typemill\plugin;
|
||||
|
||||
class cookieconsent extends plugin
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onSettingsLoaded',
|
||||
'onTwigLoaded' => 'onTwigLoaded'
|
||||
);
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
As you can see, the method returns a simple array. And the name `getSubscribedEvents` indicates, that this has something to do with events. So it's time to talk about events now.
|
||||
|
||||
## Introducing Events
|
||||
|
||||
Nearly all content management systems use events to create a plugin system. So if you know what events are, then you can create plugins for all these systems much easier. But what are events in PHP?
|
||||
|
||||
If you have ever written some code in JavaScript, then you know, what events are. If not: An event system follows a pretty simple IFTTT-logic like: "If This Than That". If this happens, than do that. If that happens, then do this.
|
||||
|
||||
As a plugin developer, you do exactly the same: When someone visits the URL of a TYPEMILL website, then TYPEMILL creates the page in several steps. For each step, TYPEMILL fires a special event. As a developer, you 'listen' or 'subscribe' to some of these events, and if the event happens, then you tell the system to perform an action, or more specific, to call a method within your plugin code. That is basically the whole magic.
|
||||
|
||||
## Cookie Consent Event Logic
|
||||
|
||||
Let us describe such and IFTTT-logic for the cookie consent plugin. It is as simple as that:
|
||||
|
||||
* If the system has loaded all settings, then give me the settings.
|
||||
* If the system has loaded the template engine, then
|
||||
* add a CSS-file,
|
||||
* add a JavaScript-file and
|
||||
* inject a little sub-template with the initial JavaScript into the theme and
|
||||
* read the user settings and paste the values for colors and content into the script.
|
||||
|
||||
## Event Logic in an Array
|
||||
|
||||
Event systems work with arrays. And it is pretty easy to describe an IFTTT-logic with an array like this:
|
||||
|
||||
* "If this happens": That is the event. We use the event name as the key in our array.
|
||||
* "Than do that": That is the plugin method. We use the name of the plugin method as the value in our array.
|
||||
|
||||
|
||||
So when the event `onSettingsLoaded` is fired, then call the method `onSettingsLoaded` in our plugin code:
|
||||
|
||||
````
|
||||
array('onSettingsLoaded' => 'onSettingsLoaded');
|
||||
````
|
||||
|
||||
You can name your method in your plugin like you want, but it is a good practice to give the method the same name as the event.
|
||||
|
||||
As you can see, working with events is pretty simple.
|
||||
|
||||
## Add Another Method
|
||||
|
||||
To test this out, simply add a method called `onSettingsLoaded` and add some logic to the method:
|
||||
|
||||
````
|
||||
class cookieconsent extends plugin
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array('onSettingsLoaded' => 'onSettingsLoaded');
|
||||
}
|
||||
|
||||
public function onSettingsLoaded()
|
||||
{
|
||||
die('sorry to interrupt you!');
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
you can also add several to one event and you can even set the order, in which the methods should be called. This is done by a mulit-dimensional array like this:
|
||||
|
||||
````
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => array(
|
||||
array('myFirstMethod', 10),
|
||||
array('mySecondMethod', -10)
|
||||
)
|
||||
);
|
||||
}
|
||||
````
|
||||
|
||||
The method with the priority of `10` gets called first, the method with the priority of `-10` gets called last.
|
||||
|
||||
Just to complete your vocabulary: In event driven programming people talk about `listening` to events or `subscribing` to events. That is why the static method has the name `getSubscribedEvents`.
|
||||
|
||||
That is the whole magic with events!!!
|
||||
|
||||
## Event Overview
|
||||
|
||||
We will only use two events in our cookie consent plugin. But TYPEMILL fires (or "dispatches") many more events, which can be very useful if you want to create more complex plugins. You can find a list of all events in the [event overview of the documentation](/for-plugin-developers/documentation/event-overview).
|
||||
|
||||
## Next: Add Methods to your Plugin
|
||||
|
||||
We have subscribed to some events. Now it's time to add some logic to our plugin. Let's do this in the next chapter.
|
||||
|
@ -1,123 +0,0 @@
|
||||
# Write the Methods
|
||||
|
||||
Let us recap: We have our basic plugin-structure, we have your basic configuration file, we have our basic php file and in our php file we already subscribed to some events. Now let's add the IFTTT-logic, that we have described before:
|
||||
|
||||
- If the system has loaded all settings, then give me the settings.
|
||||
- If the system has loaded the template engine, then
|
||||
- add a CSS-file,
|
||||
- add a JavaScript-file and
|
||||
- inject a little sub-template with the initial JavaScript into the theme and
|
||||
- take the user settings and paste the values for colors and content into the script.
|
||||
|
||||
## Get the Settings
|
||||
|
||||
To get the settings, we only need one method and two lines of code:
|
||||
|
||||
````
|
||||
<?php
|
||||
|
||||
namespace plugins\cookieconsent;
|
||||
|
||||
use \typemill\plugin;
|
||||
|
||||
class cookieconsent extends plugin
|
||||
{
|
||||
|
||||
protected $settings;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array
|
||||
'onSettingsLoaded' => 'onSettingsLoaded',
|
||||
'onTwigLoaded' => 'onTwigLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
$this->settings = $settings->getData();
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
What you can see here: If you subscribe to an event, then many events pass some data to your method. For example, the event `onSettingsLoaded` passes the complete settings-object of TYPEMILL to any subscriber. All events support the same methods to get and to return the data like this:
|
||||
|
||||
````
|
||||
public function onAnyEvent($argument)
|
||||
{
|
||||
// get the data
|
||||
$data = $argument->getData();
|
||||
|
||||
// manipulate the data here
|
||||
|
||||
// return the manipulated data
|
||||
$argument->setData($data);
|
||||
}
|
||||
````
|
||||
|
||||
You can find more details in the [event overview](/for-plugin-developers/documentation/event-overview).
|
||||
|
||||
So we got the settings of TYPEMILL and we stored the settings in a variable. Now let's master the next step and add some CSS and JavaScript.
|
||||
|
||||
## Add Assets
|
||||
|
||||
TYPEMILL provides some handy methods in the plugin class (check the [list in the documentation](/for-plugin-developers/documentation/method-overview)). Among them are methods to handel assets and to add inline CSS or JavaScript. The methods are:
|
||||
|
||||
* `addCSS('url-to-ressource.css')`
|
||||
* `addInlineCSS('body{ background: #000; }')`
|
||||
* `addJS('url-to-ressource.js')`
|
||||
* `addInlineJS('alert("hello")')`
|
||||
|
||||
You have access to all these handy methods with the `$this` keyword. If you are not familiar with `$this`, then simply google it. In short: `$this` references to the current object.
|
||||
|
||||
You can add assets like CSS or JavaScript in every event. But in this case, we want to use the `onTwigLoaded`-event:
|
||||
|
||||
````
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
|
||||
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
|
||||
}
|
||||
````
|
||||
|
||||
If you want to add an internal asset like above, then use the relative path starting from your plugin folder. TYPEMILL will add the absolute path automatically.
|
||||
|
||||
## Add Initial JavaScript
|
||||
|
||||
In the last step, we want to add the JavaScript that initializes the cookie consent. We can do this very easily with the `addInlineJS`-method like this:
|
||||
|
||||
````
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
|
||||
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
|
||||
|
||||
$script = '
|
||||
window.addEventListener("load", function(){
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "#d48484",
|
||||
"text": "#ffffff"
|
||||
},
|
||||
"button": {
|
||||
"background": "#362929",
|
||||
"text": "#fff"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"message": "This website uses cookies to ensure you get the best experience on our website.",
|
||||
"dismiss": "OK",
|
||||
"link": "Learn more"
|
||||
}
|
||||
})});';
|
||||
|
||||
$this->addInlineJS($script);
|
||||
}
|
||||
````
|
||||
|
||||
This works fine. But it also has one downside: The user can not change the values for colors and content. So we have to find better way to add the script.
|
||||
|
||||
## Next: Use a Twig Template
|
||||
|
||||
In the next chapters, we will create a Twig template and use some variables, so that the user can edit the values in the user interface.
|
@ -1,42 +0,0 @@
|
||||
#Create the Basic Plugin Structure
|
||||
|
||||
The most simple plugin has only one folder and one file. Both **must** have the same name. So the basic file structure for our plugin looks like this:
|
||||
|
||||
````
|
||||
/cookieconsent // plugin-folder
|
||||
- cookieconsent.php // plugin-file
|
||||
````
|
||||
|
||||
The `cookieconsent.php` contains the main logic and central entry point of our plugin. But we need some more files. Let us describe again, what our plugin should do:
|
||||
|
||||
- The plugin should add a CSS-file to all templates, so we have to add this file.
|
||||
- The plugin should add a JavaScript-file to all templates, so we have to add this file.
|
||||
- The plugin should add the initial script with the values for the colors and the content. We will do this with a separate twig-template.
|
||||
- The content- and color-values should be editable in the setup of TYPEMILL. We will do this with a plugin configuration file.
|
||||
|
||||
If we simply follow this description, then the file structure of our cookie consent plugin looks like this:
|
||||
|
||||
```
|
||||
/cookieconsent
|
||||
- cookieconsent.php
|
||||
- cookieconsent.yaml
|
||||
/templates
|
||||
- cookieconsent.twig
|
||||
/public
|
||||
- cookieconsent.min.js
|
||||
- cookieconsent.min.css
|
||||
```
|
||||
|
||||
As you can see, a clear description of the plugin's functionality is the key. Coding is so much easier if everything is described in clear words.
|
||||
|
||||
Let us check the naming conventions again:
|
||||
|
||||
* The name of the folder is the name of your plugin.
|
||||
* The initial php-file must have the same name as the folder and must be located in the root of your plugin folder.
|
||||
* The YAML-configuration-file must have the same name as the folder and must be located in the root of your plugin folder.
|
||||
|
||||
The names and the structure of all other files and folders are a matter of taste and completely up to you.
|
||||
|
||||
## Next Step: The Configuration File
|
||||
|
||||
We have a structure now, so let's start with a basic configuration file before we write our first php-code for our plugin.
|
@ -1,61 +0,0 @@
|
||||
# Create a Twig Template
|
||||
|
||||
In the previous chapter we have added the initial JavaScript for the cookie consent directly with the helper method `addInlineJS()`. The downside is, that the values for colors and content are still hardcoded and cannot be edited by the user. So in the next step, we want to get rid of the hardcoded values and use variables instead. We will start with a separate twig-template for the initial JavaScript.
|
||||
|
||||
If you don't understand why we do that, then you are probably in good company. Well, just proceed and everything will make sense pretty soon.
|
||||
|
||||
So let us create a new folder called `template` and in that folder let us create a new twig-template called `cookieconsent.twig`. The template looks like this and yes, we still have our hardcoded values:
|
||||
|
||||
````
|
||||
window.addEventListener("load", function(){
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "#d48484",
|
||||
"text": "#ffffff"
|
||||
},
|
||||
"button": {
|
||||
"background": "#362929",
|
||||
"text": "#fff"
|
||||
}
|
||||
},
|
||||
"theme": "edgeless",
|
||||
"position": "bottom",
|
||||
"content": {
|
||||
"message": "This website uses cookies to ensure you get the best experience on our website.",
|
||||
"dismiss": "OK",
|
||||
"link": "Learn more"
|
||||
}
|
||||
})
|
||||
});
|
||||
````
|
||||
|
||||
## Use the Twig Template Engine
|
||||
|
||||
Now the interesting part happens: Instead of adding the template with the JavaScript directly into the theme, we want to render it with the Twig-template-engine first. This enables us to use variables in the template later. We can use the Twig engine in our basic PHP-file like this:
|
||||
|
||||
````
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
|
||||
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
|
||||
|
||||
/* get Twig Instance and add the cookieconsent template-folder to the path */
|
||||
$twig = $this->getTwig();
|
||||
$loader = $twig->getLoader();
|
||||
$loader->addPath(__DIR__ . '/templates');
|
||||
|
||||
/* fetch the template, render it with twig and add it as inline-javascript */
|
||||
$this->addInlineJS($twig->fetch('/cookieconsent.twig'));
|
||||
}
|
||||
````
|
||||
|
||||
With `$this->getTwig()` we get the Twig template engine. With `$twig->getLoader()` we can add a new path to your template to Twig. And in the last line, we fetch the JavaScript-template and send it to Twig with `$twig->fetch('/cookieconsent.tiwg')`. And we still use the `addInlineJS`-method to add everything to the theme, after it has been rendered with Twig.
|
||||
|
||||
Hurra, we just finished the hardest part of the tutorial!
|
||||
|
||||
## Next: Define Variables in YAML
|
||||
|
||||
This chapter was a bit complicated and you might wonder, why we did all this. The simple answer is, that Twig can render variables in the template and we can get rid of our hardcoded values now. So let us define some variables in the next chapter. And I promise, that the remaining chapters are super easy.
|
||||
|
@ -1,60 +0,0 @@
|
||||
# Add Default Values for a Plugin
|
||||
|
||||
In TYPEMILL, it is totally easy to define some variables, that you can use in your plugin later. All you have to do is to add the variables in the YAML-configuration file of your plugin.
|
||||
|
||||
In our cookie consent plugin, we want to use variables for the colors and for the content. So simply add them in a new block called "settings" like this:
|
||||
|
||||
````
|
||||
name: Cookie Consent
|
||||
version: 1.0.0
|
||||
description: Enables a cookie consent for websites
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://cookieconsent.insites.com/
|
||||
licence: MIT
|
||||
|
||||
settings:
|
||||
popup_background: '#70c1b3'
|
||||
popup_text: '#ffffff'
|
||||
button_background: '#66b0a3'
|
||||
button_text: '#ffffff'
|
||||
theme: 'edgeless'
|
||||
position: 'bottom'
|
||||
message: 'This website uses cookies to ensure you get the best experience on our website.'
|
||||
link: 'Learn More'
|
||||
dismiss: 'Got It'
|
||||
````
|
||||
|
||||
Always start with the name `settings`. After that, simply add the variables and indent each line with two spaces. Once again: Do not use the tab or anything else, because it will break the file. Just indent with two spaces.
|
||||
|
||||
As a recommendation: Use `my_name` instead of `my-name` or `my.name`, because key-names with dashes and other special characters are a bit harder to handle in the Twig-templates later.
|
||||
|
||||
## Use the Variables
|
||||
|
||||
Great news: Once you have added these default settings to your plugin configuration file, you can access the settings in your whole application. This is because TYPEMILL does a pretty smart job and merges all settings into the central settings object. All settings means:
|
||||
|
||||
* The basic setting from TYPEMILL.
|
||||
* All default settings from all plugins.
|
||||
* All individual user settings.
|
||||
|
||||
So you want to use some of theses settings in your plugin? No problem, we already did that with the `onSettingsLoaded`-event:
|
||||
|
||||
````
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
$this->settings = $settings->getData();
|
||||
}
|
||||
````
|
||||
|
||||
The plugin-settings are now included in the `$this->settings`-object.
|
||||
|
||||
So you want to use these settings in a template? No problem, just use them like this:
|
||||
|
||||
````
|
||||
{{ settings.plugins.cookieconsent.popup_background }}
|
||||
````
|
||||
|
||||
Yes. It is really that easy.
|
||||
|
||||
##Next: Use the Variables in Twig
|
||||
|
||||
There is only one missing link: We have to pass the variables from the settings into the template engine of Twig to use them in our Twig-template. But good news again: This is done with a single line of code. So let us bring everything together and (nearly) finish the cookie consent plugin in the next chapter.
|
@ -1,127 +0,0 @@
|
||||
# Use Variables in Twig
|
||||
|
||||
We nearly finished our cookie consent plugin. And in this chapter we want bring all parts of the puzzle together.
|
||||
|
||||
## The YAML-Configuration
|
||||
|
||||
Let us start with our YAML configuration file for the cookie consent plugin. Right now we have the basic plugin informations and we have defined some variables with default values. It looks like this now:
|
||||
|
||||
````
|
||||
name: Cookie Consent
|
||||
version: 1.0.0
|
||||
description: Enables a cookie consent for websites
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://cookieconsent.insites.com/
|
||||
licence: MIT
|
||||
|
||||
settings:
|
||||
popup_background: '#70c1b3'
|
||||
popup_text: '#ffffff'
|
||||
button_background: '#66b0a3'
|
||||
button_text: '#ffffff'
|
||||
theme: 'edgeless'
|
||||
position: 'bottom'
|
||||
message: 'This website uses cookies to ensure you get the best experience on our website.'
|
||||
link: 'Learn More'
|
||||
dismiss: 'Got It'
|
||||
````
|
||||
|
||||
## The PHP-File
|
||||
|
||||
We have our basic PHP-file for our plugin with the IFTTT-logic. And we have subscribed to some events:
|
||||
|
||||
* The `onSettingsLoaded`-event: We use this event to get all the settings and store them in a variable. And the great thing is, that our default-settings from our cookie consent configuration file are included, so we have access to them, now.
|
||||
* The `onTwigLoaded`-event: We used this to add our JavaScript- and CSS-files. And we loaded the Twig-rendering engine there. We used the Twig rendering engine to fetch and render our little Twig-template with the little script to initialize the cookie banner in all pages.
|
||||
|
||||
The last thing we will do now, is to pass all settings into the template engine of Twig. And we can simply pass the settings as an argument like this:
|
||||
|
||||
````
|
||||
$this->addInlineJS($twig->fetch('/cookieconsent.twig', $this->settings));
|
||||
````
|
||||
|
||||
Totally easy. So our final PHP-file looks like this now:
|
||||
|
||||
````
|
||||
<?php
|
||||
|
||||
namespace plugins\cookieconsent;
|
||||
|
||||
use \typemill\plugin;
|
||||
|
||||
class cookieconsent extends plugin
|
||||
{
|
||||
protected $settings;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onSettingsLoaded',
|
||||
'onTwigLoaded' => 'onTwigLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
$this->settings = $settings->getData();
|
||||
}
|
||||
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
|
||||
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
|
||||
|
||||
/* get Twig Instance and add the cookieconsent template-folder to the path */
|
||||
$twig = $this->getTwig();
|
||||
$loader = $twig->getLoader();
|
||||
$loader->addPath(__DIR__ . '/templates');
|
||||
|
||||
/* fetch the template, render it with twig and add it as inline-javascript */
|
||||
$this->addInlineJS($twig->fetch('/cookieconsent.twig', $this->settings));
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
Voila. And now, we can finally use our variables in the Twig-template.
|
||||
|
||||
## The Twig Template
|
||||
|
||||
We have seen, that you can use all variables in Twig like this:
|
||||
|
||||
````
|
||||
{{ settings.plugins.cookieconsent.popup_background }}
|
||||
````
|
||||
|
||||
We are simply walking through our settings-array starting with the settings, then go to the plugins, then to the cookieconsent plugin and finally to the key `popup_background`.
|
||||
|
||||
Let us add all variables to our Twig-template now:
|
||||
|
||||
```
|
||||
window.addEventListener("load", function(){
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "{{ settings.plugins.cookieconsent.popup_background }}",
|
||||
"text": "{{ settings.plugins.cookieconsent.popup_text }}"
|
||||
},
|
||||
"button": {
|
||||
"background": "{{ settings.plugins.cookieconsent.button_background }}",
|
||||
"text": "{{ settings.plugins.cookieconsent.button_text }}"
|
||||
}
|
||||
},
|
||||
"theme": "{{ settings.plugins.cookieconsent.theme }}",
|
||||
"position": "{{ settings.plugins.cookieconsent.position }}",
|
||||
"content": {
|
||||
"message": "{{ settings.plugins.cookieconsent.message }}",
|
||||
"dismiss": "{{ settings.plugins.cookieconsent.dismiss }}",
|
||||
"link": "{{ settings.plugins.cookieconsent.link }}"
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
It was a little bit complicated to get to this point, but now everything should make sense. And let's be honest: It is way easier than in many other super complex systems and it is flexible as hell. If you have done this one or two times, then you can create a plugin like this in one or two hours.
|
||||
|
||||
## Final Step: Make Variables Editable
|
||||
|
||||
There is still missing one part and you might think that this part is the hardest to master? Yes, you are right, we still have to create a user interface so that the user can change the value in the setup of TYPEMILL. But no, this is not the hardest part. It is the easiest one. We will do it in the next chapter.
|
@ -1,161 +0,0 @@
|
||||
# Make Variables Editable
|
||||
|
||||
In the final step, we want to make the variables for our cookie consent plugin editable, so that the user can choose his own colors and add his own content in the setup of TYPEMILL. In other words, we want to create a user interface. This might sound complicated. But it is totally easy. You don't have to write a single line of code. You just have to define the input fields in the configuration file of your plugin. So let us do this quickly.
|
||||
|
||||
## Define Input Fields in YAML
|
||||
|
||||
Let us remember, that the YAML-configuration file of a plugin can have up to three parts:
|
||||
|
||||
* The first part defines the basic informations of a plugin like the version, the name or the author.
|
||||
* The second part defines the default ``settings`` for the plugin.
|
||||
* The last part defines the `forms` and `fields` for the user interface, so the user can edit the settings.
|
||||
|
||||
The field-definitions always start with `forms` and `fields` in the YAML-syntax:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
````
|
||||
|
||||
You can add as many fields as you need for your plugin. The rule is: The name of the field must be the name of your default setting. So if you have a default setting for "position" like this...
|
||||
|
||||
````
|
||||
settings:
|
||||
...
|
||||
...
|
||||
position: 'bottom'
|
||||
````
|
||||
|
||||
... then you define an input field with the same name like this:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
position:
|
||||
...
|
||||
...
|
||||
...
|
||||
````
|
||||
|
||||
The field definition is pretty simple. If you know how to create fields in HTML, then you also know, how to define them in YAML.
|
||||
|
||||
Let's have a look at the `position`-field. It is a select-box with four values. I am pretty sure that you already guess how it is defined in YAML:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
position:
|
||||
type: select
|
||||
label: Position of Cookie Banner
|
||||
options:
|
||||
bottom: Bottom
|
||||
top: Top
|
||||
bottom-left: Bottom left
|
||||
bottom-right: Bottom right
|
||||
````
|
||||
|
||||
It is basically the same as writing HTML. You simply add the type, the label and the options.
|
||||
|
||||
## Fields in the User Interface
|
||||
|
||||
TYPEMILL will read all your field definitions and automatically create a user input field in the setup. So our select-box for the position looks like this now in the user interface of the TYPEMILL setup:
|
||||
|
||||
![Typemill plugins in the setup]()
|
||||
|
||||
TYPEMILL will also insert the default value into the field and, of course, store the user input in the settings.
|
||||
|
||||
## The Complete YAML-file
|
||||
|
||||
Let us check the final YAML-file for the cookie consent plugin with the basic informations, with the default settings and with the field definitions for the user interface:
|
||||
|
||||
````
|
||||
name: Cookie Consent
|
||||
version: 1.0.0
|
||||
description: Enables a cookie consent for websites
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://cookieconsent.insites.com/
|
||||
licence: MIT
|
||||
|
||||
settings:
|
||||
popup_background: '#70c1b3'
|
||||
popup_text: '#ffffff'
|
||||
button_background: '#66b0a3'
|
||||
button_text: '#ffffff'
|
||||
theme: 'edgeless'
|
||||
position: 'bottom'
|
||||
message: 'This website uses cookies to ensure you get the best experience on our website.'
|
||||
link: 'Learn More'
|
||||
dismiss: 'Got It'
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
theme:
|
||||
type: select
|
||||
label: Theme
|
||||
placeholder: 'Add name of theme'
|
||||
required: true
|
||||
options:
|
||||
edgeless: Edgeless
|
||||
block: Block
|
||||
classic: Classic
|
||||
|
||||
position:
|
||||
type: select
|
||||
label: Position of Cookie Banner
|
||||
options:
|
||||
bottom: Bottom
|
||||
top: Top
|
||||
bottom-left: Bottom left
|
||||
bottom-right: Bottom right
|
||||
|
||||
message:
|
||||
type: textarea
|
||||
label: Message
|
||||
placeholder: 'Message for cookie-popup'
|
||||
required: true
|
||||
|
||||
link:
|
||||
type: text
|
||||
label: Label for Link
|
||||
placeholder: 'Link-Lable like More infos'
|
||||
required: true
|
||||
|
||||
dismiss:
|
||||
type: text
|
||||
label: Label for Button
|
||||
placeholder: 'Got it'
|
||||
required: true
|
||||
|
||||
popup_background:
|
||||
type: color
|
||||
label: Background Color of Popup
|
||||
placeholder: 'Add hex color value like #ffffff'
|
||||
required: true
|
||||
|
||||
popup_text:
|
||||
type: color
|
||||
label: Text Color of Popup
|
||||
placeholder: 'Add hex color value like #ffffff'
|
||||
required: true
|
||||
|
||||
button_background:
|
||||
type: color
|
||||
label: Background Color of Button
|
||||
placeholder: 'Add hex color value like #ffffff'
|
||||
required: true
|
||||
|
||||
button_text:
|
||||
type: color
|
||||
label: Text Color of Button
|
||||
placeholder: 'Add hex color value like #ffffff'
|
||||
required: true
|
||||
````
|
||||
|
||||
If you want to learn more about the field definitions, then read the [documentation about field definitions](/for-plugin-developers/documentation/field-overview). There you can find all the possibilities that you have.
|
||||
|
||||
What else to say? Well, our plugin is ready now.
|
||||
|
||||
Hurra!!!!
|
@ -1,3 +0,0 @@
|
||||
# Plugin Tutorial
|
||||
|
||||
In this short tutorial we will create the **cookie consent plugin** step by step and learn about the **file structure**, about the **configuration with YAML** and about the basic **PHP-file** for the plugin. You will also learn how to register **events**, how to use your own **templates** and how to create a **user interface** for your plugin. Everything is pretty simple and the tutorial can be finished in one hour or two.
|
@ -1,49 +0,0 @@
|
||||
# The Structure of a Plugin
|
||||
|
||||
Each plugin needs a file structure. The rules for this file structure are easy to learn important to follow. Otherwise, the plugin won't work.
|
||||
|
||||
## Basic Folder And File
|
||||
|
||||
A simple plugin works with one folder and one file. There are the following naming conventions:
|
||||
|
||||
* The name of the folder is the name of the plugin, and
|
||||
* the name of the core-php-file is name of the plugin.
|
||||
|
||||
So it might look like this:
|
||||
|
||||
````
|
||||
/myPlugin // folder
|
||||
- myPlugin.php // file with plugin-code
|
||||
````
|
||||
|
||||
## Configuration File
|
||||
|
||||
However, if you want to distribute a plugin on the TYPEMILL website (not ready yet), then you **must** add a **configuration file** with YAML syntax, and this file must have the **same name** as the plugin again. This is very important, because the versioning and the update notifications, that are shown in the setup, only work with this configuration file.
|
||||
|
||||
So the minimum requirement for a distributed plugin looks like this:
|
||||
|
||||
````
|
||||
/myPlugin // folder
|
||||
- myPlugin.php // file with plugin-code
|
||||
- myPlugin.yaml // file with yaml-configuration
|
||||
````
|
||||
|
||||
## Other files
|
||||
|
||||
You are free to add any other files and folders into your plugin. A plugin can have assets like images, CSS or JavaScript. It can also have templates, controllers and middleware. The folder structure of a more complex plugin might look like this:
|
||||
|
||||
````
|
||||
/myPlugin
|
||||
- myPlugin.php
|
||||
- myPlugin.yaml
|
||||
/controllers
|
||||
- myController.php
|
||||
/middleware
|
||||
- myMiddleware.php
|
||||
/templates
|
||||
- mytemplate.twig
|
||||
/public
|
||||
- mystyle.css
|
||||
- myscript.js
|
||||
- myimage.jpg
|
||||
````
|
@ -1,67 +0,0 @@
|
||||
# YAML Configuration File
|
||||
|
||||
Each plugin should have a configuration file written in YAML. YAML is a widespread and easy to understand configuration language that can be transformed into an array easily. Like an array, YAML uses simple key-value pairs. Deeper levels for multidimensional arrays are indicated with indentation. Indentation is made by two spaces. Do not use tabs for indentation, because it will break the transformation.
|
||||
|
||||
## Three Parts of the Configuration File
|
||||
|
||||
The YAML-configuration file for TYPEMILL can have up to three parts:
|
||||
|
||||
- The **first part** is mandatory and defines the basic meta-informations about a plugin like the name, the version and the author.
|
||||
- The **second part** is optional and defines the default values of the plugin. It must start with `settings:`.
|
||||
- The **third part** is optional and defines the input fields for the user interface. It must start with `forms:` and `fields:`.
|
||||
|
||||
## The Meta-Informations
|
||||
|
||||
In the first part, the following meta-informations are used by TYPEMILL and displayed in the TYPEMILL setup:
|
||||
|
||||
````
|
||||
name: The name of the plugin
|
||||
version: 1.0.0
|
||||
description: A short description.
|
||||
author: The name of the plugin author.
|
||||
homepage: a link to a website with informations about the plugin
|
||||
licence: Licence like MIT or others
|
||||
````
|
||||
|
||||
The version number is used by TYPEMILL to check for updates and to inform the user. Please use a valid schema for versioning, we recommend a simple system like `1.2.3 ` where position `1` is a major release, `2` is minor or feature release and `3` is a bugfix (as a rule of thumb).
|
||||
|
||||
## The Default Settings
|
||||
|
||||
The second part defines the default settings and always starts with `settings:` followed by simple key-value-pairs indented with two spaces:
|
||||
|
||||
````
|
||||
settings:
|
||||
key1: value1
|
||||
Key2: value2
|
||||
key3: value3
|
||||
````
|
||||
|
||||
The default settings are merged into the main settings of TYPEMILL and are available across the whole platform for plugins and themes. The default settings are overwritten by the individual settings of the user, if present.
|
||||
|
||||
## The Field Definitions
|
||||
|
||||
The third part defines the user interface for individual settings and always starts with `forms:` and `fields:`. If users should overwrite the default settings with individual settings in the interface, then the name of the defined field must be the same as the name of the default setting, that should be overwritten. For example:
|
||||
|
||||
````
|
||||
settings:
|
||||
position: top
|
||||
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
position:
|
||||
type: select
|
||||
label: Position of Element
|
||||
bottom: Bottom
|
||||
top: Top
|
||||
````
|
||||
|
||||
This way, the user input for the field `position` will overwrite the default value for `position`.
|
||||
|
||||
This is the rule if you want to bind default settings and user inputs. But you don't have to do this:
|
||||
|
||||
* You can define a value with `settings:` and skip the `fields:` if you don't want, that a user can change the default value.
|
||||
* You can define a `field:` and skip the `settings: ` if you don't need a default value for an input field.
|
||||
|
||||
All possibilities for fields are listed in the chapter about field definitions.
|
@ -1,237 +0,0 @@
|
||||
# Field Overview
|
||||
|
||||
Field-definitions in YAML are a simple and straight forward way to define fields for user input in the frontend. You can define a wide range of input fields in the YAML-configuration file with a simple list of field-characteristics. TYPEMILL will read all your field definitions and create a user interface on the fly.
|
||||
|
||||
Field definitions are part of the plugin YAML-configuration file and they always start with the keywords `forms` and `fields` followed by a list of fields:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
myfieldname:
|
||||
type: text
|
||||
label: My Field
|
||||
|
||||
anotherfield:
|
||||
type: text
|
||||
label: Another Field
|
||||
````
|
||||
|
||||
Fields can have a lot of characteristics like:
|
||||
|
||||
* The **field type** like text, select or textarea.
|
||||
* **Boolean field attributes** like required, checked or disabled.
|
||||
* **Field attributes with values** like placeholder, id or pattern.
|
||||
* The **field label**, which is standard for a field.
|
||||
* A **field help** text, which is an optional explanation text for a field.
|
||||
|
||||
The field input of a user is also **validated** in the backend automatically. The backend validation is pretty tight right now, so that you should always test the input fields intensively. If you run into unwanted validation errors, please report it in github.
|
||||
|
||||
## Field Types
|
||||
|
||||
Note, that field types in TYPEMILL are not equivalent to HTML field types in every case, because TYPEMILL has its own field types like a checkboxlist and textarea, which is not a HTML field type but a HTML tag. But everything will be transformed into valid HTML, of course.
|
||||
|
||||
TYPEMILL accepts the following field type definitions:
|
||||
|
||||
* checkbox
|
||||
* checkboxlist
|
||||
* color
|
||||
* date
|
||||
* email
|
||||
* hidden
|
||||
* number
|
||||
* password
|
||||
* radio
|
||||
* select
|
||||
* text
|
||||
* textarea
|
||||
* tel
|
||||
* url
|
||||
* fieldset
|
||||
|
||||
A simple field definition looks like this:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
myfieldname:
|
||||
type: text
|
||||
label: My Label
|
||||
placeholder: please insert text.
|
||||
````
|
||||
|
||||
## Label and Help
|
||||
|
||||
TYPEMILL supports labels and a help-text in the field definition:
|
||||
|
||||
- **Label**: With the label for the field. You should always use a label.
|
||||
- **Help**: This is a help-text for your field. The help-text is signaled with a question-mark at the right side of the field and the content is displayed in a box that opens on hover.
|
||||
|
||||
This is an example:
|
||||
|
||||
```
|
||||
website:
|
||||
type: url
|
||||
label: Add valid url
|
||||
help: Add a valid url with a protocoll like 'https://' or 'http://'.
|
||||
```
|
||||
|
||||
## Attributes
|
||||
|
||||
You can add attributes to a field definition. TYPEMILL supports these boolean attributes (with value: true):
|
||||
|
||||
- auto: true
|
||||
- checked: true
|
||||
- disabled: true
|
||||
- formnovalidate: true
|
||||
- multiple: true
|
||||
- readonly: true
|
||||
- required: true
|
||||
|
||||
TYPEMILL also supports the following attributes with values:
|
||||
|
||||
- id: 'myId'
|
||||
- placeholder: 'my placeholder-text here'
|
||||
- size: 5
|
||||
- rows: 5
|
||||
- cols: 5
|
||||
- class: 'myClass'
|
||||
- pattern: '[0-9]{4}'
|
||||
|
||||
So a field definition can become pretty comprehensive like this:
|
||||
|
||||
```
|
||||
year:
|
||||
type: text
|
||||
label: Add a year
|
||||
placeholder: '2018'
|
||||
required: true
|
||||
pattern: '[0-9]{4}'
|
||||
class: 'fc-year'
|
||||
id: 'app-year'
|
||||
```
|
||||
|
||||
The `pattern` attribute accepts every valid regex for an input validation in the frontend. Please note, that there is also a backend validation that might conflict with your frontend validation. So please double check your validation pattern and test the input intensively.
|
||||
|
||||
## Fields With Options
|
||||
|
||||
TYPEMILL supports three field types with options:
|
||||
|
||||
* Select
|
||||
* Radio
|
||||
* Checkboxlist
|
||||
|
||||
The standard field type with options is a `select` field. You can add any options with the keyword `options` followed by a list of value-label-pairs like this:
|
||||
|
||||
```
|
||||
theme:
|
||||
type: select
|
||||
label: Select A Theme
|
||||
options:
|
||||
edgeless: Edgeless Theme
|
||||
block: Block Theme
|
||||
classic: Classic Theme
|
||||
```
|
||||
|
||||
The value on the left side (e.g. `edgeless`) is the value of the option, that is transported to the server. The label on the right side (e.g. `Edgeless Theme`) is the label of the option, that is visible for the user in the select-box.
|
||||
|
||||
To make your live a bit easier, you can also define options for `radio` field-types and for a special field type called `checkboxlist`. A list of radio buttons can be defined like this:
|
||||
|
||||
```
|
||||
Radio:
|
||||
type: radio
|
||||
label: Select an Option
|
||||
options:
|
||||
red: Red
|
||||
green: Green
|
||||
blue: Blue
|
||||
yellow: Yellow
|
||||
```
|
||||
|
||||
And a list of checkboxes can be defined like this:
|
||||
|
||||
````
|
||||
Checkbox:
|
||||
type: checkboxlist
|
||||
label: Multiple Checkboxes
|
||||
options:
|
||||
first: First
|
||||
second: Second
|
||||
third: Third
|
||||
fourth: Fourth
|
||||
````
|
||||
|
||||
The downside of this kind of list-definitions is, that you cannot add other attributes like 'checked' or 'required' for now. But we will make it more flexible in future.
|
||||
|
||||
So for now, if you need a checkbox or a radio button with further attributes, then you should define it in a traditional way like this:
|
||||
|
||||
````
|
||||
SimpleCheckbox:
|
||||
type: checkbox
|
||||
label: Simple checkbox
|
||||
required: true
|
||||
checked: true
|
||||
description: Please check me
|
||||
````
|
||||
|
||||
## Using Fieldsets
|
||||
|
||||
If you have a lot of fields, you can group them togeter with a fieldset like this:
|
||||
|
||||
````
|
||||
forms:
|
||||
fields:
|
||||
|
||||
chapter:
|
||||
type: text
|
||||
label: chapter
|
||||
placeholder: Add Name for Chapter
|
||||
required: true
|
||||
|
||||
MyFirstfieldset:
|
||||
type: fieldset
|
||||
legend: Last Modified
|
||||
fields:
|
||||
|
||||
modified:
|
||||
type: checkbox
|
||||
label: Activate Last Modified
|
||||
description: Show last modified date at the end of each page?
|
||||
|
||||
modifiedText:
|
||||
type: text
|
||||
label: Last Modified Text
|
||||
placeholder: Last Updated
|
||||
````
|
||||
|
||||
The fields `modified` and `modifiedText` will then be grouped in a fieldset with the legend `Last Modified`.
|
||||
|
||||
## Example for a complete yaml configuration
|
||||
|
||||
To sum it up, this is a complete example of a yaml configuration file for a plugin with the meta-description, a default value and a field definition for user input:
|
||||
|
||||
````
|
||||
name: Example Plugin
|
||||
version: 1.0.0
|
||||
description: Add a short description
|
||||
author: Firstname Lastname
|
||||
homepage: http://your-website.net
|
||||
licence: MIT
|
||||
|
||||
settings:
|
||||
theme: 'edgeless'
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
theme:
|
||||
type: select
|
||||
label: Select a Theme
|
||||
required: true
|
||||
options:
|
||||
edgeless: Edgeless
|
||||
block: Block
|
||||
classic: Classic
|
||||
````
|
||||
|
@ -1,116 +0,0 @@
|
||||
# Basic PHP File
|
||||
|
||||
The PHP file contains the business logic of your plugin. The name of the php-file must be the same as the name of the plugin folder. For example:
|
||||
|
||||
````
|
||||
/myplugin
|
||||
- myplugin.php
|
||||
````
|
||||
|
||||
The plugin system of TYPEMILL is object oriented and follows the rules of event driven development. The system uses the event dispatcher of synfony, so if you have ever used the event dispatcher, then you are familiar with the system.
|
||||
|
||||
There are the following rules for the class of your plugin:
|
||||
|
||||
* **Namespace**: The namespace must start with `plugins\` followed by the name of the plugin.
|
||||
* **Classname**: The name of the class must be the name of the plugin.
|
||||
* **Extend** The class must extend the plugin base class `\typemill\plugins`.
|
||||
|
||||
The class can contain up to four parts:
|
||||
|
||||
* **Event Subscribers**: Every plugin must have a public static method called ``getSubscribedEvents()`. It returns an array with events as key and plugin-methods as values.
|
||||
* **Methods**: The business logic is written in methods. They get called, when an subscribed event happens.
|
||||
* **Routes**: You can add new routes (urls) to TYPEMILL with your plugin. Routes are optional.
|
||||
* **Middleware**: You can add new middleware to TYPEMILL with your plugin. Middleware is optional.
|
||||
|
||||
## Example of a Plugin Class
|
||||
|
||||
A minimum plugin class at least subscribes to one event and contains one subscriber method. So a minmum plugin class looks like this:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace plugins\myplugin;
|
||||
|
||||
use \typemill\plugin;
|
||||
|
||||
class myplugin extends plugin
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onSettingsLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
// do something with the $settings
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## getSubscribedEvents
|
||||
|
||||
The public static function `getSubscribedEvents()` returns an array with the name of the event as key and the name of the plugin method as value.
|
||||
|
||||
````
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array('eventName' => 'methodName');
|
||||
}
|
||||
````
|
||||
|
||||
You can listen to several events in your plugin class:
|
||||
|
||||
````
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'firstEvent' => 'firstMethod',
|
||||
'secondEvent' => 'secondMethod'
|
||||
)
|
||||
}
|
||||
````
|
||||
|
||||
You can also add several methods to a single event and give this method priorities:
|
||||
|
||||
````
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'firstEvent' => array(
|
||||
array('firstMethod', 10),
|
||||
array('anotherMethod, 1)
|
||||
),
|
||||
'secondEvent' => 'secondMethod'
|
||||
)
|
||||
}
|
||||
````
|
||||
|
||||
The rule for the order is pretty simple: The higher the order, the earlier the call. You can also use negative numbers like `-10` to give a method call a really low priority.
|
||||
|
||||
## Methods
|
||||
|
||||
You can name your methods as you want. Many people give their methods the same name as the events. This way you can easily see which method is called by which event. But it is a matter of taste:
|
||||
|
||||
````
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onSettingsLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
// do something with the $settings
|
||||
}
|
||||
|
||||
````
|
||||
|
||||
Within your methods you can write your business logic. What you should know:
|
||||
|
||||
* **Arguments**: All events pass some arguments into your method and in many cases they also pass data that you can use or manipulate (like the $settings in the example above). You can find all the details in the [event overview](/for-plugin-developers/documentation/event-overview).
|
||||
* **Helper-Methods**: The TYPEMILL plugin-class that you extend with your own class provides some useful helper methods. You can read all about these methods in the [methods overview](/for-plugin-developers/documentation/method-overview).
|
||||
|
||||
If you want to add your own routes or your own middleware, please read the chapters about them in this documentation.
|
@ -1,68 +0,0 @@
|
||||
# The TYPEMILL Events
|
||||
|
||||
When a user visits an url, then there are a lot of steps to generate and finally return the website. Theses steps are called the life cycle. TYPEMILL has its own lifecycle. When someone requests a page, then TYPEMILL initiates the application, loads all plugins, merges all settings, starts the template engine, scans the content folder, collects the content and finally renders the page.
|
||||
|
||||
When TYPEMILL goes through this life cycle, it constantly fires events and often sends some data with these events. Developers can listen to these events, hook into the system and add own functionalities.
|
||||
|
||||
This is a list of all events that TYPEMILL fires during the life cycle. The order of the events follow the life cycle. In the last column you can find the data, that each event passes to your subscriber method.
|
||||
|
||||
| Event Name | Description | Data |
|
||||
| -------------------- | ---------------------------------------- | ------------------------------------ |
|
||||
| onPluginsLoaded | TYPEMILL has loaded all plugins. | No data |
|
||||
| onSettingsLoaded | TYPEMILL has loaded and merged all settings. This includes the basic app settings, all plugin settings and the individual user settings. | Settings (a slim-object) |
|
||||
| onTwigLoaded | TYPEMILL has loaded the template engine Twig. | No data |
|
||||
| onPagetreeLoaded | TYPEMILL has scanned the content folder and has generated the content structure. | Content structure (array of objects) |
|
||||
| onBreadcrumbLoaded | TYPEMILL has created a breadcrumb for the page. | Breadcrumb (array) |
|
||||
| onItemLoaded | TYPEMILL has created the page item. | Item (object) |
|
||||
| onMarkdownLoaded | TYPEMILL has loaded the page content. | Page content (markdown syntax) |
|
||||
| onContentArrayLoaded | TYPEMILL has transformed the page content into an array. | Page content (array) |
|
||||
| onHtmlLoaded | TYPEMILL has transformed the markdown content to HTML (with the Parsedown library). | Page content (html syntax) |
|
||||
| onPageReady | TYPEMILL has collected all data for the page and will send it to the template in the next step. | All page data (array) |
|
||||
|
||||
If TYPEMILL passes data to your subscriber method, then you can get the data, use the data, manipulate the data and return the data to the app. You can do this with two simple methods: `getData()` and `setData()`.
|
||||
|
||||
Let's take the html-event as an example:
|
||||
|
||||
````
|
||||
|
||||
class MyPlugin extends \Typemill\Plugin
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onHtmlLoaded' => 'onHtmlLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
public function onHtmlParsed($html)
|
||||
{
|
||||
$data = $html->getData();
|
||||
|
||||
$data .= '<p>This is a paragraph that I added at the end of the page content</p>';
|
||||
|
||||
$html->setData($data);
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
TYPEMILL uses the symfony event dispatcher for the event system. The event dispatcher adds two other variables to each event by default:
|
||||
|
||||
* The second parameter is the **name of the event**.
|
||||
* The thirds parameter is the **event dispatcher** itself.
|
||||
|
||||
So in each of your event methods in your plugin, you can also read the event name and you have access to the dispatcher-object itself:
|
||||
|
||||
````
|
||||
public function onHtmlParsed($html, $eventName, $dispatcher)
|
||||
{
|
||||
// read the $eventName
|
||||
// use the $dispatcher
|
||||
}
|
||||
````
|
||||
|
||||
There are not many use cases to access the event-name or the dispatcher in this way. Theoretically you could fire your own events with the dispatcher object. But it is much better to access the dispatcher object within the dependency container of slim.
|
||||
|
||||
The dependency container is one of the properties and methods provided by the basic plugin class of TYPEMILL. And because all plugins extend this basic plugin-class, all plugins have access to these usefull properties and methods.
|
||||
|
||||
We will learn in the next chapter about it.
|
||||
|
@ -1,183 +0,0 @@
|
||||
# Plugin Method Overview
|
||||
|
||||
Each plugin extends the base class `Plugin` of TYPEMILL. This base class provides some build in methods that are useful for plugin developers. You can access all these helper methods with the keyword `$this`. The `$this` keyword simply references the object itself. For example:
|
||||
|
||||
````
|
||||
public function myPluginMethod()
|
||||
{
|
||||
$path = $this->getPath();
|
||||
|
||||
if($path == 'admin')
|
||||
{
|
||||
// do something
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
Here is a list of all helper methods, that the `Plugin`-class provides:
|
||||
|
||||
## addJS
|
||||
|
||||
With the function addJS, you can add an external or internal JavaScript ressource.
|
||||
|
||||
If you want to add an external JavaScript file, then simply add the full url like this:
|
||||
|
||||
````
|
||||
$this->addJS('https://some-url.com/with/path/to/javascript.js');
|
||||
````
|
||||
|
||||
If you want to add a local JavaScript file, that lives in your plugin folder, then simply add a relative url like this:
|
||||
|
||||
````
|
||||
$this->addJS('/yourpluginfolder/subfolder/javascript.js');
|
||||
````
|
||||
|
||||
## addCSS
|
||||
|
||||
The addCSS function works exactly in the same way like the addJS-method. You can add an external or internal ressource:
|
||||
|
||||
````
|
||||
$this->addCSS('https://some-url.com/with/path/to/style.css');
|
||||
$this->addCSS('/yourpluginfolder/subfolder/style.css');
|
||||
````
|
||||
|
||||
## addInlineJS
|
||||
|
||||
With this function you can add any kind of inline JavaScript to all templates.
|
||||
|
||||
````
|
||||
$this->addInlineJS('alert("hello");');
|
||||
````
|
||||
|
||||
Add the plain JavaScript without the `<script>` tag, because TYPEMILL will add it for you.
|
||||
|
||||
## addInlineCSS
|
||||
|
||||
With this function you can add any kind of inline-CSS to all templates.
|
||||
|
||||
````
|
||||
$this->addInlineCSS('body{ background: #000; }');
|
||||
````
|
||||
|
||||
All this functions only work, if the template has implemented the twig-functions to render the styles and scripts:
|
||||
|
||||
````
|
||||
{{ renderCSS() }} // this tag should be placed in the html-header
|
||||
{{ renderJS() }} // this tag should be placed before the closing body-tag
|
||||
````
|
||||
|
||||
Check the chapter about [theme design](/for-theme-developers) for more informations.
|
||||
|
||||
## getPath
|
||||
|
||||
With this function, you can get the actual path. It returns a simple string.
|
||||
|
||||
````
|
||||
$this->getPath(); // returns something like 'imprint' or '/home/imprint'
|
||||
````
|
||||
|
||||
This function can be handy, if your plugin should only work for certain path on a website.
|
||||
|
||||
````
|
||||
if($this->getPath() == 'imprint')
|
||||
{
|
||||
// do something in your plugin.
|
||||
}
|
||||
````
|
||||
|
||||
## getRoute
|
||||
|
||||
This function is a bit more flexible thant the getPath()-function, because it returns the slim uri-object:
|
||||
|
||||
````
|
||||
$this->getRoute();
|
||||
````
|
||||
|
||||
You hav access to all slim-methods for this object, which are:
|
||||
|
||||
- `getScheme()`
|
||||
- `getAuthority()`
|
||||
- `getUserInfo()`
|
||||
- `getHost()`
|
||||
- `getPort()`
|
||||
- `getPath()`
|
||||
- `getBasePath()`
|
||||
- `getQuery()` (returns the full query string, e.g. `a=1&b=2`)
|
||||
- `getFragment()`
|
||||
- `getBaseUrl()`
|
||||
|
||||
Please refer to the [slim-docuemtation](https://www.slimframework.com/docs/objects/request.html#the-request-method) for more informations.
|
||||
|
||||
## getDispatcher
|
||||
|
||||
With this helper-function, you can get the the synfony event dispatcher.
|
||||
|
||||
````
|
||||
$dispatcher = $this->getDispatcher();
|
||||
````
|
||||
|
||||
The dispatcher is also passed into your subscriber methods as third argument as default.
|
||||
|
||||
## getTwig
|
||||
|
||||
We already worked with this little helper. It returns the twig-template-engine and you can use it, to render your own templates and add some variables. For example:
|
||||
|
||||
````
|
||||
$twig = $this->getTwig(); // get the twig-object
|
||||
$loader = $twig->getLoader(); // get the twig-template-loader
|
||||
$loader->addPath(__DIR__ . '/templates'); // add your template
|
||||
|
||||
// now render the template with some variables in it.
|
||||
$twig->fetch('/yourTemplate.twig', array('mykey' => 'myvalue'));
|
||||
````
|
||||
|
||||
Please check the Twig-documentation to learn more.
|
||||
|
||||
## addTwigGlobal
|
||||
|
||||
You can also add Twig-Globals, that you can use in the frontend.
|
||||
|
||||
````
|
||||
$this->addTwigGlobal('text', new Text());
|
||||
````
|
||||
|
||||
This will add the twig-variable 'text', that you can use in your templates this way:
|
||||
|
||||
````
|
||||
{{ text }}
|
||||
````
|
||||
|
||||
## addTwigFilter
|
||||
|
||||
You can also add twig-filters in your plugin like this:
|
||||
|
||||
````
|
||||
$this->addTwigFilter('rot13', function($string){
|
||||
return str_rot13($string);
|
||||
});
|
||||
````
|
||||
|
||||
You can use this in your template like this:
|
||||
|
||||
````
|
||||
{{ rot13('this is a text') }}
|
||||
````
|
||||
|
||||
## addTwigFunction
|
||||
|
||||
You can add a twig-function in your plugin like this:
|
||||
|
||||
````
|
||||
$this->addTwigFunction('myName', function(){
|
||||
return 'My name is ';
|
||||
});
|
||||
````
|
||||
|
||||
And again, you can use this function in your template like this:
|
||||
|
||||
````
|
||||
{{ myName() }}
|
||||
````
|
||||
|
||||
Please check the [Twig-Documentation](https://twig.symfony.com/doc/2.x/) to learn more about this.
|
||||
|
@ -1,56 +0,0 @@
|
||||
# Add New Routes
|
||||
|
||||
You can add your own routes to TYPEMILL with plugin. Simply use the public static method `addNewRoutes()` like this:
|
||||
|
||||
````
|
||||
public static function addNewRoutes()
|
||||
{
|
||||
return array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/myroute',
|
||||
'class' => 'Plugins\Myplugin\MypluginController:index'
|
||||
);
|
||||
}
|
||||
````
|
||||
|
||||
The method returns an array with three values:
|
||||
|
||||
* **httpMethod**: Values can be 'get', 'post', 'put', 'delete', 'head', 'patch' or 'options'.
|
||||
* **route**: Value can be a valid route like '/this/is/my/route'. Please refer to the [slim documentation](https://www.slimframework.com/docs/v3/objects/router.html) to find out, which routes are accepted.
|
||||
* **class**: This is the class that should be called with the route. It accepts the fully classified namespace of the class followed by a colon and the method within the class, that should be called.
|
||||
|
||||
You can also add multiple routes with a multi-dimensional array like this:
|
||||
|
||||
````
|
||||
public static function addNewRoutes()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/myroute',
|
||||
'class' => 'Plugins\Myplugin\MypluginController:index'
|
||||
),
|
||||
array(
|
||||
'httpMethod' => 'post',
|
||||
'route' => '/myroute',
|
||||
'class' => 'Plugins\Myplugin\MypluginController:save'
|
||||
)
|
||||
);
|
||||
}
|
||||
````
|
||||
|
||||
To get your new route working, you have to create a php-file in your plugin with the name `MypluginCotroller.php` and a class like that:
|
||||
|
||||
````
|
||||
<?php
|
||||
|
||||
namespace Plugins\Myplugin;
|
||||
|
||||
class MypluginController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return die('I am the new plugin controller');
|
||||
}
|
||||
}
|
||||
````
|
@ -1,50 +0,0 @@
|
||||
# Add New Middleware
|
||||
|
||||
If you are not familiar with the concept of middleware, please read the [documentation of slim](https://www.slimframework.com/docs/v3/concepts/middleware.html) first. With middleware you can add some logic that is added to the live cycle of the application. Some examples for middleware are:
|
||||
|
||||
* Authenticate a user.
|
||||
* Add a CSFR protection for input fields (already exists in TYPEMILL).
|
||||
* Validate user input (already exists in TYPEMILL).
|
||||
* Add an error handler (already exists in TYPEMILL).
|
||||
|
||||
The concept of middleware is a bit harder to understand, but to add middleware with a plugin is pretty easy with the method `addNewMiddleware()`:
|
||||
|
||||
````
|
||||
public static function addNewMiddleware()
|
||||
{
|
||||
return array(
|
||||
'classname' => 'Plugins\MyPlugin\MyMiddleware',
|
||||
'params' => false
|
||||
);
|
||||
}
|
||||
|
||||
````
|
||||
|
||||
The method returns and array again and accepts to values:
|
||||
|
||||
* **classname**: The fully qualified name of the class, that should be called.
|
||||
* **params**: False or an array.
|
||||
|
||||
You can create a new file `MyMiddleware.php` in your plugin and add a middleware logic like this:
|
||||
|
||||
````
|
||||
<?php
|
||||
|
||||
namespace Plugins\MyPlugin;
|
||||
|
||||
class MyMiddleware
|
||||
{
|
||||
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, $next)
|
||||
{
|
||||
// do something here
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
The support of middleware in TYPEMILL is pretty basic right now and it has some limitations. The most important limitation:
|
||||
|
||||
* Right now you can only add global middleware. You cannot add middleware only to specific routes.
|
||||
* The order, when the middleware is executed, is fixed and you cannot manipulate it. This means, that all the plugin middleware is executed after the TYPEMILL middleware. And the order depends on when your plugin get's loaded.
|
||||
|
||||
The middleware-support in TYPEMILL will be improved in future. For now, only use it if you really know what you wanna do. You can also add a new issue in github, if you miss anything.
|
@ -1,3 +0,0 @@
|
||||
# Documentation
|
||||
|
||||
If you are already familiar with plugin development or if you want to refresh your knowledge, than check the documentation. Find a list of **events** fired by TYPEMILL, learn about useful **helper methods**, check the comprehensive list of **field definitions** or learn how to add your own **routes** and your own **middleware**.
|
@ -1,3 +0,0 @@
|
||||
# Hello Plugin-Developers!
|
||||
|
||||
You can extend TYPEMILL with your own **plugins**. Simply use the **event-system** based on the symfony event dispatcher and configure an user-interface with a super simple **YAML-file**.
|
@ -1,294 +0,0 @@
|
||||
# Release Notes
|
||||
|
||||
This is the version history with some release notes.
|
||||
|
||||
## Version 1.2.5: Create New Pages Online
|
||||
|
||||
_Release date: 07.10.2018_
|
||||
|
||||
Version 1.2.5 is a simple system update, so you only have to delete the system folder and upload the new system folder of version 1.2.5. The main changes are:
|
||||
|
||||
* Create new folders and files.
|
||||
* Mark drafted and unpublished pages in navigation.
|
||||
* Add internal 404 page for the author panel.
|
||||
* Improve delete pages (redirect and subfolder).
|
||||
|
||||
## Version 1.2.4: Refactoring and Browser Support
|
||||
|
||||
_Release date: 22.09.2018_
|
||||
|
||||
Version 1.2.4 is a small update with some improvements like browser-support and optimizing some themes and plugins, so please update the system folder, the plugin folder and the theme folder.
|
||||
|
||||
- Tested and optimized the admin panel with Firefox, Chrome, Edge and IE11.
|
||||
- Add an individual info-link for the cookieconsent plugin.
|
||||
- Redesigned and enhanced author info, share-links and date for standard theme.
|
||||
- Fixed: Get error message if session is out when saving changes in the editor.
|
||||
- Fixed: Get error message if try to move a page with unsafed changes.
|
||||
- Fixed: Optimized logic if moved file with live, draft or json version.
|
||||
- Fixed: Saves changes first before page is unpublished.
|
||||
|
||||
## Version 1.2.3: Reorder Pages
|
||||
|
||||
_Release date: 14.09.2018_
|
||||
|
||||
**Important: Upgrade to PHP 7**: With version 1.2.3 TYPEMILL requires PHP Version 7.0 or higher. This is due to a library (CSRF-protection), that uses a PHP7 function that is not supported by PHP 5.6. PHP 5.6 and PHP 7.0 will run out of life cycle in december 2018, so you should upgrade your server to PHP 7.1 or PHP 7.2 anyway.
|
||||
|
||||
Wow!! More than six weeks and only one feature: As of version 1.2.3 you can re-order your existing content. To do so, simply drag&drop the items in the new navigation of the author panel. It looks so easy, but it was so complex in the background!!
|
||||
|
||||
Follow the instruction for simple updates in the [documentation](/gettings-started/update) and simply update the `system` folder.
|
||||
|
||||
Be aware, that all existing content-files will be renamed if you reorder a file, because we add a new order-prefix to the file name. And be aware of some rules and limitations:
|
||||
|
||||
* You can move files to any other folder.
|
||||
* Only folders are allowed at the first level.
|
||||
* Folders can be reordered within the same level.
|
||||
* But a folder can not be moved to another folder or another level.
|
||||
|
||||
Here is the reason for the last restriction: If you move a folder to another folder, then the adress (url) will change for the whole folder and all its content (pages). It is a nightmare for your readers and for google.
|
||||
|
||||
If you really want to reorder your content completely, then you have to do it on the file system for now. In version 2.3.5 you will be able to create new folders and files in the author system, so you can create a new folder and move all files to the new folder manually.
|
||||
|
||||
## Version 1.2.2: Draft Management
|
||||
|
||||
_Release date: 24.07.2018_
|
||||
|
||||
Version 1.2.2 introduces a draft management. Follow the instruction for simple updates in the [documentation](/gettings-started/update), so update the `system` folder and please also update the theme `typemill`.
|
||||
|
||||
The changes are:
|
||||
|
||||
- Safe a draft.
|
||||
- Publish a page.
|
||||
- Depublish a page.
|
||||
- Delete a page.
|
||||
- All buttons for editing are fixed at the bottom of the page now.
|
||||
- Created a new shared contentController for the whole page management.
|
||||
- Extended the content-api with update, depublish and delete functionalities.
|
||||
- Converted markdown to json for edit- and draft-management (stored as txt).
|
||||
- Extended the vue-js for the editor.
|
||||
|
||||
## Version 1.2.1: Improved editor and fixes
|
||||
|
||||
_Release date: 06.07.2018_
|
||||
|
||||
** Version 1.2.1 has an important security-update, so please switch to version 1.2.1 asap. Follow the instruction for simple updates** in the [documentation](/gettings-started/update), so update the `system` folder and please also update the theme `typemill`.
|
||||
|
||||
The changes are:
|
||||
|
||||
* Fixed soft linebreaks in markdown. Now you can add two spaces at the end of a line and you will get a soft linebreak with `<br/>`.
|
||||
* Fixed navigation-error. The navigation will work now if the content starts with multiple nested folders.
|
||||
* Links on startpage are now displayed correctly.
|
||||
* You can now position your images with three classes `.left`, `.right`, `.middle`. Check the markdown test in the documentation for more info and examples.
|
||||
* Added markdown example for linked image in documentation.
|
||||
* Maximum lenght of title in editor is now 100 characters instead of 40.
|
||||
* All redirects to the author panel go to content now instead of settings.
|
||||
* Added error message, if file is not writable.
|
||||
* Added csrf-check and more security to the content-api.
|
||||
* Massively improved vue-code for the editor. Now it is vue.js logic and not vanilla script.
|
||||
* Massivley reduced and cleaned up custom code for parsedown-extension.
|
||||
|
||||
## 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 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.
|
||||
- Added security headers for content security policy, refferers, strict transport.
|
||||
|
||||
## Version 1.1.6: Refactoring
|
||||
|
||||
_Release date: 22.05.2018_
|
||||
|
||||
**Please follow the instructions for minor updates** in the [documentation](/gettings-started/update). Please also update the Typemill theme and the plugins.
|
||||
|
||||
* Added security headers.
|
||||
* Added a temporary ip-blocker if login fails 3 times.
|
||||
|
||||
- Added fieldsets for configuration fields of themes and plugins.
|
||||
- Added logic for user roles.
|
||||
- Changed url-structure from `tm-author` to `tm`.
|
||||
- Activated twig cache.
|
||||
- Started to refactored css for author panel.
|
||||
|
||||
## Version 1.1.5: Typemill Learns Math
|
||||
|
||||
_Release date: 10.05.2018_
|
||||
|
||||
**Please follow the instructions for minor updates** in the [documentation](/gettings-started/update). Please also update the Typemill theme and the plugins.
|
||||
|
||||
With version 1.1.5 Typemill learns math. There is a plugin called "math" that you can activate in the author panel. you can choose between MathJax and KaTeX. For the syntax to write math in markdown, please check the [markdown reference page](/info/markdown-test). Here are the details of this version:
|
||||
|
||||
* Extend markdown to support latex syntax.
|
||||
* New plugin to activate MathJax or KaTeX.
|
||||
* Highlight for code blocks is now a plugin.
|
||||
* Share buttons for twitter, facebook and xing can be added to the TYPEMILL theme.
|
||||
* Optimized mobile startpage.
|
||||
* Table of content has now permanent ids and anchors (headline) instead of dynamic numbers.
|
||||
|
||||
## Version 1.1.4: Refactoring
|
||||
|
||||
_Release date: (30.04.2018)_
|
||||
|
||||
**Please follow the instructions for minor updates** in the [documentation](/gettings-started/update). Please update the Typemill theme, too.
|
||||
|
||||
Version 1.1.4 is mainly an optimization and refactoring of the author panel released in version 1.1.3. Some details of the changes:
|
||||
|
||||
* Length of description is optimized for SEO now.
|
||||
* You can display a last modified date on each page now (update the TYPEMILL theme for that).
|
||||
* Optimized mobile version of the author-panel navigation.
|
||||
* Form rendering is now done in a seperate form template.
|
||||
* SettingsController is deeply refactored and some bugs are fixed.
|
||||
* FormModel partly fixed and refactored.
|
||||
* ValidationModel fixed.
|
||||
* Update notifications for system, themes and plugins are completely refactored and unified.
|
||||
* Welcome page is fixed, so that the linked themes- and plugin settings show the initial values now.
|
||||
* Settings functions for plugins and themes are cleaned up and unified.
|
||||
* Design is partly fixed and unified (open-close icons and more).
|
||||
* User documentation has been enhanced and corrected.
|
||||
|
||||
## Version 1.1.3: The Author Panel
|
||||
|
||||
_Release date: 19.04.2018_
|
||||
|
||||
**Please follow the instructions for major updates** in the [documentation](/gettings-started/update).
|
||||
|
||||
Version 1.1.3 introduces an author panel. Now you can authenticate, configure the system, themes and plugins and manage users. This is the first step towards a full authoring panel that helps you to manage, create and edit everything online.
|
||||
|
||||
* Author panel.
|
||||
* Authentication.
|
||||
* Configuration of the system, the theme and the plugins.
|
||||
* User management (create update and deleted).
|
||||
* Bugfix: Footnotes in Markdown didn't work due to parsedown library. Fixed in the new library version.
|
||||
|
||||
## Version 1.1.2: Improved Setup
|
||||
|
||||
_Release Date: 15.03.2018_
|
||||
|
||||
With version 1.1.2 we added the possibility to configure the theme in the setup a bit more. We also fixed a bug in the htaccess. So next to the "system"-folder, you should also update the theme-folder and the htaccess-file. As always, backup your settings.yaml-file, then delete it and setup your website again (visit /setup).
|
||||
|
||||
* Added configurations for themes in the setup.
|
||||
* Improved update check for themes, plugins and base app.
|
||||
* optimized design of setup.
|
||||
* Added language selection to setup (used only for lang-attribute right now).
|
||||
* Updated typemill theme accordingly
|
||||
* Bugfix: Index.php is not reachable anymore, prevents duplicate content. Please update htaccess-file.
|
||||
|
||||
## Version 1.1.1: Refactoring
|
||||
|
||||
_Release date: 25.02.2018_
|
||||
|
||||
Version 1.1.1 is mainly an improvement and some refactoring for the plugin release.
|
||||
|
||||
* Added new plugin "analytics" for integration of Matomo/Piwik and Google Analytics.
|
||||
* Theme "TYPEMILL": Design refresh.
|
||||
* Theme "TYPEMILL": Added cannonical url.
|
||||
* Theme "TYPEMILL": Added meta tags for social sharing including image reference.
|
||||
* Increased length of meta-description for google.
|
||||
* Fixed error with field builder (getAttributeValues).
|
||||
* Fixed error with static functions in settings (added declaration as static).
|
||||
* Added documentation for plugin developers.
|
||||
|
||||
## Version 1.1.0: Introduce Plugins
|
||||
|
||||
_Release date: 05.02.2018_
|
||||
|
||||
Version 1.1.0 introduces plugins to typemill. And because of the GDPR, the first plugin is an implementation of the famous cookieconsent. So heads up, you can publish GDPR-compliant websites with typemill now!
|
||||
|
||||
All plugins are in the "plugin"-folder, and I can't wait, that this folder will be stuffed with cool extensions.
|
||||
|
||||
To update the system, please delete, replace and upload
|
||||
|
||||
* the "system" folder and
|
||||
* the "theme" folder.
|
||||
* the "plugin" folder (new).
|
||||
|
||||
Then backup and delete your settings.yaml in the settings-folder.
|
||||
|
||||
Now visit your startpage and click on the new setup-button. The button will direct you to the new setup page, where you can configure the basic system and the new cookieconsent-plugin.
|
||||
|
||||
Plugins are easy to use for writers, but a plugin system is pretty complex to implement for a developer, so there is a lot of new code. For now, there is no documentation for developers, but it will follow, soon.
|
||||
|
||||
Also introduced with 1.1.0 :
|
||||
|
||||
* Field Builder
|
||||
* CSRF-Protection
|
||||
* Input Validation
|
||||
* Version Check for Plugins, Themes and Core-System
|
||||
* Simple Asset Manager
|
||||
* Twig Extensions
|
||||
|
||||
## Version 1.0.5: Table of Contents
|
||||
|
||||
_Release date: 30.11.2017_
|
||||
|
||||
- Improvement: Character encoding for the navigation has improved. You can try to use other characters than english for your file names now, but there is no garanty for it. If the characters do not work in the navigation, please use english characters only.
|
||||
- Improvement: A [TOC]-tag for generating a table of contents is now implemented. You can use the tag anywhere in your text-files, but please use a separate line for it. Update the theme for stylings.
|
||||
|
||||
## Version 1.0.4: Bugfixes
|
||||
|
||||
_Release date: 17.11.2017_
|
||||
|
||||
- Bugfix: Settings file was generated after a page refresh, this is fixed now.
|
||||
- Improvement: Cleaned up the load and merge process for settings, managed in a new static class now.
|
||||
|
||||
## Version 1.0.3: Getting Slim
|
||||
|
||||
_Release date: 14.11.2017_
|
||||
|
||||
Main improvement is a litte build process, that strips out all developer related files and reduces the size of typemill dramatically from 2.5 MB to less then 1 MB (gzipped).
|
||||
|
||||
- Bugfix: Deleted a config-file in the download-version, that broke the setup url.
|
||||
- Improvement: Meta-title is now created with the first h1-headline in the content file. File-name is used as fall back. **Please update the theme-folder with the theme-folder of version 1.0.3!!!** This will improve SEO.
|
||||
- Improvement: Stripped out all developer files in the download-version. This reduced the size of the zip-download from 2.5 MB to 800kb.
|
||||
- Improvement: Changed Namespace from "System" to "Typemill".
|
||||
|
||||
## Version 1.0.2: Bugfix
|
||||
|
||||
_Release date: 02.07.2017_
|
||||
|
||||
- Bugfix: The theme can now be changed in the yaml-file again.
|
||||
|
||||
## Version 1.0.1: Google Sitemap
|
||||
|
||||
_Release date: 01.05.2017_
|
||||
|
||||
- Bugfix: Index file in the content folder won't break the building of the navigation tree anymore.
|
||||
- New Feature: Added a google sitemap.xml in the cache folder.
|
||||
- New Feature: Added a version check, an update message can be displayed in theme now.
|
||||
|
||||
## Version 1.0.0: Hello TYPEMILL!
|
||||
_Release date: 13.04.2017_
|
||||
|
||||
The first alpha version of typemill with all basic features for a simple website:
|
||||
|
||||
- **Content** with Markdown files and folders
|
||||
- **Settings** with YAML and a setup page
|
||||
- **Themes** with Twig and six theme variables
|
||||
- {{ content }}
|
||||
- {{ description }}
|
||||
- {{ item }}
|
||||
- {{ breadcrumb }}
|
||||
- {{ navigation }}
|
||||
- {{ settings }}
|
@ -1,16 +0,0 @@
|
||||
# Usage and Licence
|
||||
|
||||
TYPEMILL is published under the MIT licence. This means, that you can do with it whatever you want.
|
||||
|
||||
You are always welcome to reference or link to TYPEMILL, to report bugs and to contribute to this project (e.g. share fixes, enhancements or ideas). If this software safed you a lot of time and money, then you are invited to donate some bucks, so I can save some money, too :-)
|
||||
|
||||
---
|
||||
|
||||
Copyright (c) 2017 Sebastian Schürmanns
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
@ -1,12 +0,0 @@
|
||||
# Imprint
|
||||
|
||||
The owner of this website is
|
||||
|
||||
Sebastian Schürmanns
|
||||
|
||||
Stendaler Str. 15
|
||||
|
||||
10559 Berlin
|
||||
|
||||
<trendschau@gmail.com>
|
||||
|
@ -1,319 +0,0 @@
|
||||
# Markdown Reference and Test Page
|
||||
|
||||
Markdown is a simple and universal syntax for text formatting. More and more writers switch to markdown, because they can format their text during the writing process without using any format-buttons. Once they are familiar with the markdown syntax, they can write formatted text much easier and faster than with any standard HTML-editor.
|
||||
|
||||
Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and others.
|
||||
|
||||
If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly.
|
||||
|
||||
##Table of Contents
|
||||
|
||||
To create a table of contents, simply write `[TOC]` in a separate line. It will be replaced with a table of contents like this automatically.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Headlines
|
||||
|
||||
```
|
||||
Headlines are simply done with hash chars like this:
|
||||
|
||||
# First Level Headline
|
||||
## Second Level Headline
|
||||
### Third Level Headline
|
||||
#### Fourth Level Headline
|
||||
##### Fifth Level Headline
|
||||
###### Sixth Level Headline
|
||||
```
|
||||
|
||||
### Third Level Headline
|
||||
|
||||
A third headline is more decent and lower prioritized than a second level headline.
|
||||
|
||||
#### Fourth Level Headline
|
||||
|
||||
A fourth level headline is more decent and lower prioritized than a third level headline.
|
||||
|
||||
##### Fifth Level Headline
|
||||
|
||||
A fifth level headline is more decent and lower prioritized than a fourth level headline.
|
||||
|
||||
##### Sixth Level Headline
|
||||
|
||||
A sixth level headline is more decent and lower prioritized than a fifths level headline.
|
||||
|
||||
##Paragraph
|
||||
|
||||
````
|
||||
|
||||
A paragraph is a simple text-block separated with a new line above and below.
|
||||
|
||||
````
|
||||
|
||||
A paragraph is a simple text-block separated with a new line above and below.
|
||||
|
||||
## Soft Linebreak
|
||||
|
||||
````
|
||||
For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return.
|
||||
|
||||
She said: "Hello"
|
||||
He said: "again"
|
||||
````
|
||||
|
||||
For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return.
|
||||
|
||||
She said: "Hello"
|
||||
He said: "again"
|
||||
|
||||
##Emphasis
|
||||
|
||||
````
|
||||
For italic text use one *asterix* or one _underscore_.
|
||||
For bold text use two **asterix** or two __underscores__.
|
||||
````
|
||||
|
||||
For italic text use one *asterix* or one _underscore_.
|
||||
|
||||
For bold text use two **asterix** or two __underscores__.
|
||||
|
||||
##Lists
|
||||
|
||||
````
|
||||
For an unordered list use a dash
|
||||
|
||||
- like
|
||||
- this
|
||||
|
||||
Or use one asterix
|
||||
|
||||
* like
|
||||
* this
|
||||
|
||||
For an ordered list use whatever number you want and add a dot:
|
||||
|
||||
1. like
|
||||
1. this
|
||||
````
|
||||
|
||||
For an unordered list use a dash
|
||||
|
||||
- like
|
||||
- this
|
||||
|
||||
Or use one asterix
|
||||
|
||||
* like
|
||||
* this
|
||||
|
||||
For an ordered list use whatever number you want and add a dot:
|
||||
|
||||
1. like
|
||||
2. this
|
||||
|
||||
## Horizontal Rule
|
||||
|
||||
```
|
||||
Easily created for example with three dashes like this:
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
Easily created for example with three dashes like this:
|
||||
|
||||
---
|
||||
|
||||
##Links
|
||||
|
||||
````
|
||||
This is an ordinary [Link](http://typemill.net).
|
||||
|
||||
Links can also be [relative](/info).
|
||||
|
||||
You can also add a [title](http://typemill.net "typemill").
|
||||
|
||||
You can even add [ids or classes](http://typemill.net){#myid .myclass}.
|
||||
|
||||
Or you can use a shortcut like http://typemill.net.
|
||||
````
|
||||
|
||||
This is an ordinary [Link](http://typemill.net).
|
||||
|
||||
Links can also be [relative](/info).
|
||||
|
||||
You can also add a [title](http://typemill.net "typemill").
|
||||
|
||||
You can even add [ids or classes](http://typemill.net){#myid .myclass}.
|
||||
|
||||
Or you can use a shortcut like http://typemill.net.
|
||||
|
||||
##Images
|
||||
|
||||
```
|
||||
The same rules as with links, but with a !
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
{#myid .myclass}
|
||||
```
|
||||
|
||||
The same rules as with links, but with a !
|
||||
|
||||
{.imgClass}
|
||||
|
||||
{.imgClass}
|
||||
|
||||
{#myid .imgClass .myClass}
|
||||
|
||||
## Linked Images
|
||||
|
||||
````
|
||||
You can link an image with a nested syntax like this:
|
||||
|
||||
[](https://typemill.net)
|
||||
````
|
||||
|
||||
You can link an image with a nested syntax like this:
|
||||
|
||||
[{.imgClass}](https://typemill.net)
|
||||
|
||||
## Image Position
|
||||
|
||||
````
|
||||
You can controll the image position with the classes .left, .right and .middle like this:
|
||||
|
||||
{.left}
|
||||
{.right}
|
||||
{.middle}
|
||||
````
|
||||
|
||||
{.left}
|
||||
|
||||
The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle".
|
||||
|
||||
{.right}
|
||||
|
||||
The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle".
|
||||
|
||||
{.middle}
|
||||
|
||||
The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle".
|
||||
|
||||
## Blockquote
|
||||
|
||||
```
|
||||
There are always some women and men with wise words
|
||||
|
||||
> But I usually don't read them, to be honest.
|
||||
```
|
||||
|
||||
There always some women and men with wise words
|
||||
|
||||
> But I usually don't read them, to be honest.
|
||||
|
||||
##Footnotes
|
||||
|
||||
````
|
||||
You can write footnotes[^1] with markdown.
|
||||
|
||||
Scroll down to the end of the page[^2] and look for the footnotes.
|
||||
|
||||
Add the footnote text at the bottom of the page like this:
|
||||
|
||||
[^1]: Thank you for scrolling.
|
||||
[^2]: This is the end of the page.
|
||||
````
|
||||
|
||||
You can write footnotes[^1] with markdown.
|
||||
|
||||
Scroll down to the end of the page[^2] and look for the footnotes.
|
||||
|
||||
## Abbreviations
|
||||
|
||||
````
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
|
||||
*[W3C]: World Wide Web Consortium
|
||||
````
|
||||
|
||||
You won't see the abbreviation directly, but if you write HTML or W3C somewhere, then you can see the tooltip with the explanation.
|
||||
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
|
||||
*[W3C]: World Wide Web Consortium
|
||||
|
||||
## Definition List
|
||||
|
||||
````
|
||||
Apple
|
||||
: Pomaceous fruit of plants of the genus Malus in the family Rosaceae.
|
||||
|
||||
Orange
|
||||
: The fruit of an evergreen tree of the genus Citrus.
|
||||
````
|
||||
|
||||
Apple
|
||||
: Pomaceous fruit of plants of the genus Malus in
|
||||
the family Rosaceae.
|
||||
|
||||
Orange
|
||||
: The fruit of an evergreen tree of the genus Citrus.
|
||||
|
||||
## Tables
|
||||
|
||||
````
|
||||
|name |usage |
|
||||
|-----------|-----------|
|
||||
| My Name | For Me |
|
||||
| Your Name | For You |
|
||||
````
|
||||
|
||||
| Name | Usage |
|
||||
| --------- | ------- |
|
||||
| My Name | For Me |
|
||||
| Your Name | For You |
|
||||
|
||||
## Code
|
||||
|
||||
````
|
||||
Let us create some `<?php inlineCode(); ?>` and now let us check
|
||||
````
|
||||
|
||||
Let us create some `<?php inlineCode(); ?>` and now let us check, if a codeblock works:
|
||||
|
||||
```
|
||||
```
|
||||
<?php
|
||||
$welcome = 'Hello World!';
|
||||
echo $welcome;
|
||||
?>
|
||||
```
|
||||
```
|
||||
## Math
|
||||
|
||||
Please activate the math-plugin to use mathematical expressions with LaTeX syntax. You can choose between MathJax or the newer KaTeX library. MathJax is included from a CDN, KaTeX is included in the plugin. So if you don't want to fetch code from a CDN, use KaTeX instead. The markdown syntax in TYPEMILL is the same for both libraries.
|
||||
|
||||
````
|
||||
Use inline LaTeX ``x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)`` like this.
|
||||
````
|
||||
|
||||
Use inline LaTeX ``x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)`` like this.
|
||||
|
||||
````
|
||||
Or specify latex sytnax for a code-block like this:
|
||||
|
||||
```latex
|
||||
x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
|
||||
```
|
||||
````
|
||||
|
||||
Or specify latex sytnax for a code-block like this:
|
||||
|
||||
```latex
|
||||
x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
|
||||
```
|
||||
Please make sure that all tests run successfully.
|
||||
|
||||
[^1]: Thank you for scrolling.
|
||||
[^2]: This is the end of the page.
|
@ -1,3 +0,0 @@
|
||||
# Info
|
||||
|
||||
Some informations like version history, copyright, licence and imprint.
|
@ -1,5 +1,6 @@
|
||||
# Typemill
|
||||
|
||||
**MODERN WEB PUBLISHING FOR WRITERS**
|
||||
|
||||
*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prosa, lyrics, manuals, documentations, studies and more. Just download and start.*
|
||||
|
||||
*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prosa, lyrics, manuals, documentations, studies and more. Just download and start.*
|
||||
|
||||
|
1
content/index.txt
Normal file
1
content/index.txt
Normal file
@ -0,0 +1 @@
|
||||
["# Typemill","**MODERN WEB PUBLISHING FOR WRITERS**","*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prosa, lyrics, manuals, documentations, studies and more. Just download and start.*"]
|
@ -16,7 +16,7 @@ class AuthController extends Controller
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('content.show'));
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -125,7 +125,7 @@ class AuthController extends Controller
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('content.show'));
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,13 +11,16 @@ use Typemill\Extensions\ParsedownExtension;
|
||||
class ContentApiController extends ContentController
|
||||
{
|
||||
public function publishArticle(Request $request, Response $response, $args)
|
||||
{
|
||||
{
|
||||
# get params from call
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
# validate input
|
||||
if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
|
||||
# validate input only if raw mode
|
||||
if($this->params['raw'])
|
||||
{
|
||||
if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
|
||||
}
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
|
||||
@ -27,15 +30,36 @@ class ContentApiController extends ContentController
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# if raw mode, use the content from request
|
||||
if($this->params['raw'])
|
||||
{
|
||||
$this->content = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
|
||||
}
|
||||
else
|
||||
{
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
# If it is a draft, then create clean markdown content
|
||||
if(is_array($this->content))
|
||||
{
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$this->content = $parsedown->arrayBlocksToMarkdown($this->content);
|
||||
}
|
||||
}
|
||||
|
||||
# set path for the file (or folder)
|
||||
$this->setItemPath('md');
|
||||
|
||||
# merge title with content for complete markdown document
|
||||
$updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
|
||||
|
||||
# update the file
|
||||
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $updatedContent))
|
||||
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $this->content))
|
||||
{
|
||||
# update the file
|
||||
$delete = $this->deleteContentFiles(['txt']);
|
||||
@ -123,7 +147,7 @@ class ContentApiController extends ContentController
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
# set url to base path initially
|
||||
$url = $this->uri->getBaseUrl() . '/tm/content';
|
||||
$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
|
||||
@ -197,8 +221,8 @@ class ContentApiController extends ContentController
|
||||
$contentArray = $parsedown->markdownToArrayBlocks($updatedContent);
|
||||
|
||||
# encode the content into json
|
||||
$contentJson = json_encode($contentArray);
|
||||
|
||||
$contentJson = json_encode($contentArray);
|
||||
|
||||
/* update the file */
|
||||
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
|
||||
{
|
||||
@ -212,7 +236,7 @@ class ContentApiController extends ContentController
|
||||
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function sortArticle(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
@ -338,12 +362,12 @@ class ContentApiController extends ContentController
|
||||
$folderContent = $folder->folderContent;
|
||||
|
||||
# create the name for the new item
|
||||
$nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
$nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
$name = implode("-", $nameParts);
|
||||
$slug = $name;
|
||||
|
||||
# initialize index
|
||||
$index = 0;
|
||||
$index = 0;
|
||||
|
||||
# initialise write object
|
||||
$write = new Write();
|
||||
@ -372,9 +396,12 @@ class ContentApiController extends ContentController
|
||||
$namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name;
|
||||
$folderPath = 'content' . $folder->path;
|
||||
|
||||
# create default content
|
||||
$content = json_encode(['# Add Title', 'Add Content']);
|
||||
|
||||
if($this->params['type'] == 'file')
|
||||
{
|
||||
if(!$write->writeFile($folderPath, $namePath . '.txt', ''))
|
||||
if(!$write->writeFile($folderPath, $namePath . '.txt', $content))
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
@ -385,7 +412,7 @@ class ContentApiController extends ContentController
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', '');
|
||||
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
|
||||
}
|
||||
|
||||
# update the structure for editor
|
||||
@ -457,7 +484,11 @@ class ContentApiController extends ContentController
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', '');
|
||||
|
||||
# create default content
|
||||
$content = json_encode(['# Add Title', 'Add Content']);
|
||||
|
||||
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
|
||||
|
||||
# update the structure for editor
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
@ -471,53 +502,239 @@ class ContentApiController extends ContentController
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url));
|
||||
}
|
||||
|
||||
|
||||
public function createBlock(Request $request, Response $response, $args)
|
||||
public function getArticleMarkdown(Request $request, Response $response, $args)
|
||||
{
|
||||
/* get params from call */
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
/* validate input */
|
||||
if(!$this->validateInput()){ return $response->withJson($this->errors,422); }
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
/* set structure */
|
||||
if(!$this->setStructure()){ return $response->withJson($this->errors, 404); }
|
||||
/* set item */
|
||||
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($content))
|
||||
{
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$content = $parsedown->markdownToArrayBlocks($content);
|
||||
}
|
||||
|
||||
# delete markdown from title
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$content[0] = trim($content[0], "# ");
|
||||
}
|
||||
|
||||
return $response->withJson(array('data' => $content, 'errors' => false));
|
||||
}
|
||||
|
||||
public function updateBlock(Request $request, Response $response, $args)
|
||||
{
|
||||
/* get params from call */
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
/* validate input */
|
||||
if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
/* set item */
|
||||
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
/* set path */
|
||||
$this->setItemPath();
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
/* get markdown-file */
|
||||
if(!$this->setMarkdownFile()){ return $response->withJson($this->errors, 404); }
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
# make it more clear which content we have
|
||||
$pageMarkdown = $this->content;
|
||||
|
||||
$blockMarkdown = $this->params['markdown'];
|
||||
|
||||
# standardize line breaks
|
||||
$blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown);
|
||||
|
||||
# remove surrounding line breaks
|
||||
$blockMarkdown = trim($blockMarkdown, "\n");
|
||||
|
||||
/* get txt-file with content array */
|
||||
$contentArray = NULL;
|
||||
if($pageMarkdown == '')
|
||||
{
|
||||
$pageMarkdown = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($pageMarkdown))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown);
|
||||
}
|
||||
|
||||
# if it is a new content-block
|
||||
if($this->params['block_id'] == 99999)
|
||||
{
|
||||
# update the markdown block in the page content
|
||||
$pageMarkdown[] = $blockMarkdown;
|
||||
$id = (count($pageMarkdown)-1);
|
||||
$blockId = 'blox-' . $id;
|
||||
}
|
||||
elseif(!isset($pageMarkdown[$this->params['block_id']]))
|
||||
{
|
||||
# return error
|
||||
return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404);
|
||||
}
|
||||
elseif($this->params['block_id'] == 0)
|
||||
{
|
||||
# update the markdown block in the page content
|
||||
$blockMarkdown = trim($blockMarkdown, "# ");
|
||||
|
||||
$blockMarkdownTitle = '# ' . $blockMarkdown;
|
||||
|
||||
$pageMarkdown[$this->params['block_id']] = $blockMarkdownTitle;
|
||||
$id = $this->params['block_id'];
|
||||
$blockId = $this->params['block_id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
# update the markdown block in the page content
|
||||
$pageMarkdown[$this->params['block_id']] = $blockMarkdown;
|
||||
$id = $this->params['block_id'];
|
||||
$blockId = $this->params['block_id'];
|
||||
}
|
||||
|
||||
# encode the content into json
|
||||
$pageJson = json_encode($pageMarkdown);
|
||||
|
||||
# set path for the file (or folder)
|
||||
$this->setItemPath('txt');
|
||||
|
||||
/* update the file */
|
||||
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
|
||||
{
|
||||
# update the internal structure
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
|
||||
}
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* parse markdown-file to content-array, if title parse title. */
|
||||
if($this->params['block_id'] == 0)
|
||||
{
|
||||
$blockArray = $parsedown->text($blockMarkdownTitle);
|
||||
}
|
||||
else
|
||||
{
|
||||
$blockArray = $parsedown->text($blockMarkdown);
|
||||
}
|
||||
|
||||
/*
|
||||
create a txt-file with parsedown-array.
|
||||
you will have .md and .txt file.
|
||||
scan folder with option to show drafts.
|
||||
but what is with structure? We use the cached structure, do not forget!!!
|
||||
if there is a draft, replace the md file with txt-file.
|
||||
display content: you have to check if md or txt. if txt, then directly open the txt-file.
|
||||
in here set markdown-file or
|
||||
set txt-file.
|
||||
if publish, render txt-content, replace markdown-file, delete txt-file
|
||||
*/
|
||||
/* parse markdown-content-array to content-string */
|
||||
$blockHTML = $parsedown->markup($blockArray);
|
||||
|
||||
return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'blockId' => $blockId, 'id' => $id, 'errors' => false));
|
||||
}
|
||||
|
||||
public function deleteBlock(Request $request, Response $response, $args)
|
||||
{
|
||||
/* get params from call */
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
/* initialize pagedown */
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
/* turn input into array */
|
||||
# set item
|
||||
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
|
||||
|
||||
# get content
|
||||
$this->content;
|
||||
|
||||
if($this->content == '')
|
||||
{
|
||||
$this->content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($this->content))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$this->content = $parsedown->markdownToArrayBlocks($this->content);
|
||||
}
|
||||
|
||||
# check if id exists
|
||||
if(!isset($this->content[$this->params['block_id']])){ return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); }
|
||||
|
||||
# delete the block
|
||||
unset($this->content[$this->params['block_id']]);
|
||||
$this->content = array_values($this->content);
|
||||
|
||||
# delete markdown from title
|
||||
if(isset($this->content[0]))
|
||||
{
|
||||
$this->content[0] = trim($this->content[0], "# ");
|
||||
}
|
||||
|
||||
/* add input to contentArray */
|
||||
|
||||
/* store updated contentArray */
|
||||
|
||||
/* transform input to html */
|
||||
|
||||
/* send html to client */
|
||||
}
|
||||
# encode the content into json
|
||||
$pageJson = json_encode($this->content);
|
||||
|
||||
# set path for the file (or folder)
|
||||
$this->setItemPath('txt');
|
||||
|
||||
/* update the file */
|
||||
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
|
||||
{
|
||||
# update the internal structure
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
|
||||
}
|
||||
return $response->withJson(array('markdown' => $this->content, 'errors' => false));
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ use Typemill\Extensions\ParsedownExtension;
|
||||
class ContentBackendController extends ContentController
|
||||
{
|
||||
/**
|
||||
* Show Content
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
@ -43,7 +43,7 @@ class ContentBackendController extends ContentController
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->render404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
@ -72,6 +72,76 @@ class ContentBackendController extends ContentController
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor.twig', array('navigation' => $this->structure, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# get the breadcrumb (here we need it only to mark the actual item active in navigation)
|
||||
$breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false;
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
foreach($content as $key => $block)
|
||||
{
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($block);
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$content[$key] = $parsedown->markup($contentArray);
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
public function showEmpty(Request $request, Response $response, $args)
|
||||
{
|
||||
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
|
||||
}
|
||||
}
|
@ -85,7 +85,7 @@ abstract class ContentController
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data);
|
||||
}
|
||||
|
||||
|
||||
protected function validateEditorInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
@ -98,6 +98,19 @@ abstract class ContentController
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateBlockInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->blockInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$this->errors = ['errors' => $vResult];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNavigationSort()
|
||||
{
|
||||
@ -288,7 +301,7 @@ abstract class ContentController
|
||||
}
|
||||
|
||||
protected function setContent()
|
||||
{
|
||||
{
|
||||
# if the file exists
|
||||
if($this->item->published OR $this->item->drafted)
|
||||
{
|
||||
|
@ -122,7 +122,7 @@ class PageController extends Controller
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
|
@ -44,7 +44,8 @@ class SettingsController extends Controller
|
||||
'author' => $newSettings['author'],
|
||||
'copyright' => $newSettings['copyright'],
|
||||
'year' => $newSettings['year'],
|
||||
'startpage' => isset($newSettings['startpage']) ? true : false
|
||||
'startpage' => isset($newSettings['startpage']) ? true : false,
|
||||
'editor' => $newSettings['editor'],
|
||||
);
|
||||
|
||||
$copyright = $this->getCopyright();
|
||||
|
@ -29,7 +29,7 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
{
|
||||
# convert to markup
|
||||
$markup = $this->elements($Elements);
|
||||
|
||||
|
||||
# trim line breaks
|
||||
$markup = trim($markup, "\n");
|
||||
|
||||
@ -266,7 +266,7 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
$Block['element']['text'] = "\$\$\n" . $text . "\n\$\$";
|
||||
return $Block;
|
||||
}
|
||||
|
||||
|
||||
# turn markdown into an array of markdown blocks for typemill edit mode
|
||||
function markdownToArrayBlocks($markdown)
|
||||
{
|
||||
@ -294,7 +294,7 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
{
|
||||
// remove empty lines
|
||||
if (chop($block) === '') continue;
|
||||
|
||||
|
||||
// if the block starts with a fenced code
|
||||
if(substr($block,0,2) == '``')
|
||||
{
|
||||
@ -332,12 +332,25 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
}
|
||||
}
|
||||
|
||||
$block = trim($block, "\n");
|
||||
|
||||
$cleanBlocks[] = $block;
|
||||
}
|
||||
|
||||
return $cleanBlocks;
|
||||
}
|
||||
|
||||
public function arrayBlocksToMarkdown(array $arrayBlocks)
|
||||
{
|
||||
$markdown = '';
|
||||
|
||||
foreach($arrayBlocks as $block)
|
||||
{
|
||||
$markdown .= $block . "\n\n";
|
||||
}
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
|
||||
protected function isComplete($codeblock)
|
||||
{
|
||||
$lines = explode("\n", $codeblock);
|
||||
@ -351,17 +364,5 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function arrayBlocksToMarkdown(array $arrayBlocks)
|
||||
{
|
||||
$markdown = '';
|
||||
|
||||
foreach($arrayBlocks as $block)
|
||||
{
|
||||
$markdown .= $block . "\n\n";
|
||||
}
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ class RedirectIfAuthenticated
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.show'));
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
|
@ -24,7 +24,7 @@ class RedirectIfNoAdmin
|
||||
|
||||
if($_SESSION['role'] != 'administrator')
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.show'));
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
|
@ -187,13 +187,14 @@ class Validation
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['title', 'author', 'copyright', 'year']);
|
||||
$v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']);
|
||||
$v->rule('lengthBetween', 'title', 2, 20);
|
||||
$v->rule('lengthBetween', 'author', 2, 40);
|
||||
$v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
|
||||
$v->rule('regex', 'author', '/^[\pL_ \-]*$/u');
|
||||
$v->rule('integer', 'year');
|
||||
$v->rule('length', 'year', 4);
|
||||
$v->rule('in', 'editor', ['raw', 'visual']);
|
||||
$v->rule('in', 'copyright', $copyright);
|
||||
|
||||
return $this->validationResult($v, $name);
|
||||
@ -224,6 +225,24 @@ class Validation
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
public function blockInput(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['markdown', 'block_id', 'url']);
|
||||
$v->rule('markdownSecure', 'markdown');
|
||||
$v->rule('regex', 'block_id', '/^[0-9.]+$/i');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for resort navigation
|
||||
|
@ -7,6 +7,7 @@ use Typemill\Middleware\RestrictApiAccess;
|
||||
|
||||
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
|
||||
@ -15,4 +16,5 @@ $app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')-
|
||||
$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
// $app->post('/api/v1/block', ContentBackendController::class . ':createBlock')->setName('api.block.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
|
@ -46,7 +46,9 @@ $app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setNam
|
||||
$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
|
||||
$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
|
||||
foreach($routes as $pluginRoute)
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ class Settings
|
||||
'settingsPath' => $rootPath . 'settings',
|
||||
'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
|
||||
'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
|
||||
'editor' => 'raw',
|
||||
'contentFolder' => 'content',
|
||||
'cache' => true,
|
||||
'cachePath' => $rootPath . 'cache',
|
||||
|
@ -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>Coming soon:</strong> You will probably miss a content editor in the author panel, but it will follow very soon. For time beeing you can write your content offline and upload the markdown files via FTP.</p>
|
||||
<p><strong>Coming soon:</strong> You can already use the web-editor to create, edit and sort your content, but there is no media library yet. We will add it together with a cool visual content editor, soon.</p>
|
||||
<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="http://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/trendschau/typemill"><i class="icon-link-ext"></i> github</a>.</p>
|
||||
</div>
|
||||
<a class="button" href="{{ path_for('settings.show') }}">Configure your website</a>
|
||||
|
@ -6,28 +6,46 @@
|
||||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "c5845105a87df2ee1999826d90622f6a",
|
||||
"css": "paragraph",
|
||||
"code": 61917,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f9cbf7508cd04145ade2800169959eef",
|
||||
"css": "font",
|
||||
"code": 59392,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e99461abfef3923546da8d745372c995",
|
||||
"css": "cog",
|
||||
"code": 59392,
|
||||
"code": 59393,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "381da2c2f7fd51f8de877c044d7f439d",
|
||||
"css": "picture",
|
||||
"code": 59393,
|
||||
"code": 59394,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
|
||||
"css": "off",
|
||||
"code": 59394,
|
||||
"code": 59395,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d7271d490b71df4311e32cdacae8b331",
|
||||
"css": "home",
|
||||
"code": 59395,
|
||||
"code": 59396,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
"code": 59397,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
@ -42,12 +60,6 @@
|
||||
"code": 61618,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5408be43f7c42bccee419c6be53fdef5",
|
||||
"css": "doc-text",
|
||||
"code": 61686,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "b091a8bd0fdade174951f17d936f51e4",
|
||||
"css": "folder-empty",
|
||||
@ -55,9 +67,9 @@
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
"code": 59396,
|
||||
"uid": "5408be43f7c42bccee419c6be53fdef5",
|
||||
"css": "doc-text",
|
||||
"code": 61686,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
|
@ -1,10 +1,12 @@
|
||||
|
||||
.icon-cog:before { content: '\e800'; } /* '' */
|
||||
.icon-picture:before { content: '\e801'; } /* '' */
|
||||
.icon-off:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-plus:before { content: '\e804'; } /* '' */
|
||||
.icon-font:before { content: '\e800'; } /* '' */
|
||||
.icon-cog:before { content: '\e801'; } /* '' */
|
||||
.icon-picture:before { content: '\e802'; } /* '' */
|
||||
.icon-off:before { content: '\e803'; } /* '' */
|
||||
.icon-home:before { content: '\e804'; } /* '' */
|
||||
.icon-plus:before { content: '\e805'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-paragraph:before { content: '\f1dd'; } /* '' */
|
File diff suppressed because one or more lines are too long
@ -1,10 +1,12 @@
|
||||
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
14
system/author/css/fontello/css/fontello-ie7.css
vendored
14
system/author/css/fontello/css/fontello-ie7.css
vendored
@ -10,12 +10,14 @@
|
||||
/* font-size: 120%; */
|
||||
}
|
||||
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
28
system/author/css/fontello/css/fontello.css
vendored
28
system/author/css/fontello/css/fontello.css
vendored
@ -1,11 +1,11 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?95383779');
|
||||
src: url('../font/fontello.eot?95383779#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?95383779') format('woff2'),
|
||||
url('../font/fontello.woff?95383779') format('woff'),
|
||||
url('../font/fontello.ttf?95383779') format('truetype'),
|
||||
url('../font/fontello.svg?95383779#fontello') format('svg');
|
||||
src: url('../font/fontello.eot?94639617');
|
||||
src: url('../font/fontello.eot?94639617#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?94639617') format('woff2'),
|
||||
url('../font/fontello.woff?94639617') format('woff'),
|
||||
url('../font/fontello.ttf?94639617') format('truetype'),
|
||||
url('../font/fontello.svg?94639617#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@ -15,7 +15,7 @@
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?95383779#fontello') format('svg');
|
||||
src: url('../font/fontello.svg?94639617#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -55,12 +55,14 @@
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-cog:before { content: '\e800'; } /* '' */
|
||||
.icon-picture:before { content: '\e801'; } /* '' */
|
||||
.icon-off:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-plus:before { content: '\e804'; } /* '' */
|
||||
.icon-font:before { content: '\e800'; } /* '' */
|
||||
.icon-cog:before { content: '\e801'; } /* '' */
|
||||
.icon-picture:before { content: '\e802'; } /* '' */
|
||||
.icon-off:before { content: '\e803'; } /* '' */
|
||||
.icon-home:before { content: '\e804'; } /* '' */
|
||||
.icon-plus:before { content: '\e805'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-paragraph:before { content: '\f1dd'; } /* '' */
|
@ -229,11 +229,11 @@ body {
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('./font/fontello.eot?67933592');
|
||||
src: url('./font/fontello.eot?67933592#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?67933592') format('woff'),
|
||||
url('./font/fontello.ttf?67933592') format('truetype'),
|
||||
url('./font/fontello.svg?67933592#fontello') format('svg');
|
||||
src: url('./font/fontello.eot?32756715');
|
||||
src: url('./font/fontello.eot?32756715#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?32756715') format('woff'),
|
||||
url('./font/fontello.ttf?32756715') format('truetype'),
|
||||
url('./font/fontello.svg?32756715#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@ -298,19 +298,21 @@ body {
|
||||
</div>
|
||||
<div class="container" id="icons">
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe800</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-picture"></i> <span class="i-name">icon-picture</span><span class="i-code">0xe801</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-off"></i> <span class="i-name">icon-off</span><span class="i-code">0xe802</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-home"></i> <span class="i-name">icon-home</span><span class="i-code">0xe803</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-font"></i> <span class="i-name">icon-font</span><span class="i-code">0xe800</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe801</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-picture"></i> <span class="i-name">icon-picture</span><span class="i-code">0xe802</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-off"></i> <span class="i-name">icon-off</span><span class="i-code">0xe803</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe804</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-home"></i> <span class="i-name">icon-home</span><span class="i-code">0xe804</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe805</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt"></i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text"></i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text"></i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf114"><i class="demo-icon icon-folder-empty"></i> <span class="i-name">icon-folder-empty</span><span class="i-code">0xf114</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf1dd"><i class="demo-icon icon-paragraph"></i> <span class="i-name">icon-paragraph</span><span class="i-code">0xf1dd</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
||||
|
Binary file not shown.
@ -6,15 +6,17 @@
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="cog" unicode="" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="font" unicode="" d="M405 538l-95-251q18 0 76-1t89-1q11 0 32 1-48 141-102 252z m-405-617l1 44q13 4 31 7t32 6 28 8 25 17 17 28l132 344 156 404h72q4-8 6-12l114-268q19-43 60-144t63-153q9-19 33-80t40-94q11-26 19-32 11-9 49-17t47-11q4-22 4-32 0-3-1-8t0-7q-35 0-106 5t-107 4q-42 0-120-4t-99-4q0 24 2 43l73 16q1 0 7 1t9 2 8 3 9 4 6 4 5 6 1 8q0 9-17 54t-40 99-24 56l-251 1q-14-32-43-109t-28-91q0-12 8-21t24-14 27-7 32-5 23-2q1-11 1-32 0-5-1-16-33 0-98 6t-97 6q-5 0-15-3t-12-2q-45-8-105-8z" horiz-adv-x="928.6" />
|
||||
|
||||
<glyph glyph-name="picture" unicode="" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
||||
<glyph glyph-name="cog" unicode="" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="off" unicode="" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="picture" unicode="" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
||||
|
||||
<glyph glyph-name="home" unicode="" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="off" unicode="" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="plus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||
<glyph glyph-name="home" unicode="" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
|
||||
|
||||
<glyph glyph-name="plus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||
|
||||
@ -23,6 +25,8 @@
|
||||
<glyph glyph-name="doc-text" unicode="" d="M819 638q16-16 27-42t11-50v-642q0-23-15-38t-38-16h-750q-23 0-38 16t-16 38v892q0 23 16 38t38 16h500q22 0 49-11t42-27z m-248 136v-210h210q-5 17-12 23l-175 175q-6 7-23 12z m215-853v572h-232q-23 0-38 16t-16 37v233h-429v-858h715z m-572 483q0 7 5 12t13 5h393q8 0 13-5t5-12v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36z m411-125q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z m0-143q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="folder-empty" unicode="" d="M857 118v393q0 22-15 38t-38 15h-393q-23 0-38 16t-16 38v36q0 22-15 38t-38 15h-179q-22 0-38-15t-16-38v-536q0-22 16-38t38-16h679q22 0 38 16t15 38z m72 393v-393q0-51-37-88t-88-37h-679q-51 0-88 37t-37 88v536q0 51 37 88t88 37h179q51 0 88-37t37-88v-18h375q51 0 88-37t37-88z" horiz-adv-x="928.6" />
|
||||
|
||||
<glyph glyph-name="paragraph" unicode="" d="M713 745v-41q0-16-10-34t-24-18q-28 0-30-1-14-3-18-17-1-6-1-36v-643q0-14-11-24t-24-10h-60q-14 0-24 10t-10 24v680h-80v-680q0-14-9-24t-25-10h-60q-14 0-24 10t-10 24v277q-82 7-137 33-70 33-107 100-36 65-36 145 0 92 50 159 49 66 116 89 62 21 233 21h267q14 0 24-10t10-24z" horiz-adv-x="714.3" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
Before (image error) Size: 4.4 KiB After (image error) Size: 5.3 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -73,6 +73,9 @@ aside.sidebar{
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.right{
|
||||
float: right;
|
||||
}
|
||||
|
||||
/********************
|
||||
* NAVI *
|
||||
@ -405,9 +408,10 @@ h1 .version-number{
|
||||
** MODAL **
|
||||
*************/
|
||||
|
||||
.modal{
|
||||
.modalWindow{
|
||||
position:fixed;
|
||||
display: none;
|
||||
display: block;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
@ -419,10 +423,6 @@ h1 .version-number{
|
||||
background: #FFF;
|
||||
background: rgba(255,255,255,0.9);
|
||||
}
|
||||
.modal.show{
|
||||
display: block;
|
||||
z-index: 99999;
|
||||
}
|
||||
.modalInner{
|
||||
position: relative;
|
||||
max-width: 350px;
|
||||
@ -1264,7 +1264,7 @@ label .help, .label .help{
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 2px #ddd;
|
||||
}
|
||||
.editor button{
|
||||
.editor button, .editor a{
|
||||
position: relative;
|
||||
border-radius: 0px;
|
||||
padding:10px;
|
||||
@ -1297,15 +1297,16 @@ label .help, .label .help{
|
||||
.buttonset .secondary--block{
|
||||
display: inline-block;
|
||||
}
|
||||
.editor button.button--secondary{
|
||||
.editor button.button--secondary, .editor a.button--secondary{
|
||||
display: inline-block;
|
||||
min-width: 40px;
|
||||
max-width: 40px;
|
||||
background: #fff;
|
||||
border: 1px solid #eee;
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
.editor button.button--secondary:hover{
|
||||
.editor button.button--secondary:hover, .editor a.button--secondary:hover, .editor a.button-secondary:focus, .editor a.button--secondary:active{
|
||||
background: #e0474c;
|
||||
border: 1px solid #e0474c;
|
||||
color: #eee;
|
||||
@ -1332,6 +1333,273 @@ label .help, .label .help{
|
||||
.desktop{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/****************
|
||||
** BLOX EDITOR **
|
||||
****************/
|
||||
|
||||
.blox-body{
|
||||
padding: 18px 20px
|
||||
}
|
||||
.blox{
|
||||
padding: 1px 20px;
|
||||
line-height: 1.5em;
|
||||
font-size: 16px;
|
||||
}
|
||||
.blox p{
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.blox-editor{
|
||||
position: relative;
|
||||
}
|
||||
.blox-editor:hover{
|
||||
background: #f9f8f6;
|
||||
}
|
||||
.blox-buttons{
|
||||
background: #fff;
|
||||
}
|
||||
.blox-editor textarea{
|
||||
font-family: arial;
|
||||
line-height: 1.5em;
|
||||
font-size: 16px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
box-sizing: border-blox;
|
||||
}
|
||||
.blox-editor button{
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
margin: 2px 2px 10px 0;
|
||||
padding: 6px 12px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.blox-editor button.edit{
|
||||
background: #e0474c;
|
||||
color: #f9f8f6;
|
||||
border: 1px solid #e0474c;
|
||||
}
|
||||
.blox-editor button.edit:hover{
|
||||
background: #cc4146;
|
||||
border: 1px solid #cc4146;
|
||||
}
|
||||
.blox-editor button.cancel{
|
||||
background: #fff;
|
||||
border: 1px solid #eee;
|
||||
color: #444;
|
||||
}
|
||||
.blox-editor button.cancel:hover{
|
||||
background: #e0474c;
|
||||
border: 1px solid #e0474c;
|
||||
color: #eee;
|
||||
}
|
||||
.blox-editor button.delete{
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
font-weight: 700;
|
||||
background: #fff;
|
||||
color: #fff;
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.blox-editor:hover button.delete{
|
||||
background: #e0474c;
|
||||
color: #fff;
|
||||
}
|
||||
.blox-editor button.delete:hover{
|
||||
background: #cc4146;
|
||||
}
|
||||
.blox-editor button.edit:disabled, .blox-editor button.cancel:disabled{
|
||||
background: #eee;
|
||||
color: #444;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.blox:hover{
|
||||
// background: #f9f8f6;
|
||||
}
|
||||
.visible{
|
||||
display: block;
|
||||
}
|
||||
.hidden{
|
||||
display: none;
|
||||
}
|
||||
.fade-editor-enter-active{
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-editor-enter{
|
||||
opacity: 0.3;
|
||||
}
|
||||
.format-bar{
|
||||
padding: 20px;
|
||||
width:100%;
|
||||
}
|
||||
.format-bar .blox-editor{
|
||||
display: inline;
|
||||
}
|
||||
.format-item{
|
||||
margin-right: 2px;
|
||||
padding: 5px;
|
||||
background: #70c1b3;
|
||||
color: #fff;
|
||||
display: inline;
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.format-item:hover{
|
||||
background: #66b0a3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/************************
|
||||
** BLOX EDITOR CONTENT **
|
||||
************************/
|
||||
|
||||
.blox h1, .blox h2, .blox h3, .blox h4, .blox h5, .blox h6{ font-weight: 700; line-height: 1em; }
|
||||
.blox h1{ font-size: 2.2em; margin: 0.6em 0 0.6em; }
|
||||
.blox h2{ font-size: 1.6em; margin: 1.3em 0 0.6em; }
|
||||
.blox h3{ font-size: 1.3em; margin: 1.2em 0 0.6em; }
|
||||
.blox h4{ font-size: 1.1em; margin: 1.2em 0 0.6em; }
|
||||
.blox h5{ font-size: 1em; margin: 1.2em 0 0.6em; }
|
||||
.blox h6{ font-size: 1em; font-style: italic; font-weight:300; margin: 1em 0 0.6em; }
|
||||
.title input{
|
||||
font-size: 2.2em;
|
||||
font-weight: 700;
|
||||
padding: 20px;
|
||||
}
|
||||
.blox pre,.blox code{
|
||||
white-space: pre;
|
||||
color: #333;
|
||||
background: #f9f8f6;
|
||||
}
|
||||
.blox code{
|
||||
display: inline-block;
|
||||
padding: 0 0.5em;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.4em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.blox code.hljs{
|
||||
background: transparent;
|
||||
}
|
||||
.blox pre{
|
||||
padding: 10px;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
border-left: 4px solid #e0474c;
|
||||
}
|
||||
.blox table{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.blox thead{
|
||||
border-bottom: 1px solid #e0474c;
|
||||
border-top: 1px solid #e0474c;
|
||||
background: #f9f8f6;
|
||||
font-weight: 700;
|
||||
}
|
||||
.blox tbody{}
|
||||
.blox tr{}
|
||||
.blox tr:nth-child(odd){ }
|
||||
.blox tr:nth-child(even){ background-color:#f9f8f6; }
|
||||
.blox td{ padding: 5px;}
|
||||
.blox th{ padding: 10px 0;}
|
||||
.blox dl{
|
||||
border-top: 1px solid #e0474c;
|
||||
border-bottom: 1px solid #e0474c;
|
||||
padding: 0.5em 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.blox dt, .blox dd{
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 3px 5px;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.blox dt{
|
||||
font-weight: 700;
|
||||
}
|
||||
.blox dt::after{
|
||||
content: ":";
|
||||
}
|
||||
.blox dd{
|
||||
width: 80%
|
||||
}
|
||||
.blox ul, .blox ol{
|
||||
padding-left: 0px;
|
||||
margin-left: 18px;
|
||||
}
|
||||
.blox blockquote{
|
||||
border-left: 4px solid #e0474c;
|
||||
background: #f9f8f6;
|
||||
position: relative;
|
||||
font-style: italic;
|
||||
font-family: serif;
|
||||
border-left: 4px solid #e0474c;
|
||||
background: #f9f8f6;
|
||||
margin: 12px 0;
|
||||
padding: 5px;
|
||||
}
|
||||
.blox blockquote:before {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 20px;
|
||||
color: #ccc;
|
||||
content: open-quote;
|
||||
font-size: 4em;
|
||||
}
|
||||
.blox blockquote p{
|
||||
margin-left: 50px;
|
||||
}
|
||||
img{}
|
||||
sup{}
|
||||
cite{}
|
||||
abbr{}
|
||||
hr{}
|
||||
.blox ul.TOC,.blox .TOC ul{
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.blox .TOC li:before{
|
||||
content: "\2192";
|
||||
margin-left: -7px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
.blox .TOC li.h1:before{
|
||||
content: "";
|
||||
}
|
||||
.blox ul.TOC{
|
||||
background: #f9f8f6;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
.blox li.h1{
|
||||
font-weight: 700;
|
||||
}
|
||||
.blox li.h2,.blox li.h3,.blox li.h4,.blox li.h5,.blox li.h6{
|
||||
font-weight: 400;
|
||||
padding-left: 25px;
|
||||
}
|
||||
.blox a, .blox a:link, .blox a:visited{
|
||||
text-decoration: none;
|
||||
color: #e0474c;
|
||||
}
|
||||
.blox a:focus, .blox a:hover, .blox a:active{
|
||||
text-decoration: underline;
|
||||
}
|
||||
.blox .TOC li:before{ color: #bbb; }
|
||||
|
||||
|
||||
@media only screen and (min-width: 600px) {
|
||||
header.headline{
|
||||
padding: 0px 20px;
|
||||
@ -1354,7 +1622,7 @@ label .help, .label .help{
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.editor button{
|
||||
.editor button, .editor a.button--secondary{
|
||||
min-width: 150px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@ -1412,7 +1680,7 @@ label .help, .label .help{
|
||||
.buttonset .secondary--block{
|
||||
display: inline-block;
|
||||
}
|
||||
.editor button.button--secondary{
|
||||
.editor button.button--secondary, .editor a.button--secondary{
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: auto;
|
||||
@ -1577,7 +1845,7 @@ label .help, .label .help{
|
||||
right: auto;
|
||||
max-width: 900px;
|
||||
}
|
||||
.editor button{
|
||||
.editor button, .editor a{
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
45
system/author/editor/editor-blox.twig
Normal file
45
system/author/editor/editor-blox.twig
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends 'layouts/layoutBlox.twig' %}
|
||||
{% block title %}Visual Content Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="formWrapper">
|
||||
|
||||
<section id="blox">
|
||||
|
||||
<div class="blox-title"></div>
|
||||
|
||||
<div class="blox-body">
|
||||
|
||||
{% for id, block in content %}
|
||||
{% if loop.first %}
|
||||
<content-block class="title" :body="false">
|
||||
<div class="blox title" @click="setData( $event, 'text-markdown')" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
|
||||
</content-block>
|
||||
{% else %}
|
||||
<content-block :body="true">
|
||||
<div class="blox" @click="setData( $event, 'textarea-markdown' )" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
|
||||
</content-block>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<content-block :body="true" v-for="newBlock in newBlocks"><div class="blox" @click="setData( $event, 'textarea-markdown' )" :data-id="newBlock.id" :id="newBlock.blockId" v-html="newBlock.content"></div></content-block>
|
||||
|
||||
<div class="format-bar">
|
||||
<content-block :body="false">
|
||||
<button class="format-item" @click="setData( $event, 'textarea-markdown' )" data-id="99999" id="blox-99999"><i class="icon-font"></i></button>
|
||||
</content-block>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
{% include 'editor/publish-controller.twig' %}
|
||||
|
||||
<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
38
system/author/editor/editor-raw.twig
Normal file
38
system/author/editor/editor-raw.twig
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends 'layouts/layoutEditor.twig' %}
|
||||
{% block title %}Raw Content Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="formWrapper">
|
||||
|
||||
<div id="editor" class="editor">
|
||||
<form action="#" v-cloak>
|
||||
|
||||
<section>
|
||||
|
||||
<fieldset>
|
||||
<div class="large" :class="{'error' : errors.title}">
|
||||
<label for="title">Title*</label>
|
||||
<input @input="changeContent" id="title" name="title" type="text" v-model="form.title" value="{{title}}" required />
|
||||
<span class="error" v-if="errors.title">${ errors.title }</span>
|
||||
</div>
|
||||
<div class="large" :class="{'error' : errors.content}">
|
||||
<label for="content">Content*</label>
|
||||
<textarea id="content" v-model="form.content" @input="changeContent" required>{{ content }}</textarea>
|
||||
<span class="error" v-if="errors.content">${ errors.content }</span>
|
||||
</div>
|
||||
<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% include 'editor/publish-controller.twig' %}
|
||||
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,59 +0,0 @@
|
||||
{% extends 'layouts/layoutEditor.twig' %}
|
||||
{% block title %}Content{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="formWrapper">
|
||||
|
||||
<div id="editor" class="editor">
|
||||
<form action="#" v-cloak>
|
||||
|
||||
<section>
|
||||
|
||||
<fieldset>
|
||||
<div class="large" :class="{'error' : errors.title}">
|
||||
<label for="title">Title*</label>
|
||||
<input @input="changeContent" id="title" name="title" type="text" v-model="form.title" value="{{title}}" required />
|
||||
<span class="error" v-if="errors.title">${ errors.title }</span>
|
||||
</div>
|
||||
<div class="large" :class="{'error' : errors.content}">
|
||||
<label for="content">Content*</label>
|
||||
<textarea id="content" v-model="form.content" @input="changeContent" @keyup.enter="submit" required>{{ content }}</textarea>
|
||||
<span class="error" v-if="errors.content">${ errors.content }</span>
|
||||
</div>
|
||||
<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
|
||||
<div class="buttonset" id="publishController" data-published="{{ item.published }}" data-drafted="{{ item.drafted }}">
|
||||
<div v-if="errors.message" class="message error">${ errors.message }</div>
|
||||
<button @click.prevent="saveDraft" id="draft" :class="draftResult" :disabled="draftDisabled"><span class="desktop">Save </span>Draft</button><button @click.prevent="publishDraft" id="publish" :class="publishResult" :disabled="publishDisabled">Publish</button>
|
||||
<div class="secondary">
|
||||
<div class="secondary--block">
|
||||
<button @click.prevent="depublishArticle" class="button--secondary button--secondary__hightlight" :disabled="publishStatus"><span class="desktop">${publishLabel}</span><span class="mobile">ON</span></button><button @click.prevent="showModal" class="button--secondary"><span class="desktop">delete</span><span class="mobile">X</mobile></button>
|
||||
</div>
|
||||
<!--
|
||||
<div class="secondary--block">
|
||||
<button class="button--secondary">md help</button>
|
||||
<button class="button--secondary">raw mode</button>
|
||||
<div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
<div id="modalWindow" :class="modalWindow">
|
||||
<div class="modalInner">
|
||||
<div @click="hideModal" id="closeModal" class="closeModal">X</div>
|
||||
<h2>Delete page</h2>
|
||||
<p>Do you really want to delete this page? Please confirm.</p>
|
||||
<button @click.prevent="deleteArticle" class="large" :class="deleteResult" :disabled="deleteDisabled">Delete Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
26
system/author/editor/publish-controller.twig
Normal file
26
system/author/editor/publish-controller.twig
Normal file
@ -0,0 +1,26 @@
|
||||
<div class="editor">
|
||||
<div class="buttonset" id="publishController" data-published="{{ item.published }}" data-drafted="{{ item.drafted }}">
|
||||
<div id="publishController" v-cloak>
|
||||
<div v-if="errors.message" class="message error">${ errors.message }</div>
|
||||
<button v-if="raw" @click.prevent="saveDraft" id="draft" :class="draftResult" :disabled="draftDisabled"><span class="desktop">Save </span>Draft</button><button @click.prevent="publishDraft" id="publish" :class="publishResult" :disabled="publishDisabled">Publish</button>
|
||||
<div class="secondary">
|
||||
<div class="secondary--block">
|
||||
<button @click.prevent="depublishArticle" class="button--secondary button--secondary__hightlight" :disabled="publishStatus"><span class="desktop">${publishLabel}</span><span class="mobile">ON</span></button>
|
||||
<a v-if="visual" href="{{ base_url }}/tm/content/raw{{item.urlRelWoF}}" class="button--secondary">raw mode</a>
|
||||
<a v-if="raw" href="{{ base_url }}/tm/content/visual{{item.urlRelWoF}}" class="button--secondary">visual mode</a>
|
||||
<button @click.prevent="showModal" class="button--secondary"><span class="desktop">delete</span><span class="mobile">X</mobile></button>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div v-if="modalWindow" id="modalWindow" class="modalWindow">
|
||||
<div class="modalInner">
|
||||
<div @click="hideModal" id="closeModal" class="closeModal">X</div>
|
||||
<h2>Delete page</h2>
|
||||
<p>Do you really want to delete this page? Please confirm.</p>
|
||||
<button @click.prevent="deleteArticle" class="large" :class="deleteResult" :disabled="deleteDisabled">Delete Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
303
system/author/js/vue-blox.js
Normal file
303
system/author/js/vue-blox.js
Normal file
@ -0,0 +1,303 @@
|
||||
const contentComponent = Vue.component('content-block', {
|
||||
props: ['body'],
|
||||
data: function () {
|
||||
return {
|
||||
preview: 'visible',
|
||||
edit: false,
|
||||
compmarkdown: '',
|
||||
componentType: '',
|
||||
disabled: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData: function()
|
||||
{
|
||||
self = this;
|
||||
|
||||
if(self.$root.$data.freeze == false && self.$root.$data.blockType != '')
|
||||
{
|
||||
self.$root.$data.freeze = true;
|
||||
this.preview = 'hidden';
|
||||
this.edit = true;
|
||||
this.compmarkdown = self.$root.$data.blockMarkdown;
|
||||
this.componentType = self.$root.$data.blockType;
|
||||
}
|
||||
},
|
||||
cancelBlock: function()
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
this.preview = 'visible';
|
||||
this.edit = false;
|
||||
this.componentType = false;
|
||||
self = this;
|
||||
self.$root.$data.freeze = false;
|
||||
},
|
||||
submitBlock: function(e){
|
||||
var emptyline = /^\s*$(?:\r\n?|\n)/gm;
|
||||
if(this.compmarkdown.search(emptyline) > -1)
|
||||
{
|
||||
var checkempty = this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,"");
|
||||
if(checkempty == '')
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
this.preview = 'visible';
|
||||
this.edit = false;
|
||||
this.componentType = false;
|
||||
self = this;
|
||||
self.$root.$data.freeze = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saveBlock();
|
||||
}
|
||||
}
|
||||
},
|
||||
saveBlock: function()
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
this.disabled = 'disabled';
|
||||
|
||||
var self = this;
|
||||
|
||||
self.$root.$data.freeze = true;
|
||||
|
||||
var url = self.$root.$data.root + '/api/v1/block';
|
||||
|
||||
var params = {
|
||||
'url': document.getElementById("path").value,
|
||||
'markdown': this.compmarkdown,
|
||||
'block_id': self.$root.$data.blockId,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
};
|
||||
|
||||
var method = 'PUT';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(httpStatus == 400)
|
||||
{
|
||||
self.disabled = false;
|
||||
publishController.publishDisabled = false;
|
||||
}
|
||||
if(response)
|
||||
{
|
||||
self.disabled = false;
|
||||
publishController.publishDisabled = false;
|
||||
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
console.info(result.errors);
|
||||
publishController.errors.message = result.errors.markdown[0];
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.preview = 'visible';
|
||||
self.edit = false;
|
||||
self.componentType = false;
|
||||
self.$root.$data.freeze = false;
|
||||
|
||||
if(self.$root.$data.blockId == 99999)
|
||||
{
|
||||
self.$root.$data.markdown.push(result.markdown);
|
||||
self.$root.$data.newBlocks.push(result);
|
||||
|
||||
self.$root.$data.blockMarkdown = '';
|
||||
self.$root.$data.blockType = 'textarea-markdown';
|
||||
self.getData();
|
||||
}
|
||||
else
|
||||
{
|
||||
var htmlid = "blox-" + self.$root.$data.blockId;
|
||||
var html = document.getElementById(htmlid);
|
||||
html.innerHTML = result.content;
|
||||
|
||||
self.$root.$data.markdown[self.$root.$data.blockId] = result.markdown;
|
||||
|
||||
self.$root.$data.blockMarkdown = '';
|
||||
self.$root.$data.blockType = '';
|
||||
}
|
||||
|
||||
publishController.publishResult = "";
|
||||
}
|
||||
}
|
||||
}, method, url, params);
|
||||
},
|
||||
deleteBlock: function(event)
|
||||
{
|
||||
var bloxeditor = event.target.parentElement;
|
||||
var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id;
|
||||
bloxeditor.id = "delete-"+bloxid;
|
||||
|
||||
this.disabled = 'disabled';
|
||||
|
||||
publishController.errors.message = false;
|
||||
|
||||
var self = this;
|
||||
|
||||
var url = self.$root.$data.root + '/api/v1/block';
|
||||
|
||||
var params = {
|
||||
'url': document.getElementById("path").value,
|
||||
'block_id': bloxid,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
};
|
||||
|
||||
var method = 'DELETE';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(httpStatus == 400)
|
||||
{
|
||||
self.disabled = false;
|
||||
}
|
||||
if(response)
|
||||
{
|
||||
self.disabled = false;
|
||||
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
publishController.errors.message = result.errors;
|
||||
publishController.publishDisabled = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.edit = false;
|
||||
self.componentType = false;
|
||||
|
||||
var deleteblock = document.getElementById("delete-"+bloxid);
|
||||
deleteblock.parentElement.remove(deleteblock);
|
||||
|
||||
var blox = document.getElementsByClassName("blox");
|
||||
var length = blox.length;
|
||||
for (var i = 0; i < length; i++ ) {
|
||||
blox[i].id = "blox-" + i;
|
||||
}
|
||||
|
||||
self.$root.$data.freeze = false;
|
||||
self.$root.$data.markdown = result.markdown;
|
||||
self.$root.$data.blockMarkdown = '';
|
||||
self.$root.$data.blockType = '';
|
||||
|
||||
publishController.publishDisabled = false;
|
||||
publishController.publishResult = "";
|
||||
}
|
||||
}
|
||||
}, method, url, params);
|
||||
},
|
||||
},
|
||||
template: '<div class="blox-editor"><div><div @keyup.enter="submitBlock" @click="getData"><transition name="fade-editor"><component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="compmarkdown = $event" :is="componentType"></component></transition><div :class="preview"><slot></slot></div></div><div class="blox-buttons" v-if="edit"><button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button><button class="cancel" :disabled="disabled" @click.prevent="cancelBlock">cancel</button></div><button v-if="body" class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)">x</button></div></div>',
|
||||
})
|
||||
|
||||
const textareaComponent = Vue.component('textarea-markdown', {
|
||||
props: ['compmarkdown', 'disabled'],
|
||||
template: '<div><textarea ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea></div>',
|
||||
mounted: function(){
|
||||
autosize(document.querySelector('textarea'));
|
||||
this.$refs.markdown.focus();
|
||||
},
|
||||
methods: {
|
||||
updatemarkdown: function(event)
|
||||
{
|
||||
this.$emit('updatedMarkdown', event.target.value);
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const textComponent = Vue.component('text-markdown', {
|
||||
props: ['compmarkdown', 'disabled'],
|
||||
template: '<div><input type="text" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></div>',
|
||||
mounted: function(){
|
||||
autosize(document.querySelector('textarea'));
|
||||
this.$refs.markdown.focus();
|
||||
},
|
||||
methods: {
|
||||
updatemarkdown: function(event)
|
||||
{
|
||||
this.$emit('updatedMarkdown', event.target.value);
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const imageComponent = Vue.component('input-image', {
|
||||
template: '<div>Image component</div>',
|
||||
})
|
||||
|
||||
const tableComponent = Vue.component('input-table', {
|
||||
template: '<div>table component</div>',
|
||||
})
|
||||
|
||||
let editor = new Vue({
|
||||
delimiters: ['${', '}'],
|
||||
el: '#blox',
|
||||
components: {
|
||||
'content-component': contentComponent,
|
||||
'textarea-component': textareaComponent,
|
||||
'text-component': textComponent,
|
||||
'image-component': imageComponent,
|
||||
'table-component': tableComponent,
|
||||
},
|
||||
data: {
|
||||
root: document.getElementById("main").dataset.url,
|
||||
markdown: false,
|
||||
blockId: false,
|
||||
blockType: false,
|
||||
blockMarkdown: '',
|
||||
freeze: false,
|
||||
newBlocks: [],
|
||||
},
|
||||
mounted: function(){
|
||||
|
||||
publishController.visual = true;
|
||||
editor.draftDisabled = true;
|
||||
|
||||
var url = this.root + '/api/v1/article/markdown';
|
||||
|
||||
var params = {
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
};
|
||||
|
||||
var method = 'POST';
|
||||
|
||||
var self = this;
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(httpStatus == 400)
|
||||
{
|
||||
}
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
self.errors.title = result.errors;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.markdown = result.data;
|
||||
}
|
||||
}
|
||||
}, method, url, params);
|
||||
},
|
||||
methods: {
|
||||
setData: function(event, blocktype, body)
|
||||
{
|
||||
this.blockId = event.currentTarget.dataset.id;
|
||||
this.blockType = blocktype;
|
||||
this.blockMarkdown = this.markdown[this.blockId];
|
||||
},
|
||||
}
|
||||
});
|
@ -2,185 +2,22 @@ let editor = new Vue({
|
||||
delimiters: ['${', '}'],
|
||||
el: '#editor',
|
||||
data: {
|
||||
root: document.getElementById("main").dataset.url,
|
||||
errors: false,
|
||||
form: {
|
||||
title: this.title = document.getElementById("title").value,
|
||||
content: this.title = document.getElementById("content").value,
|
||||
url: document.getElementById("path").value,
|
||||
csrf_name: document.getElementById("csrf_name").value,
|
||||
csrf_value: document.getElementById("csrf_value").value,
|
||||
},
|
||||
errors:{
|
||||
title: false,
|
||||
content: false,
|
||||
message: false,
|
||||
},
|
||||
modalWindow: "modal hide",
|
||||
draftDisabled: true,
|
||||
publishDisabled: document.getElementById("publishController").dataset.drafted ? false : true,
|
||||
deleteDisabled: false,
|
||||
draftResult: "",
|
||||
publishResult: "",
|
||||
deleteResult: "",
|
||||
publishStatus: document.getElementById("publishController").dataset.published ? false : true,
|
||||
publishLabel: document.getElementById("publishController").dataset.published ? "online" : "offline",
|
||||
title: document.getElementById("title").value,
|
||||
content: document.getElementById("content").value,
|
||||
}
|
||||
},
|
||||
mounted: function(){
|
||||
autosize(document.querySelector('textarea'));
|
||||
publishController.raw = true;
|
||||
},
|
||||
methods: {
|
||||
submit: function(e){
|
||||
/* count submits and react to line before. */
|
||||
},
|
||||
changeContent: function(e){
|
||||
this.draftDisabled = false;
|
||||
this.publishDisabled = false;
|
||||
this.draftResult = "";
|
||||
this.publishResult = "";
|
||||
},
|
||||
publishDraft: function(e){
|
||||
var self = this;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
self.publishResult = "load";
|
||||
self.publishDisabled = "disabled";
|
||||
|
||||
var url = this.root + '/api/v1/article/publish';
|
||||
var method = 'POST';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(httpStatus == 400)
|
||||
{
|
||||
self.publishDisabled = false;
|
||||
self.publishResult = "fail";
|
||||
self.errors.message = "You are probably logged out. Please backup your changes, login and then try again."
|
||||
}
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
self.publishDisabled = false;
|
||||
self.publishResult = "fail";
|
||||
|
||||
if(result.errors.title){ self.errors.title = result.errors.title[0] };
|
||||
if(result.errors.content){ self.errors.content = result.errors.content[0] };
|
||||
if(result.errors.message){ self.errors.message = result.errors.message };
|
||||
}
|
||||
else
|
||||
{
|
||||
self.draftDisabled = "disabled";
|
||||
self.publishResult = "success";
|
||||
self.publishStatus = false;
|
||||
self.publishLabel = "online";
|
||||
}
|
||||
}
|
||||
}, method, url, this.form );
|
||||
},
|
||||
saveDraft: function(e){
|
||||
|
||||
var self = this;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
self.draftDisabled = "disabled";
|
||||
self.draftResult = "load";
|
||||
|
||||
var url = this.root + '/api/v1/article';
|
||||
var method = 'PUT';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
self.draftDisabled = false;
|
||||
self.draftResult = 'fail';
|
||||
if(result.errors.title){ self.errors.title = result.errors.title[0] };
|
||||
if(result.errors.content){ self.errors.content = result.errors.content[0] };
|
||||
if(result.errors.message){ self.errors.message = result.errors.message };
|
||||
}
|
||||
else
|
||||
{
|
||||
self.draftResult = 'success';
|
||||
}
|
||||
}
|
||||
}, method, url, this.form );
|
||||
},
|
||||
depublishArticle: function(e){
|
||||
|
||||
if(this.draftDisabled == false)
|
||||
{
|
||||
this.errors.message = 'Please save your changes as draft first.';
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
self.publishStatus = "disabled";
|
||||
|
||||
var url = this.root + '/api/v1/article/unpublish';
|
||||
var method = 'DELETE';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
self.publishStatus = false;
|
||||
if(result.errors.message){ self.errors.message = result.errors.message };
|
||||
}
|
||||
else
|
||||
{
|
||||
self.publishResult = "";
|
||||
self.publishLabel = "offline";
|
||||
self.publishDisabled = false;
|
||||
}
|
||||
}
|
||||
}, method, url, this.form );
|
||||
},
|
||||
deleteArticle: function(e){
|
||||
var self = this;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
self.deleteDisabled = "disabled";
|
||||
self.deleteResult = "load";
|
||||
|
||||
var url = this.root + '/api/v1/article';
|
||||
var method = 'DELETE';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}, method, url, this.form );
|
||||
},
|
||||
showModal: function(e){
|
||||
this.modalWindow = "modal show";
|
||||
},
|
||||
hideModal: function(e){
|
||||
this.modalWindow = "modal";
|
||||
publishController.draftDisabled = false;
|
||||
publishController.publishDisabled = false;
|
||||
publishController.draftResult = "";
|
||||
publishController.publishResult = "";
|
||||
},
|
||||
}
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user