Ver código fonte

Version 1.1.1 Theme and Plugin Optimizations

Sebastian 7 anos atrás
pai
commit
c43c633cb4
100 arquivos alterados com 4886 adições e 154 exclusões
  1. 1 1
      cache/lastCache.txt
  2. 15 0
      content/2_for-writers/25-themes.md
  3. 15 0
      content/2_for-writers/30-plugins.md
  4. 0 40
      content/3_for-developers/01-quick-start.md
  5. 0 38
      content/3_for-developers/04-theme-structure.md
  6. 0 3
      content/3_for-developers/index.md
  7. 78 0
      content/3_for-theme-developers/01-quick-start.md
  8. 40 0
      content/3_for-theme-developers/02-theme-structure.md
  9. 21 0
      content/3_for-theme-developers/03-theme-meta.md
  10. 45 0
      content/3_for-theme-developers/04-asset-tags.md
  11. 0 0
      content/3_for-theme-developers/05-twig.md
  12. 0 0
      content/3_for-theme-developers/06-theme-variables/05-content.md
  13. 0 0
      content/3_for-theme-developers/06-theme-variables/08-description.md
  14. 0 0
      content/3_for-theme-developers/06-theme-variables/10-item.md
  15. 0 0
      content/3_for-theme-developers/06-theme-variables/15-breadcrumb.md
  16. 0 0
      content/3_for-theme-developers/06-theme-variables/25-navigation.md
  17. 0 0
      content/3_for-theme-developers/06-theme-variables/30-settings.md
  18. 0 0
      content/3_for-theme-developers/06-theme-variables/index.md
  19. 3 0
      content/3_for-theme-developers/index.md
  20. 40 0
      content/4_for-plugin-developers/01-intro.md
  21. 56 0
      content/4_for-plugin-developers/01-tutorial/02-cookie-consent-plugin.md
  22. 42 0
      content/4_for-plugin-developers/01-tutorial/04-create-the-structure.md
  23. 77 0
      content/4_for-plugin-developers/01-tutorial/06-write-the-yaml-file.md
  24. 156 0
      content/4_for-plugin-developers/01-tutorial/08-write-the-php-file.md
  25. 123 0
      content/4_for-plugin-developers/01-tutorial/10-add-methods.md
  26. 61 0
      content/4_for-plugin-developers/01-tutorial/12-add-twig-template.md
  27. 60 0
      content/4_for-plugin-developers/01-tutorial/14-add-default-values.md
  28. 127 0
      content/4_for-plugin-developers/01-tutorial/16-use-variables-in-twig.md
  29. 161 0
      content/4_for-plugin-developers/01-tutorial/18-make-variables-editable.md
  30. 3 0
      content/4_for-plugin-developers/01-tutorial/index.md
  31. 49 0
      content/4_for-plugin-developers/02-documentation/02-file-structure.md
  32. 67 0
      content/4_for-plugin-developers/02-documentation/04-configuration-file.md
  33. 203 0
      content/4_for-plugin-developers/02-documentation/06-field-overview.md
  34. 116 0
      content/4_for-plugin-developers/02-documentation/08-basic-php-file.md
  35. 68 0
      content/4_for-plugin-developers/02-documentation/10-event-overview.md
  36. 183 0
      content/4_for-plugin-developers/02-documentation/10-method-overview.md
  37. 56 0
      content/4_for-plugin-developers/02-documentation/12-routes.md
  38. 50 0
      content/4_for-plugin-developers/02-documentation/14-middleware.md
  39. 3 0
      content/4_for-plugin-developers/02-documentation/index.md
  40. 3 0
      content/4_for-plugin-developers/index.md
  41. 9 0
      content/5_info/01-release-notes.md
  42. 0 0
      content/5_info/05-usage-and-licence.md
  43. 0 0
      content/5_info/10-Imprint-and-privacy.md
  44. 0 0
      content/5_info/15_markdown-test.md
  45. 0 0
      content/5_info/20_Übermaß.md
  46. 0 0
      content/5_info/index.md
  47. 1 2
      content/index.md
  48. BIN
      media/decouple.jpg
  49. 44 0
      plugins/analytics/analytics.php
  50. 38 0
      plugins/analytics/analytics.yaml
  51. 5 0
      plugins/analytics/templates/googleanalytics.twig
  52. 11 0
      plugins/analytics/templates/piwikanalytics.twig
  53. 8 8
      plugins/cookieconsent/cookieconsent.php
  54. 41 0
      plugins/disqus/disqus.php
  55. 15 0
      plugins/disqus/disqus.yaml
  56. 12 0
      plugins/disqus/templates/disqus.twig
  57. 2 0
      system/Assets.php
  58. 2 2
      system/Controllers/Controller.php
  59. 64 20
      system/Controllers/PageController.php
  60. 3 3
      system/Controllers/SetupController.php
  61. 1 1
      system/Events/OnBreadcrumbLoaded.php
  62. 1 1
      system/Events/OnContentArrayLoaded.php
  63. 1 1
      system/Events/OnHtmlLoaded.php
  64. 1 1
      system/Events/OnItemLoaded.php
  65. 1 1
      system/Events/OnMarkdownLoaded.php
  66. 14 0
      system/Events/OnPageReady.php
  67. 1 1
      system/Events/OnPagetreeLoaded.php
  68. 1 1
      system/Events/OnPluginsLoaded.php
  69. 1 1
      system/Events/OnSettingsLoaded.php
  70. 183 3
      system/Extensions/ParsedownExtension.php
  71. 1 2
      system/Middleware/ValidationErrorsMiddleware.php
  72. 1 2
      system/Models/Field.php
  73. 1 1
      system/Models/Validation.php
  74. 44 10
      system/author/js/author.js
  75. 1 1
      system/author/setup.twig
  76. 4 4
      system/settings.php
  77. 5 6
      system/system.php
  78. 7 0
      system/vendor/autoload.php
  79. 445 0
      system/vendor/composer/ClassLoader.php
  80. 21 0
      system/vendor/composer/LICENSE
  81. 9 0
      system/vendor/composer/autoload_classmap.php
  82. 11 0
      system/vendor/composer/autoload_files.php
  83. 15 0
      system/vendor/composer/autoload_namespaces.php
  84. 22 0
      system/vendor/composer/autoload_psr4.php
  85. 70 0
      system/vendor/composer/autoload_real.php
  86. 148 0
      system/vendor/composer/autoload_static.php
  87. 892 0
      system/vendor/composer/installed.json
  88. 3 0
      system/vendor/container-interop/container-interop/.gitignore
  89. 20 0
      system/vendor/container-interop/container-interop/LICENSE
  90. 148 0
      system/vendor/container-interop/container-interop/README.md
  91. 15 0
      system/vendor/container-interop/container-interop/composer.json
  92. 114 0
      system/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md
  93. 158 0
      system/vendor/container-interop/container-interop/docs/ContainerInterface.md
  94. 259 0
      system/vendor/container-interop/container-interop/docs/Delegate-lookup-meta.md
  95. 60 0
      system/vendor/container-interop/container-interop/docs/Delegate-lookup.md
  96. BIN
      system/vendor/container-interop/container-interop/docs/images/interoperating_containers.png
  97. BIN
      system/vendor/container-interop/container-interop/docs/images/priority.png
  98. BIN
      system/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png
  99. 15 0
      system/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php
  100. 15 0
      system/vendor/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php

+ 1 - 1
cache/lastCache.txt

@@ -1 +1 @@
-1517945363
+1519575954

+ 15 - 0
content/2_for-writers/25-themes.md

@@ -0,0 +1,15 @@
+# Plugins
+
+As of version 1.1.0, TYPEMILL supports plugins. Right now all plugins are delivered with TYPEMILL by default. But as the number of plugins grows, there will probably be a central place to download and install the plugins in the future.
+
+For now, you can simply activate and setup the plugins in the TYPEMILL setup. If you want to change the settings for the plugins after the setup, then you can edit everything in the file `/settings/settings.yaml`.
+
+Plugins are right now:
+
+* **Cookie Consent**: This plugin adds a cookie consent banner to your website. You can change the colors and the text in the plugin-settings.
+* **Analytics**: This plugin adds an analytics-script for Matomo (Piwik) and Google Analytics.
+* **Disqus**: This plugin adds the comment system discuss to your content pages. It does not add the script to folder-pages, but only to real content pages.
+
+You can always activate and deactivate a plugin. But you can also delete a plugin from the plugin-folder, if you care about your webspace.
+
+Plugins have versions. If a plugin is outdated, then it is visible in the TYPEMILL setup. This is not too helpful right now because you can only visit the public setup page one time. But in near future, you will have access to the setup in the admin area with a login.

+ 15 - 0
content/2_for-writers/30-plugins.md

@@ -0,0 +1,15 @@
+# Plugins
+
+As of version 1.1.0, TYPEMILL supports plugins. Right now all plugins are delivered with TYPEMILL by default. But as the number of plugins grows, there will probably be a central place to download and install the plugins in the future.
+
+For now, you can simply activate and setup the plugins in the TYPEMILL setup. If you want to change the settings for the plugins after the setup, then you can edit everything in the file `/settings/settings.yaml`.
+
+Plugins are right now:
+
+* **Cookie Consent**: This plugin adds a cookie consent banner to your website. You can change the colors and the text in the plugin-settings.
+* **Analytics**: This plugin adds an analytics-script for Matomo (Piwik) and Google Analytics.
+* **Disqus**: This plugin adds the comment system discuss to your content pages. It does not add the script to folder-pages, but only to real content pages.
+
+You can always activate and deactivate a plugin. But you can also delete a plugin from the plugin-folder, if you care about your webspace.
+
+Plugins have versions. If a plugin is outdated, then it is visible in the TYPEMILL setup. This is not too helpful right now because you can only visit the public setup page one time. But in near future, you will have access to the setup in the admin area with a login.

+ 0 - 40
content/3_for-developers/01-quick-start.md

@@ -1,40 +0,0 @@
-# Quick Start for Developers
-
-So you are a pro 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. Change the  theme in the `settings.yaml`.
-
-## 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 static startpage.
-
-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.
-
-## Twig
-
-The template language for TYPEMILL is Twig. 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. Just print the variables out to get some insights.
-
-- `navigation`: This variable is a multidimensional array of objects. Each object represents a file or a folder. You can use a Twig-macro to create a navigation with this variable in your template. A macro in TWIG is the same as a recursive function in PHP. 
-- `content`: This variable holds the HTML content of the markdown file for a specific page.
-- `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.
-- `item`: This variable is an object of the actual page. It contains all the details like name, url, path, chapter as well as the next and previous items for a pagination. And guess what? The `navigation` variable is just an array of these item-objects (with a bit less information).
-- `settings`: You will find all the settings in this variable (like the navigation-title, the author, the theme and the copyright information).
-- `description`: This are the first 150 characters of the content of a page. You can use this for the meta description.
-
-If you did not understand everything, then please read the full developer manual. In less than one hour you can develop with TYPEMILL like a pro.
-
-Happy coding!
-

+ 0 - 38
content/3_for-developers/04-theme-structure.md

@@ -1,38 +0,0 @@
-# Theme Structure
-
-TYPEMILL requires a minimal structure and a small set of mandatory files:
-
-- **/myThemeFolder**: 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 static startpage. It is optional.
-  - **myThemeFolder.jpg**: A preview picture of your theme. It is mandatory. The file must be named exactly like the theme folder. Minimum width is 400px.
-
-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.
-    - layout.twig  // layout for all other pages, includes navigation, header and footer
-    - navigation.twig
-    - header.twig
-    - footer.twig
-- cover.twig // template for the static startpage. Extends the layoutStart.twig.
-- index.twig // template for all other pages. Extends the layout.twig
-- 404.twig // template for not found page. Extends the layout.twig.
-
-If you have not read the chapter about Twig templates yet, please read it now. It does not only describe the template language Twig, but also the template hierarchy (how templates can be included and extended).

+ 0 - 3
content/3_for-developers/index.md

@@ -1,3 +0,0 @@
-# Hello Developers!
-
-If you love **lightweight systems**, stupid **simple theming** and the template language **Twig**, then TYPEMILL is for you!

+ 78 - 0
content/3_for-theme-developers/01-quick-start.md

@@ -0,0 +1,78 @@
+# Quick Start for Theme-Developers
+
+You are a professional 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 change the theme for typemill in the `settings.yaml`, that you will find in the settings-folder. Simply add the name of the theme there.
+
+## 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 static startpage.
+
+And there are two other files your theme should have:
+
+* `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. It should 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
+````
+
+## 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 name, url, path, chapter as well as the next and previous items for a pagination. And guess what? The `navigation` variable is just an array, that hold many of these item-objects (with a bit less informations).
+- `content`: This variable holds the HTML content of the markdown file. Just print it out.
+- `description`: This are the first 150 characters 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 varialbe, you will find all the settings like the navigation-title, the author, the theme 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 CSS and JavaScript into 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 are styled correctly. 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 with TYPEMILL like a pro.
+
+Happy coding!

+ 40 - 0
content/3_for-theme-developers/02-theme-structure.md

@@ -0,0 +1,40 @@
+# Theme Structure
+
+TYPEMILL requires a minimal structure and a small set of mandatory files:
+
+- **/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.

+ 21 - 0
content/3_for-theme-developers/03-theme-meta.md

@@ -0,0 +1,21 @@
+# Theme Meta with YAML
+
+You can add some meta-information to your theme with a small YAML-file. The YAML-file must have the same name as your theme folder. The yaml-file has some useful standard-information and 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
+```
+
+Why is this useful? Some reasons:
+
+* The user can see, if his version is outdated and then update his theme to a actual version.
+* You can add a licence, that is displayed in the setup.
+* You can add your name as an author-name. This is also displayed in the setup.
+* You can add a homepage with further informations and theme-updates, that is also displayed in the setup.
+
+A YAML-file with meta-informations is not mandatory, so your theme will work without it. But it is highly recommended.

+ 45 - 0
content/3_for-theme-developers/04-asset-tags.md

@@ -0,0 +1,45 @@
+# 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. And you should add them to your theme. Otherwise, those plugins won't work:
+
+- `{{ assets.renderCSS() }}`
+- `{{ assets.renderJS() }}`
+
+You should follow best practice and 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 a good practice 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>   
+````
+

+ 0 - 0
content/3_for-developers/03-twig.md → content/3_for-theme-developers/05-twig.md


+ 0 - 0
content/3_for-developers/05-theme-variables/05-content.md → content/3_for-theme-developers/06-theme-variables/05-content.md


+ 0 - 0
content/3_for-developers/05-theme-variables/08-description.md → content/3_for-theme-developers/06-theme-variables/08-description.md


+ 0 - 0
content/3_for-developers/05-theme-variables/10-item.md → content/3_for-theme-developers/06-theme-variables/10-item.md


+ 0 - 0
content/3_for-developers/05-theme-variables/15-breadcrumb.md → content/3_for-theme-developers/06-theme-variables/15-breadcrumb.md


+ 0 - 0
content/3_for-developers/05-theme-variables/25-navigation.md → content/3_for-theme-developers/06-theme-variables/25-navigation.md


+ 0 - 0
content/3_for-developers/05-theme-variables/30-settings.md → content/3_for-theme-developers/06-theme-variables/30-settings.md


+ 0 - 0
content/3_for-developers/05-theme-variables/index.md → content/3_for-theme-developers/06-theme-variables/index.md


+ 3 - 0
content/3_for-theme-developers/index.md

@@ -0,0 +1,3 @@
+# Hello Theme-Developers!
+
+If you love **lightweight systems**, stupid **simple theming** and the template language **Twig**, then TYPEMILL is for you!

+ 40 - 0
content/4_for-plugin-developers/01-intro.md

@@ -0,0 +1,40 @@
+# 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!! 

+ 56 - 0
content/4_for-plugin-developers/01-tutorial/02-cookie-consent-plugin.md

@@ -0,0 +1,56 @@
+# 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.

+ 42 - 0
content/4_for-plugin-developers/01-tutorial/04-create-the-structure.md

@@ -0,0 +1,42 @@
+#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.

+ 77 - 0
content/4_for-plugin-developers/01-tutorial/06-write-the-yaml-file.md

@@ -0,0 +1,77 @@
+# 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.

+ 156 - 0
content/4_for-plugin-developers/01-tutorial/08-write-the-php-file.md

@@ -0,0 +1,156 @@
+# 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.
+

+ 123 - 0
content/4_for-plugin-developers/01-tutorial/10-add-methods.md

@@ -0,0 +1,123 @@
+# 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.

+ 61 - 0
content/4_for-plugin-developers/01-tutorial/12-add-twig-template.md

@@ -0,0 +1,61 @@
+# 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. 
+

+ 60 - 0
content/4_for-plugin-developers/01-tutorial/14-add-default-values.md

@@ -0,0 +1,60 @@
+# 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.

+ 127 - 0
content/4_for-plugin-developers/01-tutorial/16-use-variables-in-twig.md

@@ -0,0 +1,127 @@
+# 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.

+ 161 - 0
content/4_for-plugin-developers/01-tutorial/18-make-variables-editable.md

@@ -0,0 +1,161 @@
+# 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!!!!

+ 3 - 0
content/4_for-plugin-developers/01-tutorial/index.md

@@ -0,0 +1,3 @@
+# 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.

+ 49 - 0
content/4_for-plugin-developers/02-documentation/02-file-structure.md

@@ -0,0 +1,49 @@
+# 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
+````

+ 67 - 0
content/4_for-plugin-developers/02-documentation/04-configuration-file.md

@@ -0,0 +1,67 @@
+# 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.

+ 203 - 0
content/4_for-plugin-developers/02-documentation/06-field-overview.md

@@ -0,0 +1,203 @@
+# 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
+
+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
+````
+
+## 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
+````

+ 116 - 0
content/4_for-plugin-developers/02-documentation/08-basic-php-file.md

@@ -0,0 +1,116 @@
+# 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.

+ 68 - 0
content/4_for-plugin-developers/02-documentation/10-event-overview.md

@@ -0,0 +1,68 @@
+# 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.
+

+ 183 - 0
content/4_for-plugin-developers/02-documentation/10-method-overview.md

@@ -0,0 +1,183 @@
+# 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. 
+

+ 56 - 0
content/4_for-plugin-developers/02-documentation/12-routes.md

@@ -0,0 +1,56 @@
+# 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');
+	}
+}
+````

+ 50 - 0
content/4_for-plugin-developers/02-documentation/14-middleware.md

@@ -0,0 +1,50 @@
+# 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.

+ 3 - 0
content/4_for-plugin-developers/02-documentation/index.md

@@ -0,0 +1,3 @@
+# 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**. 

+ 3 - 0
content/4_for-plugin-developers/index.md

@@ -0,0 +1,3 @@
+# 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**.

+ 9 - 0
content/4_info/01-release-notes.md → content/5_info/01-release-notes.md

@@ -20,6 +20,15 @@ Now visit your startpage and click on the new setup-button. The button will dire
 
 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 (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.

+ 0 - 0
content/4_info/05-usage-and-licence.md → content/5_info/05-usage-and-licence.md


+ 0 - 0
content/4_info/10-Imprint-and-privacy.md → content/5_info/10-Imprint-and-privacy.md


+ 0 - 0
content/4_info/15_markdown-test.md → content/5_info/15_markdown-test.md


+ 0 - 0
content/4_info/20_Übermaß.md → content/5_info/20_Übermaß.md


+ 0 - 0
content/4_info/index.md → content/5_info/index.md


+ 1 - 2
content/index.md

@@ -1,2 +1 @@
-TYPEMILL is a simple system to **publish** your **text-work** (markdown) as a **website**. Nearly no technical skills are required. Just download, learn and start.
-
+TYPEMILL is a content management system for **writers** and their **text-work** (markdown). TYPEMILL is simple, lightweight (< 1mb) and open source. Just download and start.

BIN
media/decouple.jpg


+ 44 - 0
plugins/analytics/analytics.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Plugins\Analytics;
+
+use \Typemill\Plugin;
+
+class Analytics 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()
+	{
+		/* get Twig Instance and add the cookieconsent template-folder to the path */
+		$twig 	= $this->getTwig();					
+		$loader = $twig->getLoader();
+		$loader->addPath(__DIR__ . '/templates');
+
+		$analyticSettings = $this->settings['settings']['plugins']['analytics'];
+	
+		/* fetch the template, render it with twig and add javascript with settings */
+		if($analyticSettings['tool'] == 'piwik')
+		{
+			$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
+		}
+		elseif($analyticSettings['tool'] == 'google')
+		{
+			$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
+			$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
+		}
+	}
+}

+ 38 - 0
plugins/analytics/analytics.yaml

@@ -0,0 +1,38 @@
+name: Analytics
+version: 1.0.0
+description: Integrate Piwik or Google Analytics Script
+author: Sebastian Schürmanns
+homepage: http://typemill.net
+licence: MIT
+
+settings:
+  tool: none
+
+forms:
+  fields:
+
+    tool:
+      type: radio
+      label: Choose Your Tool
+      options:
+        none: None
+        piwik: Piwik
+        google: Google Analytics
+
+    piwik_url:
+      type: text
+      label: Piwik URL
+      help: Add the URL to your piwik installation without protocol like this: my-site.com.
+      placeholder: 'url like my-piwik-installation.com'
+
+    piwik_id:
+      type: number
+      label: Piwik Site-ID
+      help: You can find the id in Piwik under configuration and tracking code.
+      placeholder: 'simple number like 8'
+
+    google_id:
+      type: text
+      label: Google Tracking ID
+      help: You can find the tracking id in google under property. It starts with UA-
+      placeholder: 'UA-12345-6'

+ 5 - 0
plugins/analytics/templates/googleanalytics.twig

@@ -0,0 +1,5 @@
+window.dataLayer = window.dataLayer || [];
+function gtag(){dataLayer.push(arguments);}
+gtag('js', new Date());
+
+gtag('config', '{{ google_id }}');

+ 11 - 0
plugins/analytics/templates/piwikanalytics.twig

@@ -0,0 +1,11 @@
+var _paq = _paq || [];
+_paq.push(['trackPageView']);
+_paq.push(['enableLinkTracking']);
+
+(function(){
+	var u="//{{ settings.plugins.analytics.piwik_url }}/";
+	_paq.push(['setTrackerUrl', u+'piwik.php']);
+	_paq.push(['setSiteId', '{{ settings.plugins.analytics.piwik_id }}']);
+	var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
+	g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
+})();

+ 8 - 8
plugins/cookieconsent/cookieconsent.php

@@ -1,19 +1,19 @@
 <?php
 
-namespace Plugins\cookieconsent;
+namespace Plugins\CookieConsent;
 
 use \Typemill\Plugin;
 
-class cookieconsent extends Plugin
+class CookieConsent extends Plugin
 {
 	protected $settings;
 	
     public static function getSubscribedEvents()
     {
-		return [
-			'onSettingsLoaded'		=> ['onSettingsLoaded',0],
-			'onTwigLoaded' 			=> ['onTwigLoaded', 0]
-		];
+		return array(
+			'onSettingsLoaded'		=> 'onSettingsLoaded',
+			'onTwigLoaded' 			=> 'onTwigLoaded'
+		);
     }
 	
 	public function onSettingsLoaded($settings)
@@ -22,7 +22,7 @@ class cookieconsent extends Plugin
 	}
 	
 	public function onTwigLoaded()
-	{	
+	{
 		/* add external CSS and JavaScript */
 		$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
 		$this->addJS('/cookieconsent/public/cookieconsent.min.js');
@@ -33,6 +33,6 @@ class cookieconsent extends Plugin
 		$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));	
+		$this->addInlineJS($twig->fetch('/cookieconsent.twig', $this->settings));
 	}
 }

+ 41 - 0
plugins/disqus/disqus.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Plugins\Disqus;
+
+use \Typemill\Plugin;
+
+class Disqus extends Plugin
+{
+	protected $settings;
+
+    public static function getSubscribedEvents()
+    {
+		return [];
+    }
+	
+	public function onSettingsLoaded($settings)
+	{
+		$this->settings = $settings->getData();
+	}
+	
+	public function onTwigLoaded()
+	{
+		/* get Twig Instance and add the cookieconsent template-folder to the path */
+		$twig 	= $this->getTwig();					
+		$loader = $twig->getLoader();
+		$loader->addPath(__DIR__ . '/templates');
+
+		$analyticSettings = $this->settings['settings']['plugins']['analytics'];
+	
+		/* fetch the template, render it with twig and add javascript with settings */
+		if($analyticSettings['tool'] == 'piwik')
+		{
+			$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
+		}
+		elseif($analyticSettings['tool'] == 'google')
+		{
+			$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
+			$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
+		}
+	}
+}

+ 15 - 0
plugins/disqus/disqus.yaml

@@ -0,0 +1,15 @@
+name: Disqus
+version: 1.0.0
+description: Integrate Disqus into Typemill.
+author: Sebastian Schürmanns
+homepage: http://typemill.net
+licence: MIT
+
+forms:
+  fields:
+
+    shortname:
+      type: text
+      label: Disqus-Shortname
+      help: You can find the shortname in your disqus-settings
+      placeholder: 'YOUR SHORTNAME'

+ 12 - 0
plugins/disqus/templates/disqus.twig

@@ -0,0 +1,12 @@
+var disqus_config = function () {
+	this.page.url = {{ item.urlAbs }} ;
+};
+
+(function() {
+	var d = document, s = d.createElement('script');
+        
+	s.src = 'https://{{ settings.plugins.disqus.shortname }}.disqus.com/embed.js';
+        
+	s.setAttribute('data-timestamp', +new Date());
+	(d.head || d.body).appendChild(s);
+})();

+ 2 - 0
system/Assets.php

@@ -69,6 +69,8 @@ class Assets
 			return $this->baseUrl . '/plugins' . $path;
 		}
 		
+		return $path;
+		
 		if(fopen($path, "r"))
 		{
 			return $path;

+ 2 - 2
system/Controllers/Controller.php

@@ -4,7 +4,7 @@ namespace Typemill\Controllers;
 
 /* Use the slim-container */
 use Interop\Container\ContainerInterface;
-use Typemill\Events\RenderPageEvent;
+use Typemill\Events\OnPageReady;
 
 abstract class Controller
 {
@@ -17,7 +17,7 @@ abstract class Controller
 	
 	protected function render($response, $route, $data)
 	{
-		$data = $this->c->dispatcher->dispatch('onPageRendered', new RenderPageEvent($data))->getData();
+		$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
 		
 		return $this->c->view->render($response, $route, $data);
 	}

+ 64 - 20
system/Controllers/PageController.php

@@ -9,17 +9,19 @@ use Typemill\Models\WriteYaml;
 use \Symfony\Component\Yaml\Yaml;
 use Typemill\Models\VersionCheck;
 use Typemill\Models\Helpers;
-use Typemill\Events\LoadPagetreeEvent;
-use Typemill\Events\LoadBreadcrumbEvent;
-use Typemill\Events\LoadItemEvent;
-use Typemill\Events\LoadMarkdownEvent;
-use Typemill\Events\ParseHtmlEvent;
+use Typemill\Events\OnPagetreeLoaded;
+use Typemill\Events\OnBreadcrumbLoaded;
+use Typemill\Events\OnItemloaded;
+use Typemill\Events\OnMarkdownLoaded;
+use Typemill\Events\OnContentArrayLoaded;
+use Typemill\Events\OnHtmlLoaded;
 use Typemill\Extensions\ParsedownExtension;
 
 class PageController extends Controller
 {
 	public function index($request, $response, $args)
 	{
+	
 		/* Initiate Variables */
 		$structure		= false;
 		$contentHTML	= false;
@@ -52,7 +54,7 @@ class PageController extends Controller
 				{
 					$content = '<h1>No Content</h1><p>Your content folder is empty.</p>'; 
 
-					$this->render($response, '/index.twig', [ 'content' => $content ]);
+					$this->render($response, '/index.twig', array( 'content' => $content ));
 				}
 				elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400))
 				{
@@ -66,7 +68,7 @@ class PageController extends Controller
 			}
 			
 			/* dispatch event and let others manipulate the structure */
-			$structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new LoadPagetreeEvent($structure))->getData();
+			$structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData();
 		}
 		catch (Exception $e)
 		{
@@ -104,11 +106,11 @@ class PageController extends Controller
 			
 			/* get breadcrumb for page */
 			$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
-			$breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new LoadBreadcrumbEvent($breadcrumb))->getData();
-						
+			$breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
+			
 			/* add the paging to the item */
 			$item = Folder::getPagingForItem($structure, $item);
-			$item = $this->c->dispatcher->dispatch('onItemLoaded', new LoadItemEvent($item))->getData();
+			$item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
 			
 			/* check if url is a folder. If so, check if there is an index-file in that folder */
 			if($item->elementType == 'folder' && $item->index)
@@ -124,20 +126,49 @@ class PageController extends Controller
 			$contentMD = isset($filePath) ? file_get_contents($filePath) : false;
 		}
 		
-		$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new LoadMarkdownEvent($contentMD))->getData();
+		$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
 		
 		/* initialize parsedown */
-		$Parsedown 		= new ParsedownExtension();
+		$parsedown 		= new ParsedownExtension();
 
-		/* parse markdown-file to html-string */
-		$contentHTML 	= $Parsedown->text($contentMD);
-		$contentHTML 	= $this->c->dispatcher->dispatch('onHtmlParsed', new ParseHtmlEvent($contentHTML))->getData();
+		/* parse markdown-file to content-array */
+		$contentArray 	= $parsedown->text($contentMD);
+		$contentArray 	= $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData();
+	
+		/* get the first image from content array */
+		$firstImage		= $this->getFirstImage($contentArray);
+		
+		/* parse markdown-content-array to content-string */
+		$contentHTML	= $parsedown->markup($contentArray);
+		$contentHTML 	= $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
 		
-		$excerpt		= substr($contentHTML,0,200);
+		/* create excerpt from content */
+		$excerpt		= substr($contentHTML,0,320);
 		$excerpt		= explode("</h1>", $excerpt);
+		
+		/* extract title from excerpt */
 		$title			= isset($excerpt[0]) ? strip_tags($excerpt[0]) : $settings['title'];
+		
+		/* create description from excerpt */
 		$description	= isset($excerpt[1]) ? strip_tags($excerpt[1]) : false;
-		$description 	= $description ? trim(preg_replace('/\s+/', ' ', $description)) : false;
+		if($description)
+		{
+			$description 	= trim(preg_replace('/\s+/', ' ', $description));
+			$lastSpace 		= strrpos($description, ' ');
+			$description 	= substr($description, 0, $lastSpace);
+		}
+		
+		/* get url and alt-tag for first image, if exists */
+		if($firstImage)
+		{	
+			preg_match('#\((.*?)\)#', $firstImage, $img_url);
+			if($img_url[1])
+			{
+				preg_match('#\[(.*?)\]#', $firstImage, $img_alt);
+				
+				$firstImage = array('img_url' => $base_url . $img_url[1], 'img_alt' => $img_alt[1]);
+			}
+		}
 		
 		/* 
 			$timer['topiccontroller']=microtime(true);
@@ -146,10 +177,10 @@ class PageController extends Controller
 		*/
 		
 		$route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig';
-
-		$this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url ));
+		
+		$this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url, 'image' => $firstImage ));
 	}
-	
+
 	protected function getCachedStructure($cache)
 	{
 		return $cache->getCache('cache', 'structure.txt');
@@ -198,4 +229,17 @@ class PageController extends Controller
 			}
 		}
 	}
+	
+	protected function getFirstImage(array $contentBlocks)
+	{
+		foreach($contentBlocks as $block)
+		{
+			if($block['element']['name'] == 'p' && substr($block['element']['text'], 0, 2) == '![' )
+			{
+				return $block['element']['text'];
+			}
+		}
+		
+		return false;
+	}
 }

+ 3 - 3
system/Controllers/SetupController.php

@@ -10,7 +10,7 @@ class SetupController extends Controller
 {
 	public function setup($request, $response, $args)
 	{
-		$settings 	= $this->c->get('settings');
+		$settings 	= $this->c->get('settings');		
 		$themes 	= $this->getThemes();
 		$copyright	= $this->getCopyright();
 		
@@ -125,7 +125,7 @@ class SetupController extends Controller
 				else
 				{					
 					/* now fetch the original plugin settings from the plugin folder to get the field definitions */
-					$pluginOriginalSettings = \Typemill\settings::getPluginSettings($pluginName);
+					$pluginOriginalSettings = \Typemill\Settings::getPluginSettings($pluginName);
 					
 					if($pluginOriginalSettings)
 					{
@@ -166,7 +166,7 @@ class SetupController extends Controller
 			/* if everything is valid, add plugin settings to base settings again */
 			$appSettings['plugins'] = $pluginSettings;
 						
-			/* store updated settings */			
+			/* store updated settings */
 			\Typemill\Settings::updateSettings($appSettings);
 			
 			unset($_SESSION['old']);

+ 1 - 1
system/Events/LoadBreadcrumbEvent.php → system/Events/OnBreadcrumbLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for breadcrumb.
  */
  
-class LoadBreadcrumbEvent extends BaseEvent
+class OnBreadcrumbLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/RenderPageEvent.php → system/Events/OnContentArrayLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for the page rendering data.
  */
 
-class RenderPageEvent extends BaseEvent
+class OnContentArrayLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/ParseHtmlEvent.php → system/Events/OnHtmlLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for html page.
  */
 
-class ParseHtmlEvent extends BaseEvent
+class OnHtmlLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/LoadItemEvent.php → system/Events/OnItemLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for item.
  */
  
-class LoadItemEvent extends BaseEvent
+class OnItemLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/LoadMarkdownEvent.php → system/Events/OnMarkdownLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for markdown.
  */
 
-class LoadMarkdownEvent extends BaseEvent
+class OnMarkdownLoaded extends BaseEvent
 {
 
 }

+ 14 - 0
system/Events/OnPageReady.php

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

+ 1 - 1
system/Events/LoadPagetreeEvent.php → system/Events/OnPagetreeLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for the page tree.
  */
  
-class LoadPagetreeEvent extends BaseEvent
+class OnPagetreeLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/LoadPluginsEvent.php → system/Events/OnPluginsLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for the folder structure.
  */
  
-class LoadPluginsEvent extends BaseEvent
+class OnPluginsLoaded extends BaseEvent
 {
 
 }

+ 1 - 1
system/Events/LoadSettingsEvent.php → system/Events/OnSettingsLoaded.php

@@ -8,7 +8,7 @@ use Symfony\Component\EventDispatcher\Event;
  * Event for settings
  */
  
-class LoadSettingsEvent extends BaseEvent
+class OnSettingsLoaded extends BaseEvent
 {
 
 }

+ 183 - 3
system/Extensions/ParsedownExtension.php

@@ -25,9 +25,17 @@ class ParsedownExtension extends \ParsedownExtra
         # split text into lines
         $lines = explode("\n", $text);
 
-        # iterate through lines to identify blocks
-        $markup = $this->lines($lines);
-
+        # iterate through lines to identify blocks and return array of content
+        $blocks = $this->getContentArray($lines);
+		
+		return $blocks;
+	}
+	
+	function markup($blocks)
+	{
+		# iterate through array of content and get markup
+		$markup = $this->getMarkup($blocks);
+		
         # trim line breaks
         $markup = trim($markup, "\n");
 		
@@ -146,4 +154,176 @@ class ParsedownExtension extends \ParsedownExtra
 		
 		return $markup;
 	}
+	
+	
+    #
+    # Blocks
+    #
+
+    protected function getContentArray(array $lines)
+    {
+        $CurrentBlock = null;
+
+        foreach ($lines as $line)
+        {
+            if (chop($line) === '')
+            {
+                if (isset($CurrentBlock))
+                {
+                    $CurrentBlock['interrupted'] = true;
+                }
+
+                continue;
+            }
+
+            if (strpos($line, "\t") !== false)
+            {
+                $parts = explode("\t", $line);
+
+                $line = $parts[0];
+
+                unset($parts[0]);
+
+                foreach ($parts as $part)
+                {
+                    $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
+
+                    $line .= str_repeat(' ', $shortage);
+                    $line .= $part;
+                }
+            }
+
+            $indent = 0;
+
+            while (isset($line[$indent]) and $line[$indent] === ' ')
+            {
+                $indent ++;
+            }
+
+            $text = $indent > 0 ? substr($line, $indent) : $line;
+
+            # ~
+
+            $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+            # ~
+
+            if (isset($CurrentBlock['continuable']))
+            {
+                $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $CurrentBlock = $Block;
+
+                    continue;
+                }
+                else
+                {
+                    if ($this->isBlockCompletable($CurrentBlock['type']))
+                    {
+                        $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+                    }
+                }
+            }
+
+            # ~
+
+            $marker = $text[0];
+
+            # ~
+
+            $blockTypes = $this->unmarkedBlockTypes;
+
+            if (isset($this->BlockTypes[$marker]))
+            {
+                foreach ($this->BlockTypes[$marker] as $blockType)
+                {
+                    $blockTypes []= $blockType;
+                }
+            }
+
+            #
+            # ~
+
+            foreach ($blockTypes as $blockType)
+            {
+                $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $Block['type'] = $blockType;
+
+                    if ( ! isset($Block['identified']))
+                    {
+                        $Blocks []= $CurrentBlock;
+
+                        $Block['identified'] = true;
+                    }
+
+                    if ($this->isBlockContinuable($blockType))
+                    {
+                        $Block['continuable'] = true;
+                    }
+
+                    $CurrentBlock = $Block;
+
+                    continue 2;
+                }
+            }
+
+            # ~
+
+            if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
+            {
+                $CurrentBlock['element']['text'] .= "\n".$text;
+            }
+            else
+            {
+                $Blocks []= $CurrentBlock;
+
+                $CurrentBlock = $this->paragraph($Line);
+
+                $CurrentBlock['identified'] = true;
+            }
+        }
+
+        # ~
+
+        if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
+        {
+            $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+        }
+
+        # ~
+
+        $Blocks []= $CurrentBlock;
+
+        unset($Blocks[0]);
+
+        # ~
+		return $Blocks;
+	}
+	
+	public function getMarkup($Blocks)
+	{
+        $markup = '';
+
+        foreach ($Blocks as $Block)
+        {
+            if (isset($Block['hidden']))
+            {
+                continue;
+            }
+
+            $markup .= "\n";
+            $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
+        }
+
+        $markup .= "\n";
+
+        # ~
+
+        return $markup;
+    }	
 }

+ 1 - 2
system/Middleware/ValidationErrorsMiddleware.php

@@ -24,7 +24,6 @@ class ValidationErrorsMiddleware
 			unset($_SESSION['errors']);
 		}
 		
-		$response = $next($request, $response);
-		return $response;
+		return $next($request, $response);
 	}
 }

+ 1 - 2
system/Models/Field.php

@@ -63,7 +63,6 @@ class Field
 									'id',
 									'autocomplete',
 									'placeholder',
-									'value',
 									'size',
 									'rows',
 									'cols',
@@ -208,7 +207,7 @@ class Field
 	{
 		foreach($fieldConfigs as $key => $value)
 		{
-			if(is_string($key) && array_key_exists($key, $this->attrValues))
+			if(is_string($key) && in_array($key, $this->attrValues))
 			{
 				$this->attributeValues[$key] = $value;
 			}

+ 1 - 1
system/Models/Validation.php

@@ -202,7 +202,7 @@ class Validation
 				break;
 			case "text":
 				$v->rule('lengthMax', $fieldName, 200);
-				$v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');
+				$v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!]*$/u');
 				break;
 			case "textarea":
 				$v->rule('lengthMax', $fieldName, 1000);

+ 44 - 10
system/author/js/author.js

@@ -1,6 +1,5 @@
 (function () 
 {
-	
 	/**********************************
 	** Global HttpRequest-Function   **
 	** for AJAX-Requests             **
@@ -30,10 +29,35 @@
 		return httpRequest;
 	}
 	
-	function sendJson(callback, getPost, url, jsonData)
-	{	
-		var httpRequest = prepareHttpRequest();
-		httpRequest.open(getPost, url, true);
+	function prepareCORSRequest(method, url){
+		var xhr = prepareHttpRequest();
+		if ("withCredentials" in xhr)
+		{
+			xhr.open(method, url, true);
+		}
+		else if (typeof XDomainRequest != "undefined")
+		{
+			xhr = new XDomainRequest();
+			xhr.open(method, url);
+		}
+		else
+		{
+			xhr = null;
+		}
+		return xhr;
+	}
+	
+	function sendJson(callback, getPost, url, jsonData, cors = false)
+	{
+		if(cors)
+		{
+			var httpRequest = prepareCORSRequest(getPost, url);
+		}
+		else
+		{
+			var httpRequest = prepareHttpRequest();
+			httpRequest.open(getPost, url, true);			
+		}
 		httpRequest.onreadystatechange = function(e) 
 		{
 			if (this.readyState == 4) 
@@ -47,21 +71,31 @@
 				}
 				else
 				{
-					console.log('connection error, status is not 200');
+					console.log('connection error, status '+this.status);
 				}
 			}
 		};
 
 		// if you use application/json, make sure you collect the data in php 
 		// with file_get_contents('php://input') instead of $_POST
-		httpRequest.setRequestHeader('Content-Type', 'application/json'); 
+
 		// httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+		// httpRequest.setRequestHeader('Content-Type', 'text/plain'); 
+		httpRequest.setRequestHeader('Content-Type', 'application/json'); 
+		
+		// required by slim ???
 		httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
 		
-		httpRequest.send(JSON.stringify(jsonData));
+		if(jsonData)
+		{
+			httpRequest.send(JSON.stringify(jsonData));
+		}
+		else
+		{
+			httpRequest.send();
+		}
 	}
 	
-	
 	/* change the theme if choosen in selectbox */
 	var themeSwitch	= document.getElementById("themeSwitch");
 	if(themeSwitch)
@@ -102,7 +136,7 @@
 			{
 				return false;
 			}
-		}, getPost, url, false);
+		}, getPost, url, false, true);
 	}
 
 	function updatePluginVersions(pluginVersions)

+ 1 - 1
system/author/setup.twig

@@ -140,7 +140,7 @@
 										<option value="{{ value }}">{{ label }}</option>
 									{% endfor %}
 								</select>
-																																							
+
 							{% elseif field.type == 'radio' %}
 								
 								{% set options = field.getOptions() %}

+ 4 - 4
system/settings.php

@@ -20,7 +20,7 @@ class Settings
 		return array('settings' => $settings);
 	}
 	
-	private function getDefaultSettings()
+	private static function getDefaultSettings()
 	{
 		$rootPath = __DIR__ . DIRECTORY_SEPARATOR .  '..' . DIRECTORY_SEPARATOR;
 		
@@ -44,7 +44,7 @@ class Settings
 		];
 	}
 	
-	private function getUserSettings()
+	private static function getUserSettings()
 	{
 		$yaml = new Models\WriteYaml();
 		
@@ -110,12 +110,12 @@ class Settings
 			$yaml = new Models\WriteYaml();
 			
 			$pluginSettings = self::getPluginSettings($pluginName);
-			if($pluginSettings['settings'])
+			if(isset($pluginSettings['settings']))
 			{
 				$userSettings['plugins'][$pluginName] = $pluginSettings['settings'];
 			}
 			
-			$userSettings['plugins'][$pluginName]['active'] = true;
+			$userSettings['plugins'][$pluginName]['active'] = false;
 			
 			/* write settings to yaml */
 			$yaml->updateYaml('settings', 'settings.yaml', $userSettings);

+ 5 - 6
system/system.php

@@ -1,7 +1,7 @@
 <?php
 
-use Typemill\Events\LoadSettingsEvent;
-use Typemill\Events\LoadPluginsEvent;
+use Typemill\Events\OnSettingsLoaded;
+use Typemill\Events\OnPluginsLoaded;
 
 /************************
 * START SESSION			*
@@ -79,7 +79,7 @@ foreach($pluginNames as $pluginName)
 		$DIsettings = $container->get('settings');
 		$DIsettings->replace($pluginSettings);		
 	}
-	
+
 	/* if the plugin is activated, add routes/middleware and add plugin as event subscriber */
 	if(isset($settings['settings']['plugins'][$name]['active']) && $settings['settings']['plugins'][$name]['active'] != false)
 	{
@@ -91,11 +91,11 @@ foreach($pluginNames as $pluginName)
 }
 
 /* dispatch the event onPluginsLoaded */
-$dispatcher->dispatch('onPluginsLoaded', new LoadPluginsEvent($pluginNames));
+$dispatcher->dispatch('onPluginsLoaded', new OnPluginsLoaded($pluginNames));
 
 /* dispatch settings event and get all setting-updates from plugins */
 /* TODO, how to update the settings with a plugin? You cannot replace the full settings in the container, so you have to add settings in the container directly */
-$dispatcher->dispatch('onSettingsLoaded', new LoadSettingsEvent($settings))->getData();
+$dispatcher->dispatch('onSettingsLoaded', new OnSettingsLoaded($settings))->getData();
 
 /******************************
 * ADD DISPATCHER TO CONTAINER *
@@ -180,7 +180,6 @@ foreach($middleware as $pluginMiddleware)
 		$app->add(new $middlewareClass($middlewareParams));
 	}
 }
-
 $app->add(new \Typemill\Middleware\ValidationErrorsMiddleware($container['view']));
 $app->add(new \Typemill\Middleware\OldInputMiddleware($container['view']));
 $app->add($container->get('csrf'));

+ 7 - 0
system/vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit836351be733ecbf4741aea17e1973480::getLoader();

+ 445 - 0
system/vendor/composer/ClassLoader.php

@@ -0,0 +1,445 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    http://www.php-fig.org/psr/psr-0/
+ * @see    http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+    private $apcuPrefix;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath.'\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        $length = $this->prefixLengthsPsr4[$first][$search];
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 21 - 0
system/vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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.
+

+ 9 - 0
system/vendor/composer/autoload_classmap.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+);

+ 11 - 0
system/vendor/composer/autoload_files.php

@@ -0,0 +1,11 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+    '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
+    '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
+);

+ 15 - 0
system/vendor/composer/autoload_namespaces.php

@@ -0,0 +1,15 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+    'Valitron' => array($vendorDir . '/vlucas/valitron/src'),
+    'URLify' => array($vendorDir . '/jbroadway/urlify'),
+    'Twig_' => array($vendorDir . '/twig/twig/lib'),
+    'Pimple' => array($vendorDir . '/pimple/pimple/src'),
+    'ParsedownExtra' => array($vendorDir . '/erusev/parsedown-extra'),
+    'Parsedown' => array($vendorDir . '/erusev/parsedown'),
+);

+ 22 - 0
system/vendor/composer/autoload_psr4.php

@@ -0,0 +1,22 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+    'Typemill\\' => array($baseDir . '/system'),
+    'Twig\\' => array($vendorDir . '/twig/twig/src'),
+    'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
+    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
+    'Slim\\Views\\' => array($vendorDir . '/slim/twig-view/src'),
+    'Slim\\Flash\\' => array($vendorDir . '/slim/flash/src'),
+    'Slim\\Csrf\\' => array($vendorDir . '/slim/csrf/src'),
+    'Slim\\' => array($vendorDir . '/slim/slim/Slim'),
+    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
+    'Plugins\\' => array($baseDir . '/plugins'),
+    'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'),
+    'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
+);

+ 70 - 0
system/vendor/composer/autoload_real.php

@@ -0,0 +1,70 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit836351be733ecbf4741aea17e1973480
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInit836351be733ecbf4741aea17e1973480', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInit836351be733ecbf4741aea17e1973480', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require_once __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInit836351be733ecbf4741aea17e1973480::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        if ($useStaticLoader) {
+            $includeFiles = Composer\Autoload\ComposerStaticInit836351be733ecbf4741aea17e1973480::$files;
+        } else {
+            $includeFiles = require __DIR__ . '/autoload_files.php';
+        }
+        foreach ($includeFiles as $fileIdentifier => $file) {
+            composerRequire836351be733ecbf4741aea17e1973480($fileIdentifier, $file);
+        }
+
+        return $loader;
+    }
+}
+
+function composerRequire836351be733ecbf4741aea17e1973480($fileIdentifier, $file)
+{
+    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+        require $file;
+
+        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+    }
+}

+ 148 - 0
system/vendor/composer/autoload_static.php

@@ -0,0 +1,148 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit836351be733ecbf4741aea17e1973480
+{
+    public static $files = array (
+        '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
+        '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
+    );
+
+    public static $prefixLengthsPsr4 = array (
+        'T' => 
+        array (
+            'Typemill\\' => 9,
+            'Twig\\' => 5,
+        ),
+        'S' => 
+        array (
+            'Symfony\\Component\\Yaml\\' => 23,
+            'Symfony\\Component\\EventDispatcher\\' => 34,
+            'Slim\\Views\\' => 11,
+            'Slim\\Flash\\' => 11,
+            'Slim\\Csrf\\' => 10,
+            'Slim\\' => 5,
+        ),
+        'P' => 
+        array (
+            'Psr\\Http\\Message\\' => 17,
+            'Psr\\Container\\' => 14,
+            'Plugins\\' => 8,
+        ),
+        'I' => 
+        array (
+            'Interop\\Container\\' => 18,
+        ),
+        'F' => 
+        array (
+            'FastRoute\\' => 10,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'Typemill\\' => 
+        array (
+            0 => __DIR__ . '/../../..' . '/system',
+        ),
+        'Twig\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/twig/twig/src',
+        ),
+        'Symfony\\Component\\Yaml\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/yaml',
+        ),
+        'Symfony\\Component\\EventDispatcher\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
+        ),
+        'Slim\\Views\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/slim/twig-view/src',
+        ),
+        'Slim\\Flash\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/slim/flash/src',
+        ),
+        'Slim\\Csrf\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/slim/csrf/src',
+        ),
+        'Slim\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/slim/slim/Slim',
+        ),
+        'Psr\\Http\\Message\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/http-message/src',
+        ),
+        'Psr\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/container/src',
+        ),
+        'Plugins\\' => 
+        array (
+            0 => __DIR__ . '/../../..' . '/plugins',
+        ),
+        'Interop\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container',
+        ),
+        'FastRoute\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/nikic/fast-route/src',
+        ),
+    );
+
+    public static $prefixesPsr0 = array (
+        'V' => 
+        array (
+            'Valitron' => 
+            array (
+                0 => __DIR__ . '/..' . '/vlucas/valitron/src',
+            ),
+        ),
+        'U' => 
+        array (
+            'URLify' => 
+            array (
+                0 => __DIR__ . '/..' . '/jbroadway/urlify',
+            ),
+        ),
+        'T' => 
+        array (
+            'Twig_' => 
+            array (
+                0 => __DIR__ . '/..' . '/twig/twig/lib',
+            ),
+        ),
+        'P' => 
+        array (
+            'Pimple' => 
+            array (
+                0 => __DIR__ . '/..' . '/pimple/pimple/src',
+            ),
+            'ParsedownExtra' => 
+            array (
+                0 => __DIR__ . '/..' . '/erusev/parsedown-extra',
+            ),
+            'Parsedown' => 
+            array (
+                0 => __DIR__ . '/..' . '/erusev/parsedown',
+            ),
+        ),
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInit836351be733ecbf4741aea17e1973480::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit836351be733ecbf4741aea17e1973480::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInit836351be733ecbf4741aea17e1973480::$prefixesPsr0;
+
+        }, null, ClassLoader::class);
+    }
+}

+ 892 - 0
system/vendor/composer/installed.json

@@ -0,0 +1,892 @@
+[
+    {
+        "name": "psr/http-message",
+        "version": "1.0.1",
+        "version_normalized": "1.0.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/http-message.git",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2016-08-06T14:39:51+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Http\\Message\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common interface for HTTP messages",
+        "homepage": "https://github.com/php-fig/http-message",
+        "keywords": [
+            "http",
+            "http-message",
+            "psr",
+            "psr-7",
+            "request",
+            "response"
+        ]
+    },
+    {
+        "name": "twig/twig",
+        "version": "v1.35.0",
+        "version_normalized": "1.35.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/twigphp/Twig.git",
+            "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f",
+            "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "require-dev": {
+            "psr/container": "^1.0",
+            "symfony/debug": "~2.7",
+            "symfony/phpunit-bridge": "~3.3@dev"
+        },
+        "time": "2017-09-27T18:06:46+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.35-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Twig_": "lib/"
+            },
+            "psr-4": {
+                "Twig\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD-3-Clause"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com",
+                "homepage": "http://fabien.potencier.org",
+                "role": "Lead Developer"
+            },
+            {
+                "name": "Armin Ronacher",
+                "email": "armin.ronacher@active-4.com",
+                "role": "Project Founder"
+            },
+            {
+                "name": "Twig Team",
+                "homepage": "http://twig.sensiolabs.org/contributors",
+                "role": "Contributors"
+            }
+        ],
+        "description": "Twig, the flexible, fast, and secure template language for PHP",
+        "homepage": "http://twig.sensiolabs.org",
+        "keywords": [
+            "templating"
+        ]
+    },
+    {
+        "name": "slim/twig-view",
+        "version": "2.3.0",
+        "version_normalized": "2.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/slimphp/Twig-View.git",
+            "reference": "f6ff5ec3a24e11866376b8ffa235fbbb7e1d1301"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/f6ff5ec3a24e11866376b8ffa235fbbb7e1d1301",
+            "reference": "f6ff5ec3a24e11866376b8ffa235fbbb7e1d1301",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.5.0",
+            "psr/http-message": "^1.0",
+            "twig/twig": "^1.18|^2.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.8|^5.7"
+        },
+        "time": "2017-09-20T19:47:37+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Slim\\Views\\": "src"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Josh Lockhart",
+                "email": "hello@joshlockhart.com",
+                "homepage": "http://joshlockhart.com"
+            }
+        ],
+        "description": "Slim Framework 3 view helper built on top of the Twig 2 templating component",
+        "homepage": "http://slimframework.com",
+        "keywords": [
+            "framework",
+            "slim",
+            "template",
+            "twig",
+            "view"
+        ]
+    },
+    {
+        "name": "slim/flash",
+        "version": "0.4.0",
+        "version_normalized": "0.4.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/slimphp/Slim-Flash.git",
+            "reference": "9aaff5fded3b54f4e519ec3d4ac74d3d1f2cbbbc"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/slimphp/Slim-Flash/zipball/9aaff5fded3b54f4e519ec3d4ac74d3d1f2cbbbc",
+            "reference": "9aaff5fded3b54f4e519ec3d4ac74d3d1f2cbbbc",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.5.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.0"
+        },
+        "time": "2017-10-22T10:35:05+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Slim\\Flash\\": "src"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Josh Lockhart",
+                "email": "hello@joshlockhart.com",
+                "homepage": "http://joshlockhart.com"
+            }
+        ],
+        "description": "Slim Framework Flash message service provider",
+        "homepage": "http://slimframework.com",
+        "keywords": [
+            "flash",
+            "framework",
+            "message",
+            "provider",
+            "slim"
+        ]
+    },
+    {
+        "name": "erusev/parsedown",
+        "version": "1.6.4",
+        "version_normalized": "1.6.4.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/erusev/parsedown.git",
+            "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/erusev/parsedown/zipball/fbe3fe878f4fe69048bb8a52783a09802004f548",
+            "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.8.35"
+        },
+        "time": "2017-11-14T20:44:03+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Parsedown": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Emanuil Rusev",
+                "email": "hello@erusev.com",
+                "homepage": "http://erusev.com"
+            }
+        ],
+        "description": "Parser for Markdown.",
+        "homepage": "http://parsedown.org",
+        "keywords": [
+            "markdown",
+            "parser"
+        ]
+    },
+    {
+        "name": "erusev/parsedown-extra",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/erusev/parsedown-extra.git",
+            "reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
+            "reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c",
+            "shasum": ""
+        },
+        "require": {
+            "erusev/parsedown": "~1.4"
+        },
+        "time": "2015-11-01T10:19:22+00:00",
+        "type": "library",
+        "installation-source": "source",
+        "autoload": {
+            "psr-0": {
+                "ParsedownExtra": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Emanuil Rusev",
+                "email": "hello@erusev.com",
+                "homepage": "http://erusev.com"
+            }
+        ],
+        "description": "An extension of Parsedown that adds support for Markdown Extra.",
+        "homepage": "https://github.com/erusev/parsedown-extra",
+        "keywords": [
+            "markdown",
+            "markdown extra",
+            "parsedown",
+            "parser"
+        ]
+    },
+    {
+        "name": "jbroadway/urlify",
+        "version": "1.1.0-stable",
+        "version_normalized": "1.1.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/jbroadway/urlify.git",
+            "reference": "99bb78cd9002d0e9ce479bb81635eb665e37e981"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/jbroadway/urlify/zipball/99bb78cd9002d0e9ce479bb81635eb665e37e981",
+            "reference": "99bb78cd9002d0e9ce479bb81635eb665e37e981",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2017-01-03T20:12:54+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "URLify": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD"
+        ],
+        "authors": [
+            {
+                "name": "Johnny Broadway",
+                "email": "johnny@johnnybroadway.com",
+                "homepage": "http://www.johnnybroadway.com/"
+            }
+        ],
+        "description": "PHP port of URLify.js from the Django project. Transliterates non-ascii characters for use in URLs.",
+        "homepage": "https://github.com/jbroadway/urlify",
+        "keywords": [
+            "encode",
+            "iconv",
+            "link",
+            "slug",
+            "translit",
+            "transliterate",
+            "transliteration",
+            "url",
+            "urlify"
+        ]
+    },
+    {
+        "name": "vlucas/valitron",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/vlucas/valitron.git",
+            "reference": "1712b04f16db5f243ddfa5ce26a9b51eb1948a36"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/vlucas/valitron/zipball/1712b04f16db5f243ddfa5ce26a9b51eb1948a36",
+            "reference": "1712b04f16db5f243ddfa5ce26a9b51eb1948a36",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.2"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "~4.8.35"
+        },
+        "time": "2017-12-20T08:03:40+00:00",
+        "type": "library",
+        "installation-source": "source",
+        "autoload": {
+            "psr-0": {
+                "Valitron": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD"
+        ],
+        "authors": [
+            {
+                "name": "Vance Lucas",
+                "email": "vance@vancelucas.com",
+                "homepage": "http://www.vancelucas.com"
+            }
+        ],
+        "description": "Simple, elegant, stand-alone validation library with NO dependencies",
+        "homepage": "http://github.com/vlucas/valitron",
+        "keywords": [
+            "valid",
+            "validation",
+            "validator"
+        ]
+    },
+    {
+        "name": "psr/container",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/container.git",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2017-02-14T16:28:37+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Container\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common Container Interface (PHP FIG PSR-11)",
+        "homepage": "https://github.com/php-fig/container",
+        "keywords": [
+            "PSR-11",
+            "container",
+            "container-interface",
+            "container-interop",
+            "psr"
+        ]
+    },
+    {
+        "name": "container-interop/container-interop",
+        "version": "1.2.0",
+        "version_normalized": "1.2.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/container-interop/container-interop.git",
+            "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
+            "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
+            "shasum": ""
+        },
+        "require": {
+            "psr/container": "^1.0"
+        },
+        "time": "2017-02-14T19:40:03+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Interop\\Container\\": "src/Interop/Container/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+        "homepage": "https://github.com/container-interop/container-interop"
+    },
+    {
+        "name": "nikic/fast-route",
+        "version": "v1.2.0",
+        "version_normalized": "1.2.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/nikic/FastRoute.git",
+            "reference": "b5f95749071c82a8e0f58586987627054400cdf6"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/nikic/FastRoute/zipball/b5f95749071c82a8e0f58586987627054400cdf6",
+            "reference": "b5f95749071c82a8e0f58586987627054400cdf6",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.4.0"
+        },
+        "time": "2017-01-19T11:35:12+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "FastRoute\\": "src/"
+            },
+            "files": [
+                "src/functions.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD-3-Clause"
+        ],
+        "authors": [
+            {
+                "name": "Nikita Popov",
+                "email": "nikic@php.net"
+            }
+        ],
+        "description": "Fast request router for PHP",
+        "keywords": [
+            "router",
+            "routing"
+        ]
+    },
+    {
+        "name": "pimple/pimple",
+        "version": "v3.2.3",
+        "version_normalized": "3.2.3.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/silexphp/Pimple.git",
+            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
+            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0",
+            "psr/container": "^1.0"
+        },
+        "require-dev": {
+            "symfony/phpunit-bridge": "^3.2"
+        },
+        "time": "2018-01-21T07:42:36+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "3.2.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Pimple": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            }
+        ],
+        "description": "Pimple, a simple Dependency Injection Container",
+        "homepage": "http://pimple.sensiolabs.org",
+        "keywords": [
+            "container",
+            "dependency injection"
+        ]
+    },
+    {
+        "name": "slim/slim",
+        "version": "3.9.2",
+        "version_normalized": "3.9.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/slimphp/Slim.git",
+            "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
+            "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
+            "shasum": ""
+        },
+        "require": {
+            "container-interop/container-interop": "^1.2",
+            "nikic/fast-route": "^1.0",
+            "php": ">=5.5.0",
+            "pimple/pimple": "^3.0",
+            "psr/container": "^1.0",
+            "psr/http-message": "^1.0"
+        },
+        "provide": {
+            "psr/http-message-implementation": "1.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.0",
+            "squizlabs/php_codesniffer": "^2.5"
+        },
+        "time": "2017-11-26T19:13:09+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Slim\\": "Slim"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Rob Allen",
+                "email": "rob@akrabat.com",
+                "homepage": "http://akrabat.com"
+            },
+            {
+                "name": "Josh Lockhart",
+                "email": "hello@joshlockhart.com",
+                "homepage": "https://joshlockhart.com"
+            },
+            {
+                "name": "Gabriel Manricks",
+                "email": "gmanricks@me.com",
+                "homepage": "http://gabrielmanricks.com"
+            },
+            {
+                "name": "Andrew Smith",
+                "email": "a.smith@silentworks.co.uk",
+                "homepage": "http://silentworks.co.uk"
+            }
+        ],
+        "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
+        "homepage": "https://slimframework.com",
+        "keywords": [
+            "api",
+            "framework",
+            "micro",
+            "router"
+        ]
+    },
+    {
+        "name": "paragonie/random_compat",
+        "version": "v2.0.11",
+        "version_normalized": "2.0.11.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/paragonie/random_compat.git",
+            "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
+            "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.2.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "4.*|5.*"
+        },
+        "suggest": {
+            "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+        },
+        "time": "2017-09-27T21:40:39+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "files": [
+                "lib/random.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Paragon Initiative Enterprises",
+                "email": "security@paragonie.com",
+                "homepage": "https://paragonie.com"
+            }
+        ],
+        "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+        "keywords": [
+            "csprng",
+            "pseudorandom",
+            "random"
+        ]
+    },
+    {
+        "name": "slim/csrf",
+        "version": "0.8.2",
+        "version_normalized": "0.8.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/slimphp/Slim-Csrf.git",
+            "reference": "e416320bc0eecdf409aefaad2889737da095ff9a"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/slimphp/Slim-Csrf/zipball/e416320bc0eecdf409aefaad2889737da095ff9a",
+            "reference": "e416320bc0eecdf409aefaad2889737da095ff9a",
+            "shasum": ""
+        },
+        "require": {
+            "paragonie/random_compat": "^1.1|^2.0",
+            "php": ">=5.5.0",
+            "psr/http-message": "^1.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.0",
+            "slim/slim": "~3.0"
+        },
+        "time": "2017-10-14T07:59:05+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Slim\\Csrf\\": "src"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Josh Lockhart",
+                "email": "hello@joshlockhart.com",
+                "homepage": "http://joshlockhart.com"
+            }
+        ],
+        "description": "Slim Framework 3 CSRF protection middleware",
+        "homepage": "http://slimframework.com",
+        "keywords": [
+            "csrf",
+            "framework",
+            "middleware",
+            "slim"
+        ]
+    },
+    {
+        "name": "symfony/yaml",
+        "version": "v2.8.34",
+        "version_normalized": "2.8.34.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/yaml.git",
+            "reference": "be720fcfae4614df204190d57795351059946a77"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/yaml/zipball/be720fcfae4614df204190d57795351059946a77",
+            "reference": "be720fcfae4614df204190d57795351059946a77",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.9"
+        },
+        "time": "2018-01-03T07:36:31+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.8-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\Yaml\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Yaml Component",
+        "homepage": "https://symfony.com"
+    },
+    {
+        "name": "symfony/event-dispatcher",
+        "version": "v3.4.4",
+        "version_normalized": "3.4.4.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/event-dispatcher.git",
+            "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/26b87b6bca8f8f797331a30b76fdae5342dc26ca",
+            "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^5.5.9|>=7.0.8"
+        },
+        "conflict": {
+            "symfony/dependency-injection": "<3.3"
+        },
+        "require-dev": {
+            "psr/log": "~1.0",
+            "symfony/config": "~2.8|~3.0|~4.0",
+            "symfony/dependency-injection": "~3.3|~4.0",
+            "symfony/expression-language": "~2.8|~3.0|~4.0",
+            "symfony/stopwatch": "~2.8|~3.0|~4.0"
+        },
+        "suggest": {
+            "symfony/dependency-injection": "",
+            "symfony/http-kernel": ""
+        },
+        "time": "2018-01-03T07:37:34+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "3.4-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\EventDispatcher\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony EventDispatcher Component",
+        "homepage": "https://symfony.com"
+    }
+]

+ 3 - 0
system/vendor/container-interop/container-interop/.gitignore

@@ -0,0 +1,3 @@
+composer.lock
+composer.phar
+/vendor/

+ 20 - 0
system/vendor/container-interop/container-interop/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 container-interop
+
+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.

+ 148 - 0
system/vendor/container-interop/container-interop/README.md

@@ -0,0 +1,148 @@
+# Container Interoperability
+
+[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop)
+[![Total Downloads](https://poser.pugx.org/container-interop/container-interop/downloads.svg)](https://packagist.org/packages/container-interop/container-interop)
+
+## Deprecation warning!
+
+Starting Feb. 13th 2017, container-interop is officially deprecated in favor of [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md).
+Container-interop has been the test-bed of PSR-11. From v1.2, container-interop directly extends PSR-11 interfaces.
+Therefore, all containers implementing container-interop are now *de-facto* compatible with PSR-11.
+
+- Projects implementing container-interop interfaces are encouraged to directly implement PSR-11 interfaces instead.
+- Projects consuming container-interop interfaces are very strongly encouraged to directly type-hint on PSR-11 interfaces, in order to be compatible with PSR-11 containers that are not compatible with container-interop.
+
+Regarding the delegate lookup feature, that is present in container-interop and not in PSR-11, the feature is actually a design pattern. It is therefore not deprecated. Documentation regarding this design pattern will be migrated from this repository into a separate website in the future.
+
+## About
+
+*container-interop* tries to identify and standardize features in *container* objects (service locators,
+dependency injection containers, etc.) to achieve interoperability.
+
+Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations.
+
+If PHP projects that provide container implementations begin to adopt these common standards, then PHP
+applications and projects that use containers can depend on the common interfaces instead of specific
+implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume
+*any* container implementation that can be adapted to these interfaces.
+
+The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being
+worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope
+this project will pave the way for one or more future PSRs.
+
+
+## Installation
+
+You can install this package through Composer:
+
+```json
+composer require container-interop/container-interop
+```
+
+The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility
+between minor versions.
+
+## Standards
+
+### Available
+
+- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php).
+[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md).
+Describes the interface of a container that exposes methods to read its entries.
+- [*Delegate lookup feature*](docs/Delegate-lookup.md).
+[Meta Document](docs/Delegate-lookup-meta.md).
+Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This
+feature lets several containers work together in a single application.
+
+### Proposed
+
+View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC)
+
+## Compatible projects
+
+### Projects implementing `ContainerInterface`
+
+- [Acclimate](https://github.com/jeremeamia/acclimate-container): Adapters for
+  Aura.Di, Laravel, Nette DI, Pimple, Symfony DI, ZF2 Service manager, ZF2
+  Dependency injection and any container using `ArrayAccess`
+- [Aura.Di](https://github.com/auraphp/Aura.Di)
+- [auryn-container-interop](https://github.com/elazar/auryn-container-interop)
+- [Burlap](https://github.com/codeeverything/burlap)
+- [Chernozem](https://github.com/pyrsmk/Chernozem)
+- [Data Manager](https://github.com/chrismichaels84/data-manager)
+- [Disco](https://github.com/bitexpert/disco)
+- [InDI](https://github.com/idealogica/indi)
+- [League/Container](http://container.thephpleague.com/)
+- [Mouf](http://mouf-php.com)
+- [Njasm Container](https://github.com/njasm/container)
+- [PHP-DI](http://php-di.org)
+- [Picotainer](https://github.com/thecodingmachine/picotainer)
+- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
+- [Pimple3-ContainerInterop](https://github.com/Sam-Burns/pimple3-containerinterop) (using Pimple v3)
+- [SitePoint Container](https://github.com/sitepoint/Container)
+- [Thruster Container](https://github.com/ThrusterIO/container) (PHP7 only)
+- [Ultra-Lite Container](https://github.com/ultra-lite/container)
+- [Unbox](https://github.com/mindplay-dk/unbox)
+- [XStatic](https://github.com/jeremeamia/xstatic)
+- [Zend\ServiceManager](https://github.com/zendframework/zend-servicemanager)
+- [Zit](https://github.com/inxilpro/Zit)
+
+### Projects implementing the *delegate lookup* feature
+
+- [Aura.Di](https://github.com/auraphp/Aura.Di)
+- [Burlap](https://github.com/codeeverything/burlap)
+- [Chernozem](https://github.com/pyrsmk/Chernozem)
+- [InDI](https://github.com/idealogica/indi)
+- [League/Container](http://container.thephpleague.com/)
+- [Mouf](http://mouf-php.com)
+- [Picotainer](https://github.com/thecodingmachine/picotainer)
+- [PHP-DI](http://php-di.org)
+- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
+- [Ultra-Lite Container](https://github.com/ultra-lite/container)
+
+### Middlewares implementing `ContainerInterface`
+
+- [Alias-Container](https://github.com/thecodingmachine/alias-container): add
+  aliases support to any container
+- [Prefixer-Container](https://github.com/thecodingmachine/prefixer-container):
+  dynamically prefix identifiers
+- [Lazy-Container](https://github.com/snapshotpl/lazy-container): lazy services
+
+### Projects using `ContainerInterface`
+
+The list below contains only a sample of all the projects consuming `ContainerInterface`. For a more complete list have a look [here](http://packanalyst.com/class?q=Interop%5CContainer%5CContainerInterface).
+
+| | Downloads |
+| --- | --- |
+| [Adroit](https://github.com/bitexpert/adroit) | ![](https://img.shields.io/packagist/dt/bitexpert/adroit.svg) |
+| [Behat](https://github.com/Behat/Behat/pull/974) | ![](https://img.shields.io/packagist/dt/behat/behat.svg) |
+| [blast-facades](https://github.com/phpthinktank/blast-facades): Minimize complexity and represent dependencies as facades. | ![](https://img.shields.io/packagist/dt/blast/facades.svg) |
+| [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di): an extension to [Silex](http://silex.sensiolabs.org/) that adds support for any *container-interop* compatible container | ![](https://img.shields.io/packagist/dt/mouf/interop.silex.di.svg) |
+| [mindplay/walkway](https://github.com/mindplay-dk/walkway): a modular request router | ![](https://img.shields.io/packagist/dt/mindplay/walkway.svg) |
+| [mindplay/middleman](https://github.com/mindplay-dk/middleman): minimalist PSR-7 middleware dispatcher | ![](https://img.shields.io/packagist/dt/mindplay/middleman.svg) |
+| [PHP-DI/Invoker](https://github.com/PHP-DI/Invoker): extensible and configurable invoker/dispatcher | ![](https://img.shields.io/packagist/dt/php-di/invoker.svg) |
+| [Prophiler](https://github.com/fabfuel/prophiler) | ![](https://img.shields.io/packagist/dt/fabfuel/prophiler.svg) |
+| [Silly](https://github.com/mnapoli/silly): CLI micro-framework | ![](https://img.shields.io/packagist/dt/mnapoli/silly.svg) |
+| [Slim v3](https://github.com/slimphp/Slim) | ![](https://img.shields.io/packagist/dt/slim/slim.svg) |
+| [Splash](http://mouf-php.com/packages/mouf/mvc.splash-common/version/8.0-dev/README.md) | ![](https://img.shields.io/packagist/dt/mouf/mvc.splash-common.svg) |
+| [Woohoo Labs. Harmony](https://github.com/woohoolabs/harmony): a flexible micro-framework | ![](https://img.shields.io/packagist/dt/woohoolabs/harmony.svg) |
+| [zend-expressive](https://github.com/zendframework/zend-expressive) | ![](https://img.shields.io/packagist/dt/zendframework/zend-expressive.svg) |
+
+
+## Workflow
+
+Everyone is welcome to join and contribute.
+
+The general workflow looks like this:
+
+1. Someone opens a discussion (GitHub issue) to suggest an interface
+1. Feedback is gathered
+1. The interface is added to a development branch
+1. We release alpha versions so that the interface can be experimented with
+1. Discussions and edits ensue until the interface is deemed stable by a general consensus
+1. A new minor version of the package is released
+
+We try to not break BC by creating new interfaces instead of editing existing ones.
+
+While we currently work on interfaces, we are open to anything that might help towards interoperability, may that
+be code, best practices, etc.

+ 15 - 0
system/vendor/container-interop/container-interop/composer.json

@@ -0,0 +1,15 @@
+{
+    "name": "container-interop/container-interop",
+    "type": "library",
+    "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+    "homepage": "https://github.com/container-interop/container-interop",
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "Interop\\Container\\": "src/Interop/Container/"
+        }
+    },
+    "require": {
+        "psr/container": "^1.0"
+    }
+}

+ 114 - 0
system/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md

@@ -0,0 +1,114 @@
+# ContainerInterface Meta Document
+
+## Introduction
+
+This document describes the process and discussions that lead to the `ContainerInterface`.
+Its goal is to explain the reasons behind each decision.
+
+## Goal
+
+The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
+container to obtain objects and parameters.
+
+By standardizing such a behavior, frameworks and libraries using the `ContainerInterface`
+could work with any compatible container.
+That would allow end users to choose their own container based on their own preferences.
+
+It is important to distinguish the two usages of a container:
+
+- configuring entries
+- fetching entries
+
+Most of the time, those two sides are not used by the same party.
+While it is often end users who tend to configure entries, it is generally the framework that fetch
+entries to build the application.
+
+This is why this interface focuses only on how entries can be fetched from a container.
+
+## Interface name
+
+The interface name has been thoroughly discussed and was decided by a vote.
+
+The list of options considered with their respective votes are:
+
+- `ContainerInterface`: +8
+- `ProviderInterface`: +2
+- `LocatorInterface`: 0
+- `ReadableContainerInterface`: -5
+- `ServiceLocatorInterface`: -6
+- `ObjectFactory`: -6
+- `ObjectStore`: -8
+- `ConsumerInterface`: -9
+
+[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)
+
+The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1).
+
+## Interface methods
+
+The choice of which methods the interface would contain was made after a statistical analysis of existing containers.
+The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681).
+
+The summary of the analysis showed that:
+
+- all containers offer a method to get an entry by its id
+- a large majority name such method `get()`
+- for all containers, the `get()` method has 1 mandatory parameter of type string
+- some containers have an optional additional argument for `get()`, but it doesn't have the same purpose between containers
+- a large majority of the containers offer a method to test if it can return an entry by its id
+- a majority name such method `has()`
+- for all containers offering `has()`, the method has exactly 1 parameter of type string
+- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()`
+- a large majority of the containers don't implement `ArrayAccess`
+
+The question of whether to include methods to define entries has been discussed in
+[issue #1](https://github.com/container-interop/container-interop/issues/1).
+It has been judged that such methods do not belong in the interface described here because it is out of its scope
+(see the "Goal" section).
+
+As a result, the `ContainerInterface` contains two methods:
+
+- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found.
+- `has()`, returning a boolean, with one mandatory string parameter.
+
+### Number of parameters in `get()` method
+
+While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with
+existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters
+as long as they are optional, because the implementation *does* satisfy the interface.
+
+This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6).
+
+### Type of the `$id` parameter
+
+The type of the `$id` parameter in `get()` and `has()` has been discussed in
+[issue #6](https://github.com/container-interop/container-interop/issues/6).
+While `string` is used in all the containers that were analyzed, it was suggested that allowing
+anything (such as objects) could allow containers to offer a more advanced query API.
+
+An example given was to use the container as an object builder. The `$id` parameter would then be an
+object that would describe how to create an instance.
+
+The conclusion of the discussion was that this was beyond the scope of getting entries from a container without
+knowing how the container provided them, and it was more fit for a factory.
+
+## Contributors
+
+Are listed here all people that contributed in the discussions or votes, by alphabetical order:
+
+- [Amy Stephen](https://github.com/AmyStephen)
+- [David Négrier](https://github.com/moufmouf)
+- [Don Gilbert](https://github.com/dongilbert)
+- [Jason Judge](https://github.com/judgej)
+- [Jeremy Lindblom](https://github.com/jeremeamia)
+- [Marco Pivetta](https://github.com/Ocramius)
+- [Matthieu Napoli](https://github.com/mnapoli)
+- [Paul M. Jones](https://github.com/pmjones)
+- [Stephan Hochdörfer](https://github.com/shochdoerfer)
+- [Taylor Otwell](https://github.com/taylorotwell)
+
+## Relevant links
+
+- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php)
+- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed)
+- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)

+ 158 - 0
system/vendor/container-interop/container-interop/docs/ContainerInterface.md

@@ -0,0 +1,158 @@
+Container interface
+===================
+
+This document describes a common interface for dependency injection containers.
+
+The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
+container to obtain objects and parameters (called *entries* in the rest of this document).
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The word `implementor` in this document is to be interpreted as someone
+implementing the `ContainerInterface` in a dependency injection-related library or framework.
+Users of dependency injections containers (DIC) are referred to as `user`.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Specification
+-----------------
+
+### 1.1 Basics
+
+- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`.
+
+- `get` takes one mandatory parameter: an entry identifier. It MUST be a string.
+  A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier
+  is not known to the container. Two successive calls to `get` with the same
+  identifier SHOULD return the same value. However, depending on the `implementor`
+  design and/or `user` configuration, different values might be returned, so
+  `user` SHOULD NOT rely on getting the same value on 2 successive calls.
+  While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations
+  MAY accept additional optional parameters.
+
+- `has` takes one unique parameter: an entry identifier. It MUST return `true`
+  if an entry identifier is known to the container and `false` if it is not.
+  `has($id)` returning true does not mean that `get($id)` will not throw an exception.
+  It does however mean that `get($id)` will not throw a `NotFoundException`.
+
+### 1.2 Exceptions
+
+Exceptions directly thrown by the container MUST implement the
+[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php).
+
+A call to the `get` method with a non-existing id SHOULD throw a
+[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php).
+
+### 1.3 Additional features
+
+This section describes additional features that MAY be added to a container. Containers are not
+required to implement these features to respect the ContainerInterface.
+
+#### 1.3.1 Delegate lookup feature
+
+The goal of the *delegate lookup* feature is to allow several containers to share entries.
+Containers implementing this feature can perform dependency lookups in other containers.
+
+Containers implementing this feature will offer a greater lever of interoperability
+with other containers. Implementation of this feature is therefore RECOMMENDED.
+
+A container implementing this feature:
+
+- MUST implement the `ContainerInterface`
+- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
+  or any possible way). The delegate container MUST implement the `ContainerInterface`.
+
+When a container is configured to use a delegate container for dependencies:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container.
+  If the entry is not part of the container, an exception should be thrown
+  (as requested by the `ContainerInterface`).
+- Calls to the `has` method should only return `true` if the entry is part of the container.
+  If the entry is not part of the container, `false` should be returned.
+- If the fetched entry has dependencies, **instead** of performing
+  the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup
+into the same container (or another container) instead of the delegate container.
+
+2. Package
+----------
+
+The interfaces and classes described as well as relevant exception are provided as part of the
+[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package.
+
+3. `Interop\Container\ContainerInterface`
+-----------------------------------------
+
+```php
+<?php
+namespace Interop\Container;
+
+use Interop\Container\Exception\ContainerException;
+use Interop\Container\Exception\NotFoundException;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface
+{
+    /**
+     * Finds an entry of the container by its identifier and returns it.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @throws NotFoundException  No entry was found for this identifier.
+     * @throws ContainerException Error while retrieving the entry.
+     *
+     * @return mixed Entry.
+     */
+    public function get($id);
+
+    /**
+     * Returns true if the container can return an entry for the given identifier.
+     * Returns false otherwise.
+     *
+     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
+     * It does however mean that `get($id)` will not throw a `NotFoundException`.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @return boolean
+     */
+    public function has($id);
+}
+```
+
+4. `Interop\Container\Exception\ContainerException`
+---------------------------------------------------
+
+```php
+<?php
+namespace Interop\Container\Exception;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerException
+{
+}
+```
+
+5. `Interop\Container\Exception\NotFoundException`
+---------------------------------------------------
+
+```php
+<?php
+namespace Interop\Container\Exception;
+
+/**
+ * No entry was found in the container.
+ */
+interface NotFoundException extends ContainerException
+{
+}
+```

+ 259 - 0
system/vendor/container-interop/container-interop/docs/Delegate-lookup-meta.md

@@ -0,0 +1,259 @@
+Delegate lookup feature Meta Document
+=====================================
+
+1. Summary
+----------
+
+This document describes the *delegate lookup feature*.
+Containers are not required to implement this feature to respect the `ContainerInterface`.
+However, containers implementing this feature will offer a greater lever of interoperability
+with other containers, allowing multiple containers to share entries in the same application.
+Implementation of this feature is therefore recommanded.
+
+2. Why Bother?
+--------------
+
+The [`ContainerInterface`](../src/Interop/Container/ContainerInterface.php) ([meta doc](ContainerInterface.md))
+standardizes how frameworks and libraries make use of a container to obtain objects and parameters.
+
+By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface`
+could work with any compatible container.
+That would allow end users to choose their own container based on their own preferences.
+
+The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same
+application. For instance, this is what the [CompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php) 
+class of [Acclimate](https://github.com/jeremeamia/acclimate-container) is designed for:
+
+![Side by side containers](images/side_by_side_containers.png)
+
+However, an instance in container 1 cannot reference an instance in container 2.
+
+It would be better if an instance of container 1 could reference an instance in container 2,
+and the opposite should be true. 
+
+![Interoperating containers](images/interoperating_containers.png)
+
+In the sample above, entry 1 in container 1 is referencing entry 3 in container 2.
+
+3. Scope
+--------
+
+### 3.1 Goals
+
+The goal of the *delegate lookup* feature is to allow several containers to share entries.
+
+4. Approaches
+-------------
+
+### 4.1 Chosen Approach
+
+Containers implementing this feature can perform dependency lookups in other containers.
+
+A container implementing this feature:
+
+- must implement the `ContainerInterface`
+- must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any
+possible way). The *delegate container* must implement the `ContainerInterface`.
+
+When a *delegate container* is configured on a container:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container.
+If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`).
+- Calls to the `has` method should only return *true* if the entry is part of the container.
+If the entry is not part of the container, *false* should be returned.
+ - Finally, the important part: if the entry we are fetching has dependencies,
+**instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup into 
+the same container (or another container) instead of the delegate container.
+
+### 4.2 Typical usage
+
+The *delegate container* will usually be a composite container. A composite container is a container that
+contains several other containers. When performing a lookup on a composite container, the inner containers are 
+queried until one container returns an entry.
+An inner container implementing the *delegate lookup feature* will return entries it contains, but if these
+entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving
+a chance to all containers to answer.
+
+Interestingly enough, the order in which containers are added in the composite container matters. Indeed,
+the first containers to be added in the composite container can "override" the entries of containers with
+lower priority.
+
+![Containers priority](images/priority.png)
+
+In the example above, "container 2" contains a controller "myController" and the controller is referencing an 
+"entityManager" entry. "Container 1" contains also an entry named "entityManager".
+Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take 
+in charge the instanciation of both entries.
+
+However, using the *delegate lookup* feature, here is what happens when we ask the composite container for the 
+"myController" instance:
+
+- The composite container asks container 1 if if contains the "myController" instance. The answer is no.
+- The composite container asks container 2 if if contains the "myController" instance. The answer is yes.
+- The composite container performs a `get` call on container 2 for the "myController" instance.
+- Container 2 sees that "myController" has a dependency on "entityManager".
+- Container 2 delegates the lookup of "entityManager" to the composite container.
+- The composite container asks container 1 if if contains the "entityManager" instance. The answer is yes.
+- The composite container performs a `get` call on container 1 for the "entityManager" instance.
+
+In the end, we get a controller instanciated by container 2 that references an entityManager instanciated
+by container 1.
+
+### 4.3 Alternative: the fallback strategy
+
+The first proposed approach we tried was to perform all the lookups in the "local" container,
+and if a lookup fails in the container, to use the delegate container. In this scenario, the
+delegate container is used in "fallback" mode.
+
+This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1).
+It was also discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and
+[here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631).
+
+Problems with this strategy:
+
+- Heavy problem regarding infinite loops
+- Unable to overload a container entry with the delegate container entry
+
+### 4.4 Alternative: force implementing an interface
+
+The first proposed approach was to develop a `ParentAwareContainerInterface` interface.
+It was proposed here: https://github.com/container-interop/container-interop/pull/8
+
+The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of
+a `setParentContainter` method:
+
+```php
+interface ParentAwareContainerInterface extends ReadableContainerInterface {
+    /**
+     * Sets the parent container associated to that container. This container will call
+     * the parent container to fetch dependencies.
+     *
+     * @param ContainerInterface $container
+     */
+    public function setParentContainer(ContainerInterface $container);
+}
+```
+
+The interface idea was first questioned by @Ocramius [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
+@Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation
+details on the class implementing the interface. 
+Then @mnapoli made a proposal for a "convention" [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079),
+this idea was further discussed until all participants in the discussion agreed to remove the interface idea
+and replace it with a "standard" feature.
+
+**Pros:**
+
+If we had had an interface, we could have delegated the registration of the delegate/composite container to the
+the delegate/composite container itself.
+For instance:
+
+```php
+$containerA = new ContainerA();
+$containerB = new ContainerB();
+
+$compositeContainer = new CompositeContainer([$containerA, $containerB]);
+
+// The call to 'setParentContainer' is delegated to the CompositeContainer
+// It is not the responsibility of the user anymore.
+class CompositeContainer {
+	...
+	
+	public function __construct($containers) {
+		foreach ($containers as $container) {
+			if ($container instanceof ParentAwareContainerInterface) {
+				$container->setParentContainer($this);
+			}
+		}
+		...
+	}
+}
+
+``` 
+
+**Cons:**
+
+Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
+Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments,
+and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface.
+
+### 4.4 Alternative: no exception case for delegate lookups
+
+Originally, the proposed wording for delegate lookup calls was:
+
+> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself.
+
+This was later replaced by:
+
+> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
+>
+> It is however allowed for containers to provide exception cases for special entries, and a way to lookup 
+> into the same container (or another container) instead of the delegate container.
+
+Exception cases have been allowed to avoid breaking dependencies with some services that must be provided
+by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235
+
+### 4.5 Alternative: having one of the containers act as the composite container
+
+In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to
+add another DI container to this container. Most of the time, the "big" framework will be responsible for
+creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted,
+the "big" framework will not be aware of the existence of a composite container that it should use instead
+of its own container.
+
+For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container
+to make it act as a composite container.
+
+This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194) 
+and [here](http://mouf-php.com/container-interop-whats-next#solution4).
+
+This was implemented in Symfony 2 using:
+
+- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0)
+- [framework interop](https://github.com/mnapoli/framework-interop/)
+
+This was implemented in Silex using:
+
+- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di)
+
+Having a container act as the composite container is not part of the delegate lookup standard because it is
+simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop
+play nice with other DI containers.
+
+
+5. Implementations
+------------------
+
+The following projects already implement the delegate lookup feature:
+
+- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120)
+- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72)
+- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62)
+
+6. People
+---------
+
+Are listed here all people that contributed in the discussions, by alphabetical order:
+
+- [Alexandru Pătrănescu](https://github.com/drealecs)
+- [Ben Peachey](https://github.com/potherca)
+- [David Négrier](https://github.com/moufmouf)
+- [Jeremy Lindblom](https://github.com/jeremeamia)
+- [Marco Pivetta](https://github.com/Ocramius)
+- [Matthieu Napoli](https://github.com/mnapoli)
+- [Nelson J Morais](https://github.com/njasm)
+- [Phil Sturgeon](https://github.com/philsturgeon)
+- [Stephan Hochdörfer](https://github.com/shochdoerfer)
+
+7. Relevant Links
+-----------------
+
+_**Note:** Order descending chronologically._
+
+- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20)
+- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8)
+- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next)
+

+ 60 - 0
system/vendor/container-interop/container-interop/docs/Delegate-lookup.md

@@ -0,0 +1,60 @@
+Delegate lookup feature
+=======================
+
+This document describes a standard for dependency injection containers.
+
+The goal set by the *delegate lookup* feature is to allow several containers to share entries.
+Containers implementing this feature can perform dependency lookups in other containers.
+
+Containers implementing this feature will offer a greater lever of interoperability
+with other containers. Implementation of this feature is therefore RECOMMENDED.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The word `implementor` in this document is to be interpreted as someone
+implementing the delegate lookup feature in a dependency injection-related library or framework.
+Users of dependency injections containers (DIC) are referred to as `user`.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Vocabulary
+-------------
+
+In a dependency injection container, the container is used to fetch entries.
+Entries can have dependencies on other entries. Usually, these other entries are fetched by the container.
+
+The *delegate lookup* feature is the ability for a container to fetch dependencies in
+another container. In the rest of the document, the word "container" will reference the container
+implemented by the implementor. The word "delegate container" will reference the container we are
+fetching the dependencies from.
+
+2. Specification
+----------------
+
+A container implementing the *delegate lookup* feature:
+
+- MUST implement the [`ContainerInterface`](ContainerInterface.md)
+- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
+  or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md).
+
+When a container is configured to use a delegate container for dependencies:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container.
+  If the entry is not part of the container, an exception should be thrown
+  (as requested by the [`ContainerInterface`](ContainerInterface.md)).
+- Calls to the `has` method should only return `true` if the entry is part of the container.
+  If the entry is not part of the container, `false` should be returned.
+- If the fetched entry has dependencies, **instead** of performing
+  the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup
+into the same container (or another container) instead of the delegate container.
+
+3. Package / Interface
+----------------------
+
+This feature is not tied to any code, interface or package.

BIN
system/vendor/container-interop/container-interop/docs/images/interoperating_containers.png


BIN
system/vendor/container-interop/container-interop/docs/images/priority.png


BIN
system/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png


+ 15 - 0
system/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Interop\Container;
+
+use Psr\Container\ContainerInterface as PsrContainerInterface;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface extends PsrContainerInterface
+{
+}

+ 15 - 0
system/vendor/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Interop\Container\Exception;
+
+use Psr\Container\ContainerExceptionInterface as PsrContainerException;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerException extends PsrContainerException
+{
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff