mirror of
https://github.com/typemill/typemill.git
synced 2025-01-16 13:00:26 +01:00
Version 1.2.16
This commit is contained in:
parent
c03a27c10a
commit
62c7650f55
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1 +1 @@
|
||||
custom: https://www.paypal.me/typemill
|
||||
custom: https://www.paypal.me/typemill
|
||||
|
41
.gitignore
vendored
41
.gitignore
vendored
@ -1,21 +1,22 @@
|
||||
cache
|
||||
plugins/admin
|
||||
plugins/contactform
|
||||
plugins/demo
|
||||
plugins/disqus
|
||||
plugins/download
|
||||
plugins/finalwords
|
||||
plugins/joblistings
|
||||
plugins/landingpage
|
||||
plugins/mail
|
||||
plugins/newsletter
|
||||
plugins/textadds
|
||||
plugins/version
|
||||
settings/settings.yaml
|
||||
settings/formdata.yaml
|
||||
settings/users
|
||||
system/vendor
|
||||
tests
|
||||
themes/monograf
|
||||
zips
|
||||
cache
|
||||
plugins/admin
|
||||
plugins/contactform
|
||||
plugins/demo
|
||||
plugins/disqus
|
||||
plugins/download
|
||||
plugins/finalwords
|
||||
plubins/hyer
|
||||
plugins/joblistings
|
||||
plugins/landingpage
|
||||
plugins/mail
|
||||
plugins/newsletter
|
||||
plugins/textadds
|
||||
plugins/version
|
||||
settings/settings.yaml
|
||||
settings/formdata.yaml
|
||||
settings/users
|
||||
system/vendor
|
||||
tests
|
||||
themes/monograf
|
||||
zips
|
||||
build.php
|
80
.htaccess
80
.htaccess
@ -1,41 +1,41 @@
|
||||
RewriteEngine On
|
||||
|
||||
# If your homepage is http://yourdomain.com/yoursite
|
||||
# Set the RewriteBase to:
|
||||
# RewriteBase /yoursite
|
||||
|
||||
# In some environements, an empty RewriteBase is required:
|
||||
# RewriteBase /
|
||||
|
||||
# Protect your system files from prying eyes
|
||||
RewriteRule ^(system\/author\/) - [L]
|
||||
RewriteRule ^(system) - [F,L]
|
||||
RewriteRule ^(content) - [F,L]
|
||||
RewriteRule ^(settings) - [F,L]
|
||||
RewriteRule ^(.*)?\.yml$ - [F,L]
|
||||
Rewriterule ^(.*)?\.yaml$ - [F,L]
|
||||
RewriteRule ^(.*)?\.txt$ - [F,L]
|
||||
RewriteRule ^(.*)?\.example$ - [F,L]
|
||||
RewriteRule ^(.*/)?\.git+ - [F,L]
|
||||
|
||||
# Use this to redirect HTTP to HTTPS on apache servers
|
||||
# RewriteCond %{HTTPS} off
|
||||
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# Use this to redirect www to non-wwww on apache servers
|
||||
# RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
||||
# RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
|
||||
|
||||
# Use this to redirect slash/ to no slash urls on apache servers
|
||||
# RewriteCond %{REQUEST_FILENAME} !-d
|
||||
# RewriteRule ^(.*)/$ /$1 [R=301,L]
|
||||
|
||||
# Removes index.php
|
||||
RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC]
|
||||
RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L]
|
||||
|
||||
# Directs all web requests through the site index file
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteEngine On
|
||||
|
||||
# If your homepage is http://yourdomain.com/yoursite
|
||||
# Set the RewriteBase to:
|
||||
# RewriteBase /yoursite
|
||||
|
||||
# In some environements, an empty RewriteBase is required:
|
||||
# RewriteBase /
|
||||
|
||||
# Protect your system files from prying eyes
|
||||
RewriteRule ^(system\/author\/) - [L]
|
||||
RewriteRule ^(system) - [F,L]
|
||||
RewriteRule ^(content) - [F,L]
|
||||
RewriteRule ^(settings) - [F,L]
|
||||
RewriteRule ^(.*)?\.yml$ - [F,L]
|
||||
Rewriterule ^(.*)?\.yaml$ - [F,L]
|
||||
RewriteRule ^(.*)?\.txt$ - [F,L]
|
||||
RewriteRule ^(.*)?\.example$ - [F,L]
|
||||
RewriteRule ^(.*/)?\.git+ - [F,L]
|
||||
|
||||
# Use this to redirect HTTP to HTTPS on apache servers
|
||||
# RewriteCond %{HTTPS} off
|
||||
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# Use this to redirect www to non-wwww on apache servers
|
||||
# RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
||||
# RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
|
||||
|
||||
# Use this to redirect slash/ to no slash urls on apache servers
|
||||
# RewriteCond %{REQUEST_FILENAME} !-d
|
||||
# RewriteRule ^(.*)/$ /$1 [R=301,L]
|
||||
|
||||
# Removes index.php
|
||||
RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC]
|
||||
RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L]
|
||||
|
||||
# Directs all web requests through the site index file
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^ index.php [QSA,L]
|
@ -1,32 +1,32 @@
|
||||
{
|
||||
"name": "trendschau/typemill",
|
||||
"type": "project",
|
||||
"description": "A crazy simple tool to create web-documentations and online manuals with markdown files.",
|
||||
"keywords": ["documentations","manuals","flat-file","Markdown","php"],
|
||||
"homepage": "http://typemill.net",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"vendor-dir": "system/vendor"
|
||||
},
|
||||
"require": {
|
||||
"slim/slim": "~3.7",
|
||||
"twig/twig": "~1.18",
|
||||
"slim/twig-view": "~2.3",
|
||||
"slim/flash": "~0.4",
|
||||
"Slim/csrf": "~0.8",
|
||||
"symfony/yaml": "~2.8",
|
||||
"symfony/event-dispatcher": "~3.3",
|
||||
"erusev/parsedown": "~1.4",
|
||||
"erusev/parsedown-extra": "dev-master",
|
||||
"jbroadway/urlify": "~1.1",
|
||||
"vlucas/valitron": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Typemill\\": "system/",
|
||||
"Plugins\\": "plugins/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
{
|
||||
"name": "trendschau/typemill",
|
||||
"type": "project",
|
||||
"description": "A crazy simple tool to create web-documentations and online manuals with markdown files.",
|
||||
"keywords": ["documentations","manuals","flat-file","Markdown","php"],
|
||||
"homepage": "http://typemill.net",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"vendor-dir": "system/vendor"
|
||||
},
|
||||
"require": {
|
||||
"slim/slim": "~3.7",
|
||||
"twig/twig": "~1.18",
|
||||
"slim/twig-view": "~2.3",
|
||||
"slim/flash": "~0.4",
|
||||
"Slim/csrf": "~0.8",
|
||||
"symfony/yaml": "~2.8",
|
||||
"symfony/event-dispatcher": "~3.3",
|
||||
"erusev/parsedown": "~1.4",
|
||||
"erusev/parsedown-extra": "dev-master",
|
||||
"jbroadway/urlify": "~1.1",
|
||||
"vlucas/valitron": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Typemill\\": "system/",
|
||||
"Plugins\\": "plugins/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
12
index.php
12
index.php
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/system/vendor/autoload.php';
|
||||
|
||||
require __DIR__ . '/system/system.php';
|
||||
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/system/vendor/autoload.php';
|
||||
|
||||
require __DIR__ . '/system/system.php';
|
||||
|
||||
$app->run();
|
12
licence.md
12
licence.md
@ -1,7 +1,7 @@
|
||||
Copyright (c) 2017 Sebastian Schürmanns
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
Copyright (c) 2017 Sebastian Schürmanns
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,47 +1,47 @@
|
||||
<?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'];
|
||||
|
||||
if(isset($analyticSettings['tool']))
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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'];
|
||||
|
||||
if(isset($analyticSettings['tool']))
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +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-
|
||||
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'
|
@ -1,5 +1,5 @@
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', '{{ google_id }}');
|
@ -1,11 +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);
|
||||
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);
|
||||
})();
|
@ -1,38 +1,38 @@
|
||||
<?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));
|
||||
}
|
||||
<?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));
|
||||
}
|
||||
}
|
@ -1,88 +1,88 @@
|
||||
name: Cookie Consent
|
||||
version: 1.0.1
|
||||
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'
|
||||
href: 'https://cookiesandyou.com/'
|
||||
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
|
||||
|
||||
href:
|
||||
type: url
|
||||
label: Link to more informations
|
||||
placeholder: 'https://cookiesandyou.com/'
|
||||
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'
|
||||
name: Cookie Consent
|
||||
version: 1.0.1
|
||||
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'
|
||||
href: 'https://cookiesandyou.com/'
|
||||
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
|
||||
|
||||
href:
|
||||
type: url
|
||||
label: Link to more informations
|
||||
placeholder: 'https://cookiesandyou.com/'
|
||||
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
|
@ -1,6 +1,6 @@
|
||||
.cc-window{opacity:1;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{transition:transform 1s ease}.cc-animate.cc-revoke.cc-top{transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-bottom,.cc-animate.cc-revoke.cc-active.cc-top,.cc-revoke:hover{transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;transition:max-height 1s}
|
||||
.cc-link,.cc-revoke:hover{text-decoration:underline}.cc-revoke,.cc-window{position:fixed;overflow:hidden;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1}
|
||||
.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{display:block;-ms-flex:1 1 auto;flex:1 1 auto;max-width:100%;margin-right:1em}.cc-compliance{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-floating .cc-compliance>.cc-btn{-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em}
|
||||
@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-floating,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-ms-flex:1 1 auto;flex:1 1 auto}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-ms-flex-align:unset;align-items:unset}.cc-window.cc-banner .cc-message{margin-right:0}}
|
||||
.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block}
|
||||
.cc-window{opacity:1;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{transition:transform 1s ease}.cc-animate.cc-revoke.cc-top{transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-bottom,.cc-animate.cc-revoke.cc-active.cc-top,.cc-revoke:hover{transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;transition:max-height 1s}
|
||||
.cc-link,.cc-revoke:hover{text-decoration:underline}.cc-revoke,.cc-window{position:fixed;overflow:hidden;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1}
|
||||
.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{display:block;-ms-flex:1 1 auto;flex:1 1 auto;max-width:100%;margin-right:1em}.cc-compliance{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-floating .cc-compliance>.cc-btn{-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em}
|
||||
@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-floating,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-ms-flex:1 1 auto;flex:1 1 auto}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-ms-flex-align:unset;align-items:unset}.cc-window.cc-banner .cc-message{margin-right:0}}
|
||||
.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block}
|
||||
.cc-theme-edgeless.cc-window{padding:0}.cc-floating.cc-theme-edgeless .cc-message{margin:2em 2em 1.5em}.cc-banner.cc-theme-edgeless .cc-btn{margin:0;padding:.8em 1.8em;height:100%}.cc-banner.cc-theme-edgeless .cc-message{margin-left:1em}.cc-floating.cc-theme-edgeless .cc-btn+.cc-btn{margin-left:0}
|
@ -1,22 +1,22 @@
|
||||
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 }}",
|
||||
"href": "{{ settings.plugins.cookieconsent.href }}"
|
||||
}
|
||||
})
|
||||
});
|
||||
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 }}",
|
||||
"href": "{{ settings.plugins.cookieconsent.href }}"
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -1,28 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\Highlight;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class Highlight extends Plugin
|
||||
{
|
||||
protected $settings;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onTwigLoaded' => 'onTwigLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addCSS('/highlight/public/default.css');
|
||||
$this->addJS('/highlight/public/highlight.pack.js');
|
||||
|
||||
/* initialize the script */
|
||||
$this->addInlineJS('hljs.initHighlightingOnLoad();');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Plugins\Highlight;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class Highlight extends Plugin
|
||||
{
|
||||
protected $settings;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onTwigLoaded' => 'onTwigLoaded'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function onTwigLoaded()
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addCSS('/highlight/public/default.css');
|
||||
$this->addJS('/highlight/public/highlight.pack.js');
|
||||
|
||||
/* initialize the script */
|
||||
$this->addInlineJS('hljs.initHighlightingOnLoad();');
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
name: Highlight
|
||||
version: 1.0.0
|
||||
description: Adds the famous javascript syntax highlighter.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://highlightjs.org/
|
||||
name: Highlight
|
||||
version: 1.0.0
|
||||
description: Adds the famous javascript syntax highlighter.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://highlightjs.org/
|
||||
licence: BSD
|
@ -1,24 +1,24 @@
|
||||
Copyright (c) 2006, Ivan Sagalaev
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of highlight.js nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Copyright (c) 2006, Ivan Sagalaev
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of highlight.js nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -1,99 +1,99 @@
|
||||
/*
|
||||
|
||||
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
/* Base color: saturation 0; */
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-attribute,
|
||||
.hljs-selector-tag,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-doctag,
|
||||
.hljs-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* User color: hue: 0 */
|
||||
|
||||
.hljs-type,
|
||||
.hljs-string,
|
||||
.hljs-number,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-quote,
|
||||
.hljs-template-tag,
|
||||
.hljs-deletion {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #880000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-symbol,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-link,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #BC6060;
|
||||
}
|
||||
|
||||
|
||||
/* Language color: hue: 90; */
|
||||
|
||||
.hljs-literal {
|
||||
color: #78A960;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-code,
|
||||
.hljs-addition {
|
||||
color: #397300;
|
||||
}
|
||||
|
||||
|
||||
/* Meta color: hue: 200 */
|
||||
|
||||
.hljs-meta {
|
||||
color: #1f7199;
|
||||
}
|
||||
|
||||
.hljs-meta-string {
|
||||
color: #4d99bf;
|
||||
}
|
||||
|
||||
|
||||
/* Misc effects */
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
/*
|
||||
|
||||
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
/* Base color: saturation 0; */
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-attribute,
|
||||
.hljs-selector-tag,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-doctag,
|
||||
.hljs-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* User color: hue: 0 */
|
||||
|
||||
.hljs-type,
|
||||
.hljs-string,
|
||||
.hljs-number,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-quote,
|
||||
.hljs-template-tag,
|
||||
.hljs-deletion {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #880000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-symbol,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-link,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #BC6060;
|
||||
}
|
||||
|
||||
|
||||
/* Language color: hue: 90; */
|
||||
|
||||
.hljs-literal {
|
||||
color: #78A960;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-code,
|
||||
.hljs-addition {
|
||||
color: #397300;
|
||||
}
|
||||
|
||||
|
||||
/* Meta color: hue: 200 */
|
||||
|
||||
.hljs-meta {
|
||||
color: #1f7199;
|
||||
}
|
||||
|
||||
.hljs-meta-string {
|
||||
color: #4d99bf;
|
||||
}
|
||||
|
||||
|
||||
/* Misc effects */
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
204
plugins/hyer/getads.php
Normal file
204
plugins/hyer/getads.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\hyer;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class getads extends Plugin
|
||||
{
|
||||
|
||||
public static function getSubscribedEvents(){}
|
||||
|
||||
# get only pro ads for product
|
||||
public function pro()
|
||||
{
|
||||
|
||||
# get referrer url
|
||||
if(!isset($_SERVER['HTTP_REFERER']))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Referrer is missing']);
|
||||
}
|
||||
|
||||
# get the url parameter
|
||||
$input = $this->getParams();
|
||||
|
||||
# create secret
|
||||
$secret = time();
|
||||
$secret = substr($secret,0,-1);
|
||||
$oldsecret = $secret -1;
|
||||
$secret = md5($secret . $this->getSalt());
|
||||
$oldsecret = md5($oldsecret . $this->getSalt());
|
||||
|
||||
if(!isset($input['access']) OR ($input['access'] != $secret AND $input['access'] != $oldsecret))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Secret is incorrect']);
|
||||
}
|
||||
|
||||
# get the last segment of url
|
||||
$referrer = $_SERVER['HTTP_REFERER'];
|
||||
$segment = basename($referrer);
|
||||
|
||||
# get the settings
|
||||
$settings = \Typemill\Settings::loadSettings();
|
||||
|
||||
# get url-map to find the correct cms for requesting page
|
||||
$urlmap = json_decode($settings['settings']['plugins']['hyer']['urlmap'],true);
|
||||
|
||||
# map the request with the map in settings
|
||||
if(!isset($urlmap[$segment]))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'We could not map that url']);
|
||||
}
|
||||
|
||||
# make call
|
||||
# $hyerApi = 'http://localhost/typemillService/public/publicapi/proads';
|
||||
$hyerApi = 'https://service.cmsstash.de/publicapi/proads';
|
||||
$hyerRequest = ['product' => $urlmap[$segment]];
|
||||
|
||||
$result = $this->makeApiCall($hyerRequest, $hyerApi);
|
||||
|
||||
return $this->returnJson($result);
|
||||
}
|
||||
|
||||
# get 10 latest pro ads
|
||||
public function latest()
|
||||
{
|
||||
# get the url parameter
|
||||
$input = $this->getParams();
|
||||
|
||||
# create secret
|
||||
$secret = time();
|
||||
$secret = substr($secret,0,-1);
|
||||
$oldsecret = $secret -1;
|
||||
$secret = md5($secret . $this->getSalt());
|
||||
$oldsecret = md5($oldsecret . $this->getSalt());
|
||||
|
||||
if(!isset($input['access']) OR ($input['access'] != $secret AND $input['access'] != $oldsecret))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Secret is incorrect']);
|
||||
}
|
||||
|
||||
# $hyerApi = 'http://localhost/typemillService/public/publicapi/latestproads';
|
||||
$hyerApi = 'https://service.cmsstash.de/publicapi/latestproads';
|
||||
$hyerRequest = [];
|
||||
|
||||
$result = $this->makeApiCall($hyerRequest, $hyerApi);
|
||||
|
||||
return $this->returnJson($result);
|
||||
}
|
||||
|
||||
# search for ads from directory
|
||||
public function search()
|
||||
{
|
||||
session_start();
|
||||
|
||||
# get the url parameter
|
||||
$input = $this->getParams();
|
||||
|
||||
# validate input here
|
||||
|
||||
# read session data for simple csrf check
|
||||
$token = isset($_SESSION['hyer']) ? $_SESSION['hyer'] : false;
|
||||
$time = isset($_SESSION['hyer-expire']) ? $_SESSION['hyer-expire'] : false;
|
||||
|
||||
# perform simple security or csrf check
|
||||
if(!isset($input['token']) OR !$token OR !$time OR ($input['token'] != $token ) OR (time() >= $time))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Die Sitzung ist abgelaufen, bitte laden Sie die Seite neu.']);
|
||||
}
|
||||
|
||||
# $hyerApi = 'http://localhost/typemillService/public/publicapi/searchads';
|
||||
$hyerApi = 'https://service.cmsstash.de/publicapi/searchads';
|
||||
$hyerRequest = [];
|
||||
|
||||
if(isset($input['product']) && $input['product'] != '')
|
||||
{
|
||||
if(!$this->validateLengthBetween($input['product'], 2, 50) OR !$this->validateHtml($input['product']))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Product is invalid']);
|
||||
}
|
||||
$hyerRequest['product'] = $input['product'];
|
||||
}
|
||||
if(isset($input['region']) && $input['region'] != '')
|
||||
{
|
||||
if(!$this->validateLengthBetween($input['region'], 2, 50) OR !$this->validateHtml($input['region']))
|
||||
{
|
||||
return $this->returnJsonError(['message' => 'Product is invalid']);
|
||||
}
|
||||
$hyerRequest['region'] = $input['region'];
|
||||
}
|
||||
|
||||
$result = $this->makeApiCall($hyerRequest, $hyerApi);
|
||||
|
||||
if($result)
|
||||
{
|
||||
return $this->returnJson($result);
|
||||
}
|
||||
return $this->returnJsonError(['message' => 'Kein Ergebnis']);
|
||||
}
|
||||
|
||||
private function makeApiCall($hyerRequest, $hyerApi)
|
||||
{
|
||||
|
||||
# get the settings
|
||||
$settings = \Typemill\Settings::loadSettings();
|
||||
|
||||
# get api key from settings
|
||||
$apikey = $settings['settings']['plugins']['hyer']['apikey'];
|
||||
$siteid = $settings['settings']['plugins']['hyer']['siteid'];
|
||||
|
||||
|
||||
# use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Authorization: Basic " . base64_encode($siteid . ":". $apikey),
|
||||
'method' => 'GET',
|
||||
'content' => http_build_query($hyerRequest),
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
ini_set('allow_url_fopen', '1');
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($hyerApi, false, $context);
|
||||
$result = json_decode($result);
|
||||
|
||||
ini_set('allow_url_fopen', '0');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function validateHtml($value)
|
||||
{
|
||||
if($value == strip_tags($value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function validateLengthBetween($value, $min, $max)
|
||||
{
|
||||
$length = $this->stringLength($value);
|
||||
|
||||
return ($length !== false) && $length >= $min && $length <= $max;
|
||||
}
|
||||
|
||||
private function stringLength($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return false;
|
||||
} elseif (function_exists('mb_strlen')) {
|
||||
return mb_strlen($value);
|
||||
}
|
||||
|
||||
return strlen($value);
|
||||
}
|
||||
|
||||
private function getSalt()
|
||||
{
|
||||
return "asPx9Derf2";
|
||||
}
|
||||
|
||||
}
|
221
plugins/hyer/hyer.php
Normal file
221
plugins/hyer/hyer.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\hyer;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class Hyer extends Plugin
|
||||
{
|
||||
protected $item;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onsettingsLoaded',
|
||||
'onItemLoaded' => 'onItemLoaded',
|
||||
'onContentArrayLoaded' => 'onContentArrayLoaded',
|
||||
);
|
||||
}
|
||||
|
||||
public static function addNewRoutes()
|
||||
{
|
||||
# the route for the api calls
|
||||
return array(
|
||||
array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/latestadsfe30s8edw4wdkp',
|
||||
'class' => 'Plugins\hyer\getads:latest'
|
||||
),
|
||||
array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/proadsfe30s8edw4wdkp',
|
||||
'class' => 'Plugins\hyer\getads:pro'
|
||||
),
|
||||
array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/searchadsfe30s8edw4wdkp',
|
||||
'class' => 'Plugins\hyer\getads:search'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
$this->settings = $settings->getData();
|
||||
}
|
||||
|
||||
public function onItemLoaded($item)
|
||||
{
|
||||
$this->item = $item->getData();
|
||||
}
|
||||
|
||||
public function onContentArrayLoaded($contentArray)
|
||||
{
|
||||
# get content array
|
||||
$content = $contentArray->getData();
|
||||
$settings = $this->getPluginSettings('hyer');
|
||||
$path = $this->getPath();
|
||||
$segment = basename($path); # get the last url segment
|
||||
$urlmap = json_decode($settings['urlmap'],true); # to find the correct page to include app
|
||||
$directory = trim($settings['directory'],"/"); # the path for the directory
|
||||
$salt = "asPx9Derf2";
|
||||
|
||||
# if we are on the directory page
|
||||
|
||||
if(trim($path,"/") == trim($settings['directory'],"/"))
|
||||
{
|
||||
# activate axios and vue in frontend
|
||||
$this->activateAxios();
|
||||
$this->activateVue();
|
||||
$this->activateTachyons();
|
||||
|
||||
# add the css and vue application
|
||||
$this->addCSS('/hyer/public/hyer.css');
|
||||
$this->addJS('/hyer/public/hyer.js');
|
||||
|
||||
$twig = $this->getTwig(); // get the twig-object
|
||||
$loader = $twig->getLoader(); // get the twig-template-loader
|
||||
$loader->addPath(__DIR__ . '/templates');
|
||||
$svg = $twig->fetch('/hyer.twig');
|
||||
|
||||
# simple security for first request
|
||||
$secret = time();
|
||||
$secret = substr($secret,0,-1);
|
||||
$secret = md5($secret . $salt);
|
||||
|
||||
# simple csrf protection with a session for long following requests
|
||||
session_start();
|
||||
$length = 32;
|
||||
$token = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
|
||||
$_SESSION['hyer'] = $token;
|
||||
$_SESSION['hyer-expire'] = time() + 1300; # 60 seconds * 30 minutes
|
||||
|
||||
# use this to secure the api long term
|
||||
# $token = time();
|
||||
# $token = substr($token,0,-4);
|
||||
# $token = md5($token . $salt);
|
||||
|
||||
$products = '';
|
||||
|
||||
foreach($urlmap as $product)
|
||||
{
|
||||
$products .= $product . ',';
|
||||
}
|
||||
$products = trim($products, ",");
|
||||
|
||||
# create div for vue app
|
||||
$textHyer = '<div data-access="' . $secret . '" data-token="' . $token . '" data-products="' . $products . '" id="hyerapp"><directory></directory></div>';
|
||||
|
||||
# create content type
|
||||
$textHyer = Array
|
||||
(
|
||||
'rawHtml' => $svg . $textHyer,
|
||||
'allowRawHtmlInSafeMode' => true,
|
||||
'autobreak' => 1
|
||||
);
|
||||
|
||||
$content[] = $textHyer;
|
||||
|
||||
}
|
||||
# map the url with the map in settings
|
||||
elseif(isset($urlmap[$segment]))
|
||||
{
|
||||
# activate axios and vue in frontend
|
||||
$this->activateAxios();
|
||||
$this->activateVue();
|
||||
$this->activateTachyons();
|
||||
|
||||
# add the css and vue application
|
||||
$this->addCSS('/hyer/public/hyer.css');
|
||||
$this->addJS('/hyer/public/hyer.js');
|
||||
|
||||
$twig = $this->getTwig(); // get the twig-object
|
||||
$loader = $twig->getLoader(); // get the twig-template-loader
|
||||
$loader->addPath(__DIR__ . '/templates');
|
||||
$svg = $twig->fetch('/hyer.twig');
|
||||
|
||||
# use this to secure the api a bit
|
||||
$secret = time();
|
||||
$secret = substr($secret,0,-1);
|
||||
$secret = md5($secret . $salt);
|
||||
|
||||
# create div for vue app
|
||||
$textHyer = '<div data-access="' . $secret . '" id="hyerapp"><premiumads></premiumads></div>';
|
||||
$textHeadline = (isset($settings['headline']) && !empty($settings['headline'])) ? $settings['headline'] : false;
|
||||
$textTeaser = (isset($settings['teaser']) && !empty($settings['teaser'])) ? $settings['teaser'] : false;
|
||||
|
||||
# create content type
|
||||
$textHyer = Array
|
||||
(
|
||||
'rawHtml' => $svg . $textHyer,
|
||||
'allowRawHtmlInSafeMode' => true,
|
||||
'autobreak' => 1
|
||||
);
|
||||
|
||||
if($textHeadline)
|
||||
{
|
||||
$textHeadline = array(
|
||||
'name' => 'h2',
|
||||
'text' => $textHeadline,
|
||||
'handler' => 'line',
|
||||
'attributes' => Array
|
||||
(
|
||||
'id' => $textHeadline
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if($textTeaser)
|
||||
{
|
||||
$textTeaser = array(
|
||||
'name' => 'p',
|
||||
'handler' => Array
|
||||
(
|
||||
'function' => 'lineElements',
|
||||
'argument' => $textTeaser,
|
||||
'destination' => 'elements'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$length = count($content);
|
||||
$thisElement = 0;
|
||||
$pos = false;
|
||||
$i = 0;
|
||||
$position = isset($settings['position']) ? $settings['position'] : 3;
|
||||
|
||||
while($i < $length)
|
||||
{
|
||||
if($content[$i]['name'] == 'h2')
|
||||
{
|
||||
$thisElement = $thisElement + 1;
|
||||
}
|
||||
|
||||
# place hyer app before the 3rd headline h2-level
|
||||
if($thisElement == $position)
|
||||
{
|
||||
$pos = $i;
|
||||
break;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
if($pos)
|
||||
{
|
||||
$start = array_slice($content, 0, $pos);
|
||||
$end = array_slice($content, $pos);
|
||||
if($textHeadline){ $start[] = $textHeadline; }
|
||||
if($textTeaser){ $start[] = $textTeaser; }
|
||||
$content = array_merge( $start, array($textHyer), $end );
|
||||
}
|
||||
else
|
||||
{
|
||||
if($textHeadline){ $content[] = $textHeadline; }
|
||||
if($textTeaser){ $content[] = $textTeaser; }
|
||||
$content[] = $textHyer;
|
||||
}
|
||||
}
|
||||
|
||||
$contentArray->setData($content);
|
||||
}
|
||||
}
|
44
plugins/hyer/hyer.yaml
Normal file
44
plugins/hyer/hyer.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
name: HYER SPACE
|
||||
version: 1.0.0
|
||||
description: Integrate HYER Text-Adds into typemill.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://typemill.net
|
||||
licence: MIT
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
apikey:
|
||||
type: text
|
||||
label: API KEY
|
||||
placeholder:
|
||||
required: true
|
||||
|
||||
siteid:
|
||||
type: text
|
||||
label: Site ID
|
||||
placeholder:
|
||||
required: true
|
||||
|
||||
urlmap:
|
||||
type: textarea
|
||||
label: URL Map
|
||||
placeholder: '180 characters'
|
||||
required: true
|
||||
|
||||
directory:
|
||||
type: text
|
||||
label: Path to directory
|
||||
placeholder: 'path/to/directory'
|
||||
|
||||
headline:
|
||||
type: text
|
||||
label: Headline before ad-block on review page
|
||||
|
||||
teaser:
|
||||
type: textarea
|
||||
label: Teaser before ad-block on review page
|
||||
|
||||
position:
|
||||
type: number
|
||||
label: Position for the ad-block (before xyz h2 headlines)
|
64
plugins/hyer/public/hyer.css
Normal file
64
plugins/hyer/public/hyer.css
Normal file
@ -0,0 +1,64 @@
|
||||
/**********************
|
||||
** ICONS **
|
||||
**********************/
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke-width: 0;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
.icon-users {
|
||||
width: 1.125em;
|
||||
}
|
||||
.bg-customized{
|
||||
background: #f9f8f6;
|
||||
}
|
||||
.bg-customized-2{
|
||||
background: #ff4136;
|
||||
}
|
||||
.b--customized-2{
|
||||
border-color: #ff4136;
|
||||
}
|
||||
.customized-2{
|
||||
color: #ff4136;
|
||||
}
|
||||
.custombutton{
|
||||
line-height: 1.55rem;
|
||||
}
|
||||
.link, .link:active, .link:focus, .link:hover, .link:link, .link:visited {
|
||||
transition: color .15s ease-in;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
.link-white, .link-white.active, .link-white:focus, .link-white:hover, .link-white:link, .link-white:visited{
|
||||
color: #fff;
|
||||
}
|
||||
select{
|
||||
background-color: #f9f8f6;
|
||||
}
|
||||
select{
|
||||
/* reset */
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
||||
/* style */
|
||||
background-image:
|
||||
linear-gradient(45deg, transparent 50%, #444 50%),
|
||||
linear-gradient(135deg, #444 50%, transparent 50%),
|
||||
linear-gradient(to right, transparent, transparent);
|
||||
background-position:
|
||||
calc(100% - 20px) calc(1em + 6px),
|
||||
calc(100% - 15px) calc(1em + 6px),
|
||||
100% 0;
|
||||
background-size:
|
||||
5px 5px,
|
||||
5px 5px,
|
||||
2.8em 2.8em;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
.shadow-hover:hover{
|
||||
box-shadow: 0px 0px 2px 0px rgba(158,158,158,1);
|
||||
}
|
290
plugins/hyer/public/hyer.js
Normal file
290
plugins/hyer/public/hyer.js
Normal file
@ -0,0 +1,290 @@
|
||||
const myaxios = axios.create({
|
||||
baseURL: document.getElementsByTagName("base")[0].href
|
||||
});
|
||||
|
||||
Vue.component('directory', {
|
||||
data: function () {
|
||||
return {
|
||||
ads: [],
|
||||
products: [],
|
||||
selectedProduct: "",
|
||||
selectedRegion: "",
|
||||
token: "",
|
||||
message: false,
|
||||
messageType: '',
|
||||
}
|
||||
},
|
||||
template: '<div>' +
|
||||
'<div>' +
|
||||
'<form action="#">' +
|
||||
'<fieldset class="bn ma0 pa0">' +
|
||||
'<div class="flex-ns items-end justify-around">' +
|
||||
'<div class="w-100 mr3-ns">' +
|
||||
'<label for="country" class="f6 b db mb2 mt4 tl">CMS</label>' +
|
||||
'<select v-model="selectedProduct" class="input-reset outline-focus ba b--black-20 ph2 pv3 mb2 db w-100" name="location[index][country]" v-model="location.country" required>' +
|
||||
'<option value="">-</option>' +
|
||||
'<option v-for="product in products">{{ product }}</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'<div class="w-100 mr3-ns">' +
|
||||
'<label for="region" class="f6 b db mb2 mt4 tl">Region</label>' +
|
||||
'<select v-model="selectedRegion" class="input-reset outline-focus ba b--black-20 ph2 pv3 mb2 db w-100" name="location[index][region]" v-model="location.region" required>' +
|
||||
'<option value="">-</option>' +
|
||||
'<option>Baden-Würtemberg</option>' +
|
||||
'<option>Bayern</option>' +
|
||||
'<option>Berlin</option>' +
|
||||
'<option>Brandenburg</option>' +
|
||||
'<option>Bremen</option>' +
|
||||
'<option>Hamburg</option>' +
|
||||
'<option>Hessen</option>' +
|
||||
'<option>Mecklenburg-Vorpommern</option>' +
|
||||
'<option>Niedersachsen</option>' +
|
||||
'<option>Nordrhein-Westfalen</option>' +
|
||||
'<option>Rheinland-Pfalz</option>' +
|
||||
'<option>Saarland</option>' +
|
||||
'<option>Sachsen</option>' +
|
||||
'<option>Sachsen-Anhalt</option>' +
|
||||
'<option>Schleswig-Holstein</option>' +
|
||||
'<option>Thüringen</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'<button @click.prevent="searchProducts" class="w-100 w-30-ns link bg-customized-2 custombutton link-white dim bn mb2 pa3 mt3 mt0-ns pointer">Suchen</button>' +
|
||||
'</div>' +
|
||||
'</fieldset>' +
|
||||
'</form>' +
|
||||
'</div>' +
|
||||
'<div v-if="message" class="w-100 pa3 tc mv3 customized-2 ba b--customized-2">{{ message }}</div>' +
|
||||
'<div v-for="ad in ads">' +
|
||||
'<div class="w-100 bg-customized dark-gray flex dib tl pa0 mv3 f5 relative">' +
|
||||
'<div class="w-100 ba b--light-gray pa0 shadow-hover">' +
|
||||
'<a :href="getLink(ad)" rel="ugc sponsored" target="_blank" class="link dark-gray pa0 mt3 mb0">' +
|
||||
'<div v-if="ad.pro" class="absolute-ns tc top-0 right-0 ba b--dark-gray pa1 f6 ma3">Premium</div>' +
|
||||
'<h3 class="mt4 mb1 pv0 ph3 underline-hover">{{ getTitle(ad) }}</h3>' +
|
||||
'<small class="pv0 ph3 ma0 underline-hover">{{ getLink(ad) }}</small>' +
|
||||
'<p class="mt1 mb3 pv0 ph3 underline-hover">{{ getTeaser(ad) }}</p>' +
|
||||
'</a>' +
|
||||
'<div class="f5 pv2 ph3 bt dark-gray b--moon-gray w-100 flex">' +
|
||||
'<div class="tl w-50">' +
|
||||
'<svg class="icon icon-location"><use xlink:href="#icon-location"></use></svg> {{ ad.city }} ' +
|
||||
'<span class="pl2"><svg class="icon icon-location"><use xlink:href="#icon-users"></use></svg> {{ ad.size}}</span>' +
|
||||
'</div>' +
|
||||
'<div class="tr w-50">Web:' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" :href="ad.company_link" target="_blank"><svg class="icon icon-location"><use xlink:href="#icon-home"></use></svg></a>' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_1" :href="ad.sm_link_1" target="_blank"><svg :class="getSocialIcon(ad.sm_link_1)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_1, true)"></use></svg></a>' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_2" :href="ad.sm_link_2" target="_blank"><svg :class="getSocialIcon(ad.sm_link_2)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_2, true)"></use></svg></a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="w-100 bg-white dark-gray dib tl pa0 mv3 f5">' +
|
||||
'<div class="w-100 ba b--light-gray pa0 tc">' +
|
||||
'<h3 class="mt4 mb0 pv0 ph3">Web-Agentur oder Freelancer?</h3>' +
|
||||
'<p class="mt3 mb3 pv0 ph3">Dann ist genau hier Ihre Expertise gefragt!<br/> Mit einem Dienstleister-Eintrag auf cmsstash.de</p>' +
|
||||
'<a href="https://service.cmsstash.de" class="link bg-customized-2 link-white dim ba1 b--red mt0 mb4 ph3 pv2 dib br1">Eintrag erstellen</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
mounted: function(){
|
||||
|
||||
var products = document.getElementById("hyerapp").dataset.products;
|
||||
this.products = products.split(',');
|
||||
|
||||
this.token = document.getElementById("hyerapp").dataset.token;
|
||||
|
||||
self = this;
|
||||
|
||||
var access = document.getElementById("hyerapp").dataset.access;
|
||||
|
||||
myaxios.get('/latestadsfe30s8edw4wdkp?access='+access)
|
||||
.then(function (response) {
|
||||
|
||||
self.ads = response.data.ads;
|
||||
/* if matomo is on, check dom for new links */
|
||||
if(_paq)
|
||||
{
|
||||
Vue.nextTick(function () {
|
||||
_paq.push(['enableLinkTracking']);
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function (error) {});
|
||||
},
|
||||
methods: {
|
||||
searchProducts: function()
|
||||
{
|
||||
self = this;
|
||||
|
||||
myaxios.get('/searchadsfe30s8edw4wdkp',{
|
||||
params: {
|
||||
token: this.token,
|
||||
product: this.selectedProduct,
|
||||
region: this.selectedRegion
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
self.ads = response.data.ads;
|
||||
/* if matomo is on, check dom for new links */
|
||||
if(_paq)
|
||||
{
|
||||
Vue.nextTick(function () {
|
||||
_paq.push(['enableLinkTracking']);
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
self.message = error.response.data.message;
|
||||
self.messageType = 'error';
|
||||
}
|
||||
});
|
||||
},
|
||||
getTitle: function(product)
|
||||
{
|
||||
if(product.ad_title)
|
||||
{
|
||||
return product.ad_title;
|
||||
}
|
||||
return product.company_name;
|
||||
},
|
||||
getTeaser: function(product)
|
||||
{
|
||||
if(product.ad_teaser)
|
||||
{
|
||||
return product.ad_teaser;
|
||||
}
|
||||
return product.company_description;
|
||||
},
|
||||
getLink: function(product)
|
||||
{
|
||||
if(product.ad_link)
|
||||
{
|
||||
return product.ad_link;
|
||||
}
|
||||
return product.company_link;
|
||||
},
|
||||
getSocialIcon: function(icon,id)
|
||||
{
|
||||
var prefix = id ? '#' : '';
|
||||
if(icon.indexOf("twitter") > -1){ return prefix+'icon-twitter' }
|
||||
else if(icon.indexOf("facebook") > -1){ return prefix+'icon-facebook' }
|
||||
else if(icon.indexOf("xing") > -1){ return prefix+'icon-xing2' }
|
||||
else if(icon.indexOf("linkedin") > -1){ return prefix+'icon-linkedin2' }
|
||||
return prefix+'icon-link';
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Vue.component('premiumads', {
|
||||
data: function () {
|
||||
return {
|
||||
ads: [],
|
||||
fill: [],
|
||||
}
|
||||
},
|
||||
template: '<div>' +
|
||||
'<div v-for="ad in ads">' +
|
||||
'<div class="w-100 bg-customized dark-gray flex dib tl pa0 mv3 f5 relative">' +
|
||||
'<div class="w-100 ba b--light-gray pa0 shadow-hover">' +
|
||||
'<a rel="ugc sponsored" :href="getLink(ad)" target="_blank" class="link dark-gray pa0 mt3 mb0">' +
|
||||
'<div v-if="ad.pro" class="absolute-ns tc top-0 right-0 ba b--dark-gray pa1 f6 ma3">Premium</div>' +
|
||||
'<h3 class="mt4 mb1 pv0 ph3 underline-hover">{{ getTitle(ad) }}</h3>' +
|
||||
'<small class="pv0 ph3 ma0 underline-hover">{{ getLink(ad) }}</small>' +
|
||||
'<p class="mt1 mb3 pv0 ph3 underline-hover">{{ getTeaser(ad) }}</p>' +
|
||||
'</a>' +
|
||||
'<div class="f5 pv2 ph3 bt dark-gray b--moon-gray w-100 flex">' +
|
||||
'<div class="tl w-50">' +
|
||||
'<svg class="icon icon-location"><use xlink:href="#icon-location"></use></svg> {{ ad.city }} ' +
|
||||
'<span class="pl2"><svg class="icon icon-location"><use xlink:href="#icon-users"></use></svg> {{ ad.size}}</span>' +
|
||||
'</div>' +
|
||||
'<div class="tr w-50">Web:' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" :href="ad.company_link" target="_blank"><svg class="icon icon-location"><use xlink:href="#icon-home"></use></svg></a>' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_1" :href="ad.sm_link_1" target="_blank"><svg :class="getSocialIcon(ad.sm_link_1)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_1, true)"></use></svg></a>' +
|
||||
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_2" :href="ad.sm_link_2" target="_blank"><svg :class="getSocialIcon(ad.sm_link_2)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_2, true)"></use></svg></a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div v-if="ads.length < 3">' +
|
||||
'<div class="w-100 bg-white dark-gray dib tl pa0 mv3 f5">' +
|
||||
'<div class="w-100 ba b--light-gray pa0 tc">' +
|
||||
'<h3 class="mt4 mb0 pv0 ph3">Web-Agentur oder Freelancer?</h3>' +
|
||||
'<p class="mt3 mb3 pv0 ph3">Dann ist genau hier Ihre Expertise gefragt!<br/> Mit einem Dienstleister-Eintrag auf cmsstash.de</p>' +
|
||||
'<a href="https://service.cmsstash.de" class="link bg-customized-2 link-white dim ba1 b--red mt0 mb4 ph3 pv2 dib br1">Eintrag erstellen</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
mounted: function(){
|
||||
|
||||
self = this;
|
||||
var access = document.getElementById("hyerapp").dataset.access;
|
||||
|
||||
myaxios.get('/proadsfe30s8edw4wdkp?access='+access)
|
||||
.then(function (response) {
|
||||
self.ads = response.data.ads;
|
||||
var restads = 3 - self.ads.length;
|
||||
for(var i = 0; i < restads; i++)
|
||||
{
|
||||
self.fill.push(1);
|
||||
}
|
||||
/* if matomo is on, check dom for new links */
|
||||
if(_paq)
|
||||
{
|
||||
Vue.nextTick(function () {
|
||||
_paq.push(['enableLinkTracking']);
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getTitle: function(product)
|
||||
{
|
||||
if(product.ad_title)
|
||||
{
|
||||
return product.ad_title;
|
||||
}
|
||||
return product.company_name;
|
||||
},
|
||||
getTeaser: function(product)
|
||||
{
|
||||
if(product.ad_teaser)
|
||||
{
|
||||
return product.ad_teaser;
|
||||
}
|
||||
return product.company_description;
|
||||
},
|
||||
getLink: function(product)
|
||||
{
|
||||
if(product.ad_link)
|
||||
{
|
||||
return product.ad_link;
|
||||
}
|
||||
return product.company_link;
|
||||
},
|
||||
getSocialIcon: function(icon,id)
|
||||
{
|
||||
var prefix = id ? '#' : '';
|
||||
if(icon.indexOf("twitter") > -1){ return prefix+'icon-twitter' }
|
||||
else if(icon.indexOf("facebook") > -1){ return prefix+'icon-facebook' }
|
||||
else if(icon.indexOf("xing") > -1){ return prefix+'icon-xing2' }
|
||||
else if(icon.indexOf("linkedin") > -1){ return prefix+'icon-linkedin2' }
|
||||
return prefix+'icon-link';
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var app = new Vue({
|
||||
el: "#hyerapp",
|
||||
data: {
|
||||
disabled: false,
|
||||
message: '',
|
||||
messageType: '',
|
||||
},
|
||||
});
|
51
plugins/hyer/templates/hyer.twig
Normal file
51
plugins/hyer/templates/hyer.twig
Normal file
@ -0,0 +1,51 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" style="display:none">
|
||||
<symbol id="icon-facebook" viewBox="0 0 16 16">
|
||||
<title>facebook</title>
|
||||
<path d="M9.5 3h2.5v-3h-2.5c-1.93 0-3.5 1.57-3.5 3.5v1.5h-2v3h2v8h3v-8h2.5l0.5-3h-3v-1.5c0-0.271 0.229-0.5 0.5-0.5z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-home" viewBox="0 0 16 16">
|
||||
<title>home</title>
|
||||
<path d="M16 9.226l-8-6.21-8 6.21v-2.532l8-6.21 8 6.21zM14 9v6h-4v-4h-4v4h-4v-6l6-4.5z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-location" viewBox="0 0 16 16">
|
||||
<title>location</title>
|
||||
<path d="M8 0c-2.761 0-5 2.239-5 5 0 5 5 11 5 11s5-6 5-11c0-2.761-2.239-5-5-5zM8 8c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-users" viewBox="0 0 18 16">
|
||||
<title>users</title>
|
||||
<path d="M12 12.041v-0.825c1.102-0.621 2-2.168 2-3.716 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h14c0-2.015-2.608-3.682-6-3.959z"></path>
|
||||
<path d="M5.112 12.427c0.864-0.565 1.939-0.994 3.122-1.256-0.235-0.278-0.449-0.588-0.633-0.922-0.475-0.863-0.726-1.813-0.726-2.748 0-1.344 0-2.614 0.478-3.653 0.464-1.008 1.299-1.633 2.488-1.867-0.264-1.195-0.968-1.98-2.841-1.98-3 0-3 2.015-3 4.5 0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h4.359c0.227-0.202 0.478-0.393 0.753-0.573z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-twitter" viewBox="0 0 16 16">
|
||||
<title>twitter</title>
|
||||
<path d="M16 3.538c-0.588 0.263-1.222 0.438-1.884 0.516 0.678-0.406 1.197-1.050 1.444-1.816-0.634 0.375-1.338 0.65-2.084 0.797-0.6-0.638-1.453-1.034-2.397-1.034-1.813 0-3.281 1.469-3.281 3.281 0 0.256 0.028 0.506 0.084 0.747-2.728-0.138-5.147-1.444-6.766-3.431-0.281 0.484-0.444 1.050-0.444 1.65 0 1.138 0.578 2.144 1.459 2.731-0.538-0.016-1.044-0.166-1.488-0.409 0 0.013 0 0.028 0 0.041 0 1.591 1.131 2.919 2.634 3.219-0.275 0.075-0.566 0.116-0.866 0.116-0.212 0-0.416-0.022-0.619-0.059 0.419 1.303 1.631 2.253 3.066 2.281-1.125 0.881-2.538 1.406-4.078 1.406-0.266 0-0.525-0.016-0.784-0.047 1.456 0.934 3.181 1.475 5.034 1.475 6.037 0 9.341-5.003 9.341-9.341 0-0.144-0.003-0.284-0.009-0.425 0.641-0.459 1.197-1.038 1.637-1.697z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-linkedin2" viewBox="0 0 16 16">
|
||||
<title>linkedin2</title>
|
||||
<path d="M6 6h2.767v1.418h0.040c0.385-0.691 1.327-1.418 2.732-1.418 2.921 0 3.461 1.818 3.461 4.183v4.817h-2.885v-4.27c0-1.018-0.021-2.329-1.5-2.329-1.502 0-1.732 1.109-1.732 2.255v4.344h-2.883v-9z"></path>
|
||||
<path d="M1 6h3v9h-3v-9z"></path>
|
||||
<path d="M4 3.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-xing2" viewBox="0 0 16 16">
|
||||
<title>xing2</title>
|
||||
<path d="M2.431 3.159c-0.138 0-0.256 0.050-0.316 0.144-0.059 0.1-0.050 0.225 0.013 0.353l1.559 2.7c0.003 0.006 0.003 0.009 0 0.013l-2.45 4.331c-0.063 0.128-0.059 0.256 0 0.353 0.059 0.094 0.163 0.156 0.3 0.156h2.306c0.344 0 0.513-0.234 0.628-0.447 0 0 2.397-4.241 2.491-4.406-0.009-0.016-1.588-2.766-1.588-2.766-0.116-0.203-0.287-0.431-0.644-0.431h-2.3z"></path>
|
||||
<path d="M12.125 0c-0.344 0-0.494 0.216-0.619 0.441 0 0-4.972 8.816-5.134 9.106 0.009 0.016 3.278 6.016 3.278 6.016 0.116 0.203 0.291 0.441 0.644 0.441h2.306c0.137 0 0.247-0.053 0.306-0.147 0.063-0.1 0.059-0.228-0.006-0.356l-3.25-5.947c-0.003-0.006-0.003-0.009 0-0.016l5.109-9.034c0.063-0.128 0.066-0.256 0.006-0.356-0.059-0.094-0.169-0.147-0.306-0.147h-2.334z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-cart" viewBox="0 0 16 16">
|
||||
<title>cart</title>
|
||||
<path d="M6 14.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
|
||||
<path d="M16 14.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
|
||||
<path d="M16 8v-6h-12c0-0.552-0.448-1-1-1h-3v1h2l0.751 6.438c-0.458 0.367-0.751 0.93-0.751 1.562 0 1.105 0.895 2 2 2h12v-1h-12c-0.552 0-1-0.448-1-1 0-0.003 0-0.007 0-0.010l13-1.99z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-warning" viewBox="0 0 16 16">
|
||||
<title>warning</title>
|
||||
<path d="M8 1.45l6.705 13.363h-13.409l6.705-13.363zM8 0c-0.345 0-0.69 0.233-0.951 0.698l-6.829 13.611c-0.523 0.93-0.078 1.691 0.989 1.691h13.583c1.067 0 1.512-0.761 0.989-1.691h0l-6.829-13.611c-0.262-0.465-0.606-0.698-0.951-0.698v0z"></path>
|
||||
<path d="M9 13c0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1z"></path>
|
||||
<path d="M8 11c-0.552 0-1-0.448-1-1v-3c0-0.552 0.448-1 1-1s1 0.448 1 1v3c0 0.552-0.448 1-1 1z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-coin-euro" viewBox="0 0 16 16">
|
||||
<title>coin-euro</title>
|
||||
<path d="M7.5 1c-4.142 0-7.5 3.358-7.5 7.5s3.358 7.5 7.5 7.5c4.142 0 7.5-3.358 7.5-7.5s-3.358-7.5-7.5-7.5zM7.5 14.5c-3.314 0-6-2.686-6-6s2.686-6 6-6c3.314 0 6 2.686 6 6s-2.686 6-6 6z"></path>
|
||||
<path d="M10.482 10.068c-0.239-0.139-0.545-0.058-0.684 0.181-0.27 0.463-0.767 0.751-1.298 0.751h-2c-0.652 0-1.208-0.418-1.414-1h2.414c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5h-2.5v-1h2.5c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5h-2.414c0.206-0.582 0.762-1 1.414-1h2c0.531 0 1.028 0.288 1.298 0.751 0.139 0.239 0.445 0.32 0.684 0.181s0.32-0.445 0.181-0.684c-0.448-0.77-1.277-1.249-2.162-1.249h-2c-1.207 0-2.217 0.86-2.45 2h-0.55c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h0.5v1h-0.5c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h0.55c0.232 1.14 1.242 2 2.45 2h2c0.886 0 1.714-0.478 2.162-1.249 0.139-0.239 0.058-0.545-0.181-0.684z"></path>
|
||||
</symbol>
|
||||
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
@ -1,47 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\Math;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class Math 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()
|
||||
{
|
||||
$mathSettings = $this->settings['settings']['plugins']['math'];
|
||||
|
||||
if($mathSettings['tool'] == 'mathjax')
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addJS('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/latest.js?config=TeX-MML-AM_CHTML');
|
||||
}
|
||||
|
||||
if($mathSettings['tool'] == 'katex')
|
||||
{
|
||||
$this->addJS('/math/public/katex.min.js');
|
||||
$this->addJS('/math/public/auto-render.min.js');
|
||||
$this->addCSS('/math/public/katex.min.css');
|
||||
|
||||
/* initialize autorendering of page only in frontend */
|
||||
if (strpos($this->getPath(), 'tm/content') === false)
|
||||
{
|
||||
$this->addInlineJs('renderMathInElement(document.body);');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Plugins\Math;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
|
||||
class Math 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()
|
||||
{
|
||||
$mathSettings = $this->settings['settings']['plugins']['math'];
|
||||
|
||||
if($mathSettings['tool'] == 'mathjax')
|
||||
{
|
||||
/* add external CSS and JavaScript */
|
||||
$this->addJS('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/latest.js?config=TeX-MML-AM_CHTML');
|
||||
}
|
||||
|
||||
if($mathSettings['tool'] == 'katex')
|
||||
{
|
||||
$this->addJS('/math/public/katex.min.js');
|
||||
$this->addJS('/math/public/auto-render.min.js');
|
||||
$this->addCSS('/math/public/katex.min.css');
|
||||
|
||||
/* initialize autorendering of page only in frontend */
|
||||
if (strpos($this->getPath(), 'tm/content') === false)
|
||||
{
|
||||
$this->addInlineJs('renderMathInElement(document.body);');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
name: Math
|
||||
version: 1.0.2
|
||||
description: Adds support for katex and mathjax.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://mathjax.org/
|
||||
licence: Apache 2.0 / MIT
|
||||
|
||||
settings:
|
||||
tool: none
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
tool:
|
||||
type: radio
|
||||
label: Choose Your Tool
|
||||
options:
|
||||
none: None
|
||||
katex: KaTex
|
||||
name: Math
|
||||
version: 1.0.2
|
||||
description: Adds support for katex and mathjax.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://mathjax.org/
|
||||
licence: Apache 2.0 / MIT
|
||||
|
||||
settings:
|
||||
tool: none
|
||||
|
||||
forms:
|
||||
fields:
|
||||
|
||||
tool:
|
||||
type: radio
|
||||
label: Choose Your Tool
|
||||
options:
|
||||
none: None
|
||||
katex: KaTex
|
||||
mathjax: MathJax
|
@ -1,140 +1,140 @@
|
||||
# [<img src="https://khan.github.io/KaTeX/katex-logo.svg" width="130" alt="KaTeX">](https://khan.github.io/KaTeX/)
|
||||
[![Build Status](https://travis-ci.org/Khan/KaTeX.svg?branch=master)](https://travis-ci.org/Khan/KaTeX)
|
||||
[![codecov](https://codecov.io/gh/Khan/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/Khan/KaTeX)
|
||||
[![Join the chat at https://gitter.im/Khan/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Khan/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
|
||||
|
||||
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
|
||||
* **Print quality:** KaTeX’s layout is based on Donald Knuth’s TeX, the gold standard for math typesetting.
|
||||
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
|
||||
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
|
||||
|
||||
KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. More information can be found on the [list of supported commands](https://khan.github.io/KaTeX/function-support.html) and on the [wiki](https://github.com/khan/katex/wiki).
|
||||
|
||||
## Usage
|
||||
|
||||
You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.css" integrity="sha384-VEnyslhHLHiYPca9KFkBB3CMeslnM9CzwjxsEbZTeA21JBm7tdLwKoZmCt3cZTYD" crossorigin="anonymous">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.js" integrity="sha384-O4hpKqcplNCe+jLuBVEXC10Rn1QEqAmX98lKAIFBEDxZI0a+6Z2w2n8AEtQbR4CD" crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
#### In-browser rendering
|
||||
|
||||
Call `katex.render` with a TeX expression and a DOM element to render into:
|
||||
|
||||
```js
|
||||
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element);
|
||||
```
|
||||
|
||||
If KaTeX can't parse the expression, it throws a `katex.ParseError` error.
|
||||
|
||||
#### Server side rendering or rendering to a string
|
||||
|
||||
To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`:
|
||||
|
||||
```js
|
||||
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}");
|
||||
// '<span class="katex">...</span>'
|
||||
```
|
||||
|
||||
Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression.
|
||||
|
||||
#### Security
|
||||
|
||||
Any HTML generated by KaTeX *should* be safe from `<script>` or other code
|
||||
injection attacks.
|
||||
(See `maxSize` below for preventing large width/height visual affronts.)
|
||||
Of course, it is always a good idea to sanitize the HTML, though you will need
|
||||
a rather generous whitelist (including some of SVG and MathML) to support
|
||||
all of KaTeX.
|
||||
|
||||
#### Handling errors
|
||||
|
||||
If KaTeX encounters an error (invalid or unsupported LaTeX), then it will
|
||||
throw an exception of type `katex.ParseError`. The message in this error
|
||||
includes some of the LaTeX source code, so needs to be escaped if you want
|
||||
to render it to HTML. In particular, you should convert `&`, `<`, `>`
|
||||
characters to `&`, `<`, `>` (e.g., using `_.escape`)
|
||||
before including either LaTeX source code or exception messages in your
|
||||
HTML/DOM. (Failure to escape in this way makes a `<script>` injection
|
||||
attack possible if your LaTeX source is untrusted.)
|
||||
|
||||
#### Rendering options
|
||||
|
||||
You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are:
|
||||
|
||||
- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
|
||||
- `throwOnError`: `boolean`. If `true`, KaTeX will throw a `ParseError` when it encounters an unsupported command. If `false`, KaTeX will render the unsupported command as text in the color given by `errorColor`. (default: `true`)
|
||||
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color which unsupported commands are rendered in. (default: `#cc0000`)
|
||||
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters).
|
||||
- `colorIsTextColor`: `boolean`. If `true`, `\color` will work like LaTeX's `\textcolor`, and take two arguments (e.g., `\color{blue}{hello}`), which restores the old behavior of KaTeX (pre-0.8.0). If `false` (the default), `\color` will work like LaTeX's `\color`, and take one argument (e.g., `\color{blue}hello`). In both cases, `\textcolor` works as in LaTeX (e.g., `\textcolor{blue}{hello}`).
|
||||
- `maxSize`: `number`. If non-zero, all user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped to `maxSize` ems. Otherwise, users can make elements and spaces arbitrarily large (the default behavior).
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
katex.render("c = \\pm\\sqrt{a^2 + b^2}\\in\\RR", element, {
|
||||
displayMode: true,
|
||||
macros: {
|
||||
"\\RR": "\\mathbb{R}"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Automatic rendering of math on a page
|
||||
|
||||
Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
|
||||
|
||||
#### Font size and lengths
|
||||
|
||||
By default, KaTeX math is rendered in a 1.21× larger font than the surrounding
|
||||
context, which makes super- and subscripts easier to read. You can control
|
||||
this using CSS, for example:
|
||||
|
||||
```css
|
||||
.katex { font-size: 1.1em; }
|
||||
```
|
||||
|
||||
KaTeX supports all TeX units, including absolute units like `cm` and `in`.
|
||||
Absolute units are currently scaled relative to the default TeX font size of
|
||||
10pt, so that `\kern1cm` produces the same results as `\kern2.845275em`.
|
||||
As a result, relative and absolute units are both uniformly scaled relative
|
||||
to LaTeX with a 10pt font; for example, the rectangle `\rule{1cm}{1em}` has
|
||||
the same aspect ratio in KaTeX as in LaTeX. However, because most browsers
|
||||
default to a larger font size, this typically means that a 1cm kern in KaTeX
|
||||
will appear larger than 1cm in browser units.
|
||||
|
||||
### Common Issues
|
||||
- Many Markdown preprocessors, such as the one that Jekyll and GitHub Pages use,
|
||||
have a "smart quotes" feature. This changes `'` to `’` which is an issue for
|
||||
math containing primes, e.g. `f'`. This can be worked around by defining a
|
||||
single character macro which changes them back, e.g. `{"’", "'"}`.
|
||||
- KaTeX follows LaTeX's rendering of `aligned` and `matrix` environments unlike
|
||||
MathJax. When displaying fractions one above another in these vertical
|
||||
layouts there may not be enough space between rows for people who are used to
|
||||
MathJax's rendering. The distance between rows can be adjusted by using
|
||||
`\\[0.1em]` instead of the standard line separator distance.
|
||||
- KaTeX does not support the `align` environment because LaTeX doesn't support
|
||||
`align` in math mode. The `aligned` environment offers the same functionality
|
||||
but in math mode, so use that instead or define a macro that maps `align` to
|
||||
`aligned`.
|
||||
|
||||
## Libraries
|
||||
|
||||
### Angular2+
|
||||
- [ng-katex](https://github.com/garciparedes/ng-katex) Angular module to write beautiful math expressions with TeX syntax boosted by KaTeX library
|
||||
|
||||
### Ruby
|
||||
|
||||
- [katex-ruby](https://github.com/glebm/katex-ruby) Provides server-side rendering and integration with popular Ruby web frameworks (Rails, Hanami, and anything that uses Sprockets).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
# [<img src="https://khan.github.io/KaTeX/katex-logo.svg" width="130" alt="KaTeX">](https://khan.github.io/KaTeX/)
|
||||
[![Build Status](https://travis-ci.org/Khan/KaTeX.svg?branch=master)](https://travis-ci.org/Khan/KaTeX)
|
||||
[![codecov](https://codecov.io/gh/Khan/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/Khan/KaTeX)
|
||||
[![Join the chat at https://gitter.im/Khan/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Khan/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
|
||||
|
||||
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
|
||||
* **Print quality:** KaTeX’s layout is based on Donald Knuth’s TeX, the gold standard for math typesetting.
|
||||
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
|
||||
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
|
||||
|
||||
KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. More information can be found on the [list of supported commands](https://khan.github.io/KaTeX/function-support.html) and on the [wiki](https://github.com/khan/katex/wiki).
|
||||
|
||||
## Usage
|
||||
|
||||
You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.css" integrity="sha384-VEnyslhHLHiYPca9KFkBB3CMeslnM9CzwjxsEbZTeA21JBm7tdLwKoZmCt3cZTYD" crossorigin="anonymous">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.js" integrity="sha384-O4hpKqcplNCe+jLuBVEXC10Rn1QEqAmX98lKAIFBEDxZI0a+6Z2w2n8AEtQbR4CD" crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
#### In-browser rendering
|
||||
|
||||
Call `katex.render` with a TeX expression and a DOM element to render into:
|
||||
|
||||
```js
|
||||
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element);
|
||||
```
|
||||
|
||||
If KaTeX can't parse the expression, it throws a `katex.ParseError` error.
|
||||
|
||||
#### Server side rendering or rendering to a string
|
||||
|
||||
To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`:
|
||||
|
||||
```js
|
||||
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}");
|
||||
// '<span class="katex">...</span>'
|
||||
```
|
||||
|
||||
Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression.
|
||||
|
||||
#### Security
|
||||
|
||||
Any HTML generated by KaTeX *should* be safe from `<script>` or other code
|
||||
injection attacks.
|
||||
(See `maxSize` below for preventing large width/height visual affronts.)
|
||||
Of course, it is always a good idea to sanitize the HTML, though you will need
|
||||
a rather generous whitelist (including some of SVG and MathML) to support
|
||||
all of KaTeX.
|
||||
|
||||
#### Handling errors
|
||||
|
||||
If KaTeX encounters an error (invalid or unsupported LaTeX), then it will
|
||||
throw an exception of type `katex.ParseError`. The message in this error
|
||||
includes some of the LaTeX source code, so needs to be escaped if you want
|
||||
to render it to HTML. In particular, you should convert `&`, `<`, `>`
|
||||
characters to `&`, `<`, `>` (e.g., using `_.escape`)
|
||||
before including either LaTeX source code or exception messages in your
|
||||
HTML/DOM. (Failure to escape in this way makes a `<script>` injection
|
||||
attack possible if your LaTeX source is untrusted.)
|
||||
|
||||
#### Rendering options
|
||||
|
||||
You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are:
|
||||
|
||||
- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
|
||||
- `throwOnError`: `boolean`. If `true`, KaTeX will throw a `ParseError` when it encounters an unsupported command. If `false`, KaTeX will render the unsupported command as text in the color given by `errorColor`. (default: `true`)
|
||||
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color which unsupported commands are rendered in. (default: `#cc0000`)
|
||||
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters).
|
||||
- `colorIsTextColor`: `boolean`. If `true`, `\color` will work like LaTeX's `\textcolor`, and take two arguments (e.g., `\color{blue}{hello}`), which restores the old behavior of KaTeX (pre-0.8.0). If `false` (the default), `\color` will work like LaTeX's `\color`, and take one argument (e.g., `\color{blue}hello`). In both cases, `\textcolor` works as in LaTeX (e.g., `\textcolor{blue}{hello}`).
|
||||
- `maxSize`: `number`. If non-zero, all user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped to `maxSize` ems. Otherwise, users can make elements and spaces arbitrarily large (the default behavior).
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
katex.render("c = \\pm\\sqrt{a^2 + b^2}\\in\\RR", element, {
|
||||
displayMode: true,
|
||||
macros: {
|
||||
"\\RR": "\\mathbb{R}"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Automatic rendering of math on a page
|
||||
|
||||
Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
|
||||
|
||||
#### Font size and lengths
|
||||
|
||||
By default, KaTeX math is rendered in a 1.21× larger font than the surrounding
|
||||
context, which makes super- and subscripts easier to read. You can control
|
||||
this using CSS, for example:
|
||||
|
||||
```css
|
||||
.katex { font-size: 1.1em; }
|
||||
```
|
||||
|
||||
KaTeX supports all TeX units, including absolute units like `cm` and `in`.
|
||||
Absolute units are currently scaled relative to the default TeX font size of
|
||||
10pt, so that `\kern1cm` produces the same results as `\kern2.845275em`.
|
||||
As a result, relative and absolute units are both uniformly scaled relative
|
||||
to LaTeX with a 10pt font; for example, the rectangle `\rule{1cm}{1em}` has
|
||||
the same aspect ratio in KaTeX as in LaTeX. However, because most browsers
|
||||
default to a larger font size, this typically means that a 1cm kern in KaTeX
|
||||
will appear larger than 1cm in browser units.
|
||||
|
||||
### Common Issues
|
||||
- Many Markdown preprocessors, such as the one that Jekyll and GitHub Pages use,
|
||||
have a "smart quotes" feature. This changes `'` to `’` which is an issue for
|
||||
math containing primes, e.g. `f'`. This can be worked around by defining a
|
||||
single character macro which changes them back, e.g. `{"’", "'"}`.
|
||||
- KaTeX follows LaTeX's rendering of `aligned` and `matrix` environments unlike
|
||||
MathJax. When displaying fractions one above another in these vertical
|
||||
layouts there may not be enough space between rows for people who are used to
|
||||
MathJax's rendering. The distance between rows can be adjusted by using
|
||||
`\\[0.1em]` instead of the standard line separator distance.
|
||||
- KaTeX does not support the `align` environment because LaTeX doesn't support
|
||||
`align` in math mode. The `aligned` environment offers the same functionality
|
||||
but in math mode, so use that instead or define a macro that maps `align` to
|
||||
`aligned`.
|
||||
|
||||
## Libraries
|
||||
|
||||
### Angular2+
|
||||
- [ng-katex](https://github.com/garciparedes/ng-katex) Angular module to write beautiful math expressions with TeX syntax boosted by KaTeX library
|
||||
|
||||
### Ruby
|
||||
|
||||
- [katex-ruby](https://github.com/glebm/katex-ruby) Provides server-side rendering and integration with popular Ruby web frameworks (Rails, Hanami, and anything that uses Sprockets).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
|
87
plugins/search/index.php
Normal file
87
plugins/search/index.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\search;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
use \Typemill\Models\Write;
|
||||
use \Typemill\Models\WriteCache;
|
||||
|
||||
class Index extends Plugin
|
||||
{
|
||||
public static function getSubscribedEvents(){}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$write = new Write();
|
||||
|
||||
$index = $write->getFile('cache', 'index.json');
|
||||
if(!$index)
|
||||
{
|
||||
$this->createIndex();
|
||||
$index = $write->getFile('cache', 'index.json');
|
||||
}
|
||||
|
||||
return $this->returnJson($index);
|
||||
}
|
||||
|
||||
private function createIndex()
|
||||
{
|
||||
$write = new WriteCache();
|
||||
|
||||
# get content structure
|
||||
$structure = $write->getCache('cache', 'structure.txt');
|
||||
|
||||
# get data for search-index
|
||||
$index = $this->getAllContent($structure, $write);
|
||||
|
||||
# store the index file here
|
||||
$write->writeFile('cache', 'index.json', json_encode($index, JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
|
||||
private function getAllContent($structure, $write, $index = NULL)
|
||||
{
|
||||
foreach($structure as $item)
|
||||
{
|
||||
if($item->elementType == "folder")
|
||||
{
|
||||
if($item->fileType == 'md')
|
||||
{
|
||||
$page = $write->getFileWithPath('content' . $item->path . DIRECTORY_SEPARATOR . 'index.md');
|
||||
$pageArray = $this->getPageContentArray($page, $item->urlAbs);
|
||||
$index[$pageArray['url']] = $pageArray;
|
||||
}
|
||||
|
||||
$index = $this->getAllContent($item->folderContent, $write, $index);
|
||||
}
|
||||
else
|
||||
{
|
||||
$page = $write->getFileWithPath('content' . $item->path);
|
||||
$pageArray = $this->getPageContentArray($page, $item->urlAbs);
|
||||
$index[$pageArray['url']] = $pageArray;
|
||||
}
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
private function getPageContentArray($page, $url)
|
||||
{
|
||||
$parts = explode("\n", $page, 2);
|
||||
|
||||
# get the title / headline
|
||||
$title = trim($parts[0], '# ');
|
||||
$title = str_replace(["\r\n", "\n", "\r"],' ', $title);
|
||||
|
||||
# get and cleanup the content
|
||||
$content = $parts[1];
|
||||
$content = strip_tags($content);
|
||||
$content = str_replace(["\r\n", "\n", "\r"],' ', $content);
|
||||
|
||||
$pageContent = [
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'url' => $url
|
||||
];
|
||||
|
||||
return $pageContent;
|
||||
}
|
||||
}
|
19
plugins/search/public/lunr-licence.md
Normal file
19
plugins/search/public/lunr-licence.md
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (C) 2013 by Oliver Nightingale
|
||||
|
||||
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.
|
6
plugins/search/public/lunr.min.js
vendored
Normal file
6
plugins/search/public/lunr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
64
plugins/search/public/search.css
Normal file
64
plugins/search/public/search.css
Normal file
@ -0,0 +1,64 @@
|
||||
.searchContainer{
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.searchContainer input{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 1rem;
|
||||
float: left;
|
||||
padding-left: 15px;
|
||||
border-radius: 2px;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
.searchContainer button{
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border: none;
|
||||
background: #232833;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
color: #fff;
|
||||
font-size: 10pt;
|
||||
margin-left: -50px;
|
||||
}
|
||||
.searchContainer button:hover,.searchContainer button:focus, .searchContainer button:active{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#searchresult{
|
||||
}
|
||||
.resultwrapper{
|
||||
}
|
||||
button#closeSearchResult{
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
margin: 10px;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
font-size: 1rem;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
padding: 15px;
|
||||
}
|
||||
button#closeSearchResult:hover,#closeSearchResult:focus{
|
||||
cursor: pointer;
|
||||
}
|
||||
.resultlist{
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style:none;
|
||||
}
|
||||
.resultitem{
|
||||
|
||||
}
|
||||
.resultheader{
|
||||
|
||||
}
|
||||
.resultsnippet{
|
||||
|
||||
}
|
115
plugins/search/public/search.js
Normal file
115
plugins/search/public/search.js
Normal file
@ -0,0 +1,115 @@
|
||||
var searchField = document.getElementById("searchField");
|
||||
var searchButton = document.getElementById("searchButton");
|
||||
|
||||
if(searchField && searchButton)
|
||||
{
|
||||
var searchIndex = false;
|
||||
var documents = false;
|
||||
var holdcontent = false;
|
||||
var contentwrapper = false;
|
||||
|
||||
searchField.addEventListener("focus", function(event){
|
||||
|
||||
if(!searchIndex)
|
||||
{
|
||||
myaxios.get('/indexrs51gfe2o2')
|
||||
.then(function (response) {
|
||||
|
||||
documents = JSON.parse(response.data);
|
||||
|
||||
searchIndex = lunr(function() {
|
||||
this.ref("id");
|
||||
this.field("title", { boost: 10 });
|
||||
this.field("content");
|
||||
for (var key in documents){
|
||||
this.add({
|
||||
"id": documents[key].url,
|
||||
"title": documents[key].title,
|
||||
"content": documents[key].content
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
.catch(function (error) {});
|
||||
}
|
||||
});
|
||||
|
||||
searchButton.addEventListener("click", function(event){
|
||||
event.preventDefault();
|
||||
|
||||
var term = document.getElementById('searchField').value;
|
||||
var results = searchIndex.search(term);
|
||||
|
||||
var resultPages = results.map(function (match) {
|
||||
return documents[match.ref];
|
||||
});
|
||||
|
||||
resultsString = "<div class='resultwrapper'><h1>Result for " + term + "</h1>";
|
||||
resultsString += "<button id='closeSearchResult'>close</button>";
|
||||
resultsString += "<ul class='resultlist'>";
|
||||
resultPages.forEach(function (r) {
|
||||
resultsString += "<li class='resultitem'>";
|
||||
resultsString += "<a class='resultheader' href='" + r.url + "?q=" + term + "'><h3>" + r.title + "</h3></a>";
|
||||
resultsString += "<div class='resultsnippet'>" + r.content.substring(0, 200) + " ...</div>";
|
||||
resultsString += "</li>"
|
||||
});
|
||||
resultsString += "</ul></div>";
|
||||
|
||||
if(!holdcontent)
|
||||
{
|
||||
contentwrapper = document.getElementById("searchresult").parentNode;
|
||||
holdcontent = contentwrapper.innerHTML;
|
||||
}
|
||||
|
||||
contentwrapper.innerHTML = resultsString;
|
||||
|
||||
document.getElementById("closeSearchResult").addEventListener("click", function(event){
|
||||
contentwrapper.innerHTML = holdcontent;
|
||||
});
|
||||
|
||||
}, false);
|
||||
}
|
||||
|
||||
/*
|
||||
var searchIndex = lunr(function() {
|
||||
this.ref("id");
|
||||
this.field("title", { boost: 10 });
|
||||
this.field("content");
|
||||
for (var key in window.pages) {
|
||||
this.add({
|
||||
"id": key,
|
||||
"title": pages[key].title,
|
||||
"content": pages[key].content
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function getQueryVariable(variable) {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if (pair[0] === variable) {
|
||||
return decodeURIComponent(pair[1].replace(/\+/g, "%20"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var searchTerm = getQueryVariable("q");
|
||||
// creation of searchIndex from earlier example
|
||||
var results = searchIndex.search(searchTerm);
|
||||
var resultPages = results.map(function (match) {
|
||||
return pages[match.ref];
|
||||
});
|
||||
|
||||
// resultPages from previous example
|
||||
resultsString = "";
|
||||
resultPages.forEach(function (r) {
|
||||
resultsString += "<li>";
|
||||
resultsString += "<a class='result' href='" + r.url + "?q=" + searchTerm + "'><h3>" + r.title + "</h3></a>";
|
||||
resultsString += "<div class='snippet'>" + r.content.substring(0, 200) + "</div>";
|
||||
resultsString += "</li>"
|
||||
});
|
||||
document.querySelector("#search-results").innerHTML = resultsString;
|
||||
*/
|
128
plugins/search/search.php
Normal file
128
plugins/search/search.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\search;
|
||||
|
||||
use \Typemill\Plugin;
|
||||
use \Typemill\Models\Write;
|
||||
|
||||
class Search extends index
|
||||
{
|
||||
protected $item;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onSettingsLoaded' => 'onsettingsLoaded',
|
||||
'onContentArrayLoaded' => 'onContentArrayLoaded',
|
||||
'onPageReady' => 'onPageReady',
|
||||
'onPagePublished' => 'onPagePublished',
|
||||
'onPageUnpublished' => 'onPageUnpublished',
|
||||
'onPageSorted' => 'onPageSorted',
|
||||
'onPageDeleted' => 'onPageDeleted',
|
||||
);
|
||||
}
|
||||
|
||||
# get search.json with route
|
||||
# update search.json on publish
|
||||
|
||||
public static function addNewRoutes()
|
||||
{
|
||||
# the route for the api calls
|
||||
return array(
|
||||
array(
|
||||
'httpMethod' => 'get',
|
||||
'route' => '/indexrs51gfe2o2',
|
||||
'class' => 'Plugins\search\index:index'
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function onSettingsLoaded($settings)
|
||||
{
|
||||
$this->settings = $settings->getData();
|
||||
}
|
||||
|
||||
# at any of theses events, delete the old search index
|
||||
public function onPagePublished($item)
|
||||
{
|
||||
$this->deleteSearchIndex();
|
||||
}
|
||||
public function onPageUnpublished($item)
|
||||
{
|
||||
$this->deleteSearchIndex();
|
||||
}
|
||||
public function onPageSorted($inputParams)
|
||||
{
|
||||
$this->deleteSearchIndex();
|
||||
}
|
||||
public function onPageDeleted($item)
|
||||
{
|
||||
$this->deleteSearchIndex();
|
||||
}
|
||||
|
||||
private function deleteSearchIndex()
|
||||
{
|
||||
$write = new Write();
|
||||
|
||||
# store the index file here
|
||||
$write->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'index.json');
|
||||
}
|
||||
|
||||
public function onContentArrayLoaded($contentArray)
|
||||
{
|
||||
# get content array
|
||||
$content = $contentArray->getData();
|
||||
$settings = $this->getPluginSettings('search');
|
||||
$salt = "asPx9Derf2";
|
||||
|
||||
# activate axios and vue in frontend
|
||||
$this->activateAxios();
|
||||
$this->activateVue();
|
||||
|
||||
# add the css and vue application
|
||||
$this->addCSS('/search/public/search.css');
|
||||
$this->addJS('/search/public/lunr.min.js');
|
||||
$this->addJS('/search/public/search.js');
|
||||
|
||||
# simple security for first request
|
||||
$secret = time();
|
||||
$secret = substr($secret,0,-1);
|
||||
$secret = md5($secret . $salt);
|
||||
|
||||
# simple csrf protection with a session for long following requests
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
$length = 32;
|
||||
$token = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
|
||||
$_SESSION['search'] = $token;
|
||||
$_SESSION['search-expire'] = time() + 1300; # 60 seconds * 30 minutes
|
||||
|
||||
# create div for vue app
|
||||
$search = '<div data-access="' . $secret . '" data-token="' . $token . '" id="searchresult"></div>';
|
||||
|
||||
# create content type
|
||||
$search = Array
|
||||
(
|
||||
'rawHtml' => $search,
|
||||
'allowRawHtmlInSafeMode' => true,
|
||||
'autobreak' => 1
|
||||
);
|
||||
|
||||
$content[] = $search;
|
||||
|
||||
$contentArray->setData($content);
|
||||
}
|
||||
|
||||
public function onPageReady($page)
|
||||
{
|
||||
$pageData = $page->getData($page);
|
||||
|
||||
$pageData['widgets']['search'] = '<div class="searchContainer" id="searchForm">'.
|
||||
'<input id="searchField" type="text" placeholder="search ..." />'.
|
||||
'<button id="searchButton" type="button">GO</button>'.
|
||||
'</div>';
|
||||
$page->setData($pageData);
|
||||
}
|
||||
}
|
6
plugins/search/search.yaml
Normal file
6
plugins/search/search.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: Search
|
||||
version: 1.0.0
|
||||
description: Adds a search to typemill with lunr.js.
|
||||
author: Sebastian Schürmanns
|
||||
homepage: https://typemill.net
|
||||
licence: MIT
|
206
readme.md
206
readme.md
@ -1,104 +1,104 @@
|
||||
# About TYPEMILL
|
||||
|
||||
TYPEMILL is a small flat file cms created for editors and writers. It provides a author-friendly dashboard and a visual-block-editor for markdown based on vue.js. Use TYPEMILL for manuals, documentations, web-books and similar publications. The website http://typemill.net itself is an example for TYPEMILL.
|
||||
|
||||
![TYPEMILL Screenshot](https://typemill.net/media/tm-toc.gif)
|
||||
|
||||
## Features
|
||||
|
||||
* Creates a website based on markdown files.
|
||||
* Provides an author-friendly visual markdown editor (work in progress, based on VUE.js).
|
||||
* Provides a pure markdown editing mode.
|
||||
* Markdown supports table of contents (TOC), tables, footnotes, abbreviations and definition lists.
|
||||
* Create and sort pages with drag & drop in the navigation.
|
||||
* Configure the system, the themes and the plugins in the dashboard.
|
||||
* Create and manage users.
|
||||
* Develop configurable plugins with the Symfony Event Dispatcher.
|
||||
* Develop configurable themes with TWIG.
|
||||
* Allows super easy backend and frontend forms with simple YAML-files.
|
||||
* Ships with a fully responsive standard theme
|
||||
* Ships with plugins for
|
||||
* MathJax and KaTeX.
|
||||
* Code highlighting.
|
||||
* Matomo/Piwik and Google Analytics.
|
||||
* Cookie Consent.
|
||||
|
||||
## Requirements
|
||||
|
||||
* PHP 7+
|
||||
* Apache server
|
||||
* mod_rewrite and htaccess
|
||||
|
||||
If you run a linux system, then please double check that mod_rewrite and htaccess are active!!!
|
||||
|
||||
## Installation
|
||||
|
||||
Download TYPEMILL from the [TYPEMILL website](http://typemill.net), unzip the files and you are done.
|
||||
|
||||
If you are a developer, you can also clone this repository. To do so, open your command line, go to your project folder (e.g. htdocs) and type:
|
||||
|
||||
git clone git://github.com/trendschau/typemill.git
|
||||
|
||||
The GitHub-version has no vendor-folder, so you have to update and include all libraries and dependencies with composer. To do so, open your command line, go to your TYPEMILL folder and type:
|
||||
|
||||
composer update
|
||||
|
||||
If you did not use composer before, please go to the [composer website](http://getcomposer.org) and start to learn.
|
||||
|
||||
To run TYPEMILL on a **live** system, simply upload the files to your server
|
||||
|
||||
## Make Folders Writable.
|
||||
|
||||
Make sure that the following folders and all their files are writable (permission 774 recursively):
|
||||
|
||||
* cache
|
||||
* content
|
||||
* media
|
||||
* settings
|
||||
|
||||
You can use your ftp-software for that.
|
||||
|
||||
## Setup
|
||||
|
||||
Please go to `your-typemill-website.com/setup`, create an initial user and then setup your system in the author panel.
|
||||
|
||||
## Login
|
||||
|
||||
You can find your login screen under `/tm/login` or simply go to `/setup` and you will be redirected to the login-page.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can read the full documentation for writers, for theme developers and for plugin developers on the [TYPEMILL website](http://typemill.net).
|
||||
|
||||
## Support
|
||||
|
||||
This is an open source project. I love it and I spend about 20 hours a week on it (starting in 2017). There is no business model right now, but you can hire me for implementation or simply support this project if you like.
|
||||
|
||||
Donate: https://www.paypal.me/typemill
|
||||
|
||||
## Contribute
|
||||
|
||||
Typemill is still in an early stage and contributions are highly welcome. Here are some ideas for non-coder:
|
||||
|
||||
* Find bugs and errors (open a new issue on github for it).
|
||||
* Improve the documentation.
|
||||
* Describe some missing features and explain, why they are important for other users.
|
||||
* Write a blog post about typemill.
|
||||
|
||||
Some ideas for devs (please fork this repository make your changes and create a pull request):
|
||||
|
||||
* Fix a bug.
|
||||
* Create a nice theme.
|
||||
* Create a new plugin.
|
||||
* Improve the CSS-code with BEM or utility-css (e.g. Tailwind) and make it modular.
|
||||
* Rebuild the theme with the new css-grid feature.
|
||||
* Improve the accessibility of html and css.
|
||||
* Help to establish autotests with selenium or cypress.
|
||||
* Write unit-tests.
|
||||
* Write an auto-update functionality.
|
||||
|
||||
For hints, questions, problems and support, please open up a new issue on GitHub.
|
||||
|
||||
## Licence
|
||||
|
||||
# About TYPEMILL
|
||||
|
||||
TYPEMILL is a small flat file cms created for editors and writers. It provides a author-friendly dashboard and a visual-block-editor for markdown based on vue.js. Use TYPEMILL for manuals, documentations, web-books and similar publications. The website http://typemill.net itself is an example for TYPEMILL.
|
||||
|
||||
![TYPEMILL Screenshot](https://typemill.net/media/tm-toc.gif)
|
||||
|
||||
## Features
|
||||
|
||||
* Creates a website based on markdown files.
|
||||
* Provides an author-friendly visual markdown editor (work in progress, based on VUE.js).
|
||||
* Provides a pure markdown editing mode.
|
||||
* Markdown supports table of contents (TOC), tables, footnotes, abbreviations and definition lists.
|
||||
* Create and sort pages with drag & drop in the navigation.
|
||||
* Configure the system, the themes and the plugins in the dashboard.
|
||||
* Create and manage users.
|
||||
* Develop configurable plugins with the Symfony Event Dispatcher.
|
||||
* Develop configurable themes with TWIG.
|
||||
* Allows super easy backend and frontend forms with simple YAML-files.
|
||||
* Ships with a fully responsive standard theme
|
||||
* Ships with plugins for
|
||||
* MathJax and KaTeX.
|
||||
* Code highlighting.
|
||||
* Matomo/Piwik and Google Analytics.
|
||||
* Cookie Consent.
|
||||
|
||||
## Requirements
|
||||
|
||||
* PHP 7+
|
||||
* Apache server
|
||||
* mod_rewrite and htaccess
|
||||
|
||||
If you run a linux system, then please double check that mod_rewrite and htaccess are active!!!
|
||||
|
||||
## Installation
|
||||
|
||||
Download TYPEMILL from the [TYPEMILL website](http://typemill.net), unzip the files and you are done.
|
||||
|
||||
If you are a developer, you can also clone this repository. To do so, open your command line, go to your project folder (e.g. htdocs) and type:
|
||||
|
||||
git clone git://github.com/trendschau/typemill.git
|
||||
|
||||
The GitHub-version has no vendor-folder, so you have to update and include all libraries and dependencies with composer. To do so, open your command line, go to your TYPEMILL folder and type:
|
||||
|
||||
composer update
|
||||
|
||||
If you did not use composer before, please go to the [composer website](http://getcomposer.org) and start to learn.
|
||||
|
||||
To run TYPEMILL on a **live** system, simply upload the files to your server
|
||||
|
||||
## Make Folders Writable.
|
||||
|
||||
Make sure that the following folders and all their files are writable (permission 774 recursively):
|
||||
|
||||
* cache
|
||||
* content
|
||||
* media
|
||||
* settings
|
||||
|
||||
You can use your ftp-software for that.
|
||||
|
||||
## Setup
|
||||
|
||||
Please go to `your-typemill-website.com/setup`, create an initial user and then setup your system in the author panel.
|
||||
|
||||
## Login
|
||||
|
||||
You can find your login screen under `/tm/login` or simply go to `/setup` and you will be redirected to the login-page.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can read the full documentation for writers, for theme developers and for plugin developers on the [TYPEMILL website](http://typemill.net).
|
||||
|
||||
## Support
|
||||
|
||||
This is an open source project. I love it and I spend about 20 hours a week on it (starting in 2017). There is no business model right now, but you can hire me for implementation or simply support this project if you like.
|
||||
|
||||
Donate: https://www.paypal.me/typemill
|
||||
|
||||
## Contribute
|
||||
|
||||
Typemill is still in an early stage and contributions are highly welcome. Here are some ideas for non-coder:
|
||||
|
||||
* Find bugs and errors (open a new issue on github for it).
|
||||
* Improve the documentation.
|
||||
* Describe some missing features and explain, why they are important for other users.
|
||||
* Write a blog post about typemill.
|
||||
|
||||
Some ideas for devs (please fork this repository make your changes and create a pull request):
|
||||
|
||||
* Fix a bug.
|
||||
* Create a nice theme.
|
||||
* Create a new plugin.
|
||||
* Improve the CSS-code with BEM or utility-css (e.g. Tailwind) and make it modular.
|
||||
* Rebuild the theme with the new css-grid feature.
|
||||
* Improve the accessibility of html and css.
|
||||
* Help to establish autotests with selenium or cypress.
|
||||
* Write unit-tests.
|
||||
* Write an auto-update functionality.
|
||||
|
||||
For hints, questions, problems and support, please open up a new issue on GitHub.
|
||||
|
||||
## Licence
|
||||
|
||||
TYPEMILL is published under MIT licence. Please check the licence of the included libraries, too.
|
@ -1,81 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Assets
|
||||
{
|
||||
protected $baseUrl;
|
||||
|
||||
public function __construct($baseUrl)
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->JS = array();
|
||||
$this->CSS = array();
|
||||
$this->inlineJS = array();
|
||||
$this->inlineCSS = array();
|
||||
}
|
||||
|
||||
public function addCSS($CSS)
|
||||
{
|
||||
$CSSfile = $this->getFileUrl($CSS);
|
||||
|
||||
if($CSSfile)
|
||||
{
|
||||
$this->CSS[] = '<link rel="stylesheet" href="' . $CSSfile . '" />';
|
||||
}
|
||||
}
|
||||
|
||||
public function addInlineCSS($CSS)
|
||||
{
|
||||
$this->inlineCSS[] = '<style>' . $CSS . '</style>';
|
||||
}
|
||||
|
||||
public function addJS($JS)
|
||||
{
|
||||
$JSfile = $this->getFileUrl($JS);
|
||||
|
||||
if($JSfile)
|
||||
{
|
||||
$this->JS[] = '<script src="' . $JSfile . '"></script>';
|
||||
}
|
||||
}
|
||||
|
||||
public function addInlineJS($JS)
|
||||
{
|
||||
$this->inlineJS[] = '<script>' . $JS . '</script>';
|
||||
}
|
||||
|
||||
public function renderCSS()
|
||||
{
|
||||
return implode('', $this->CSS) . implode('', $this->inlineCSS);
|
||||
}
|
||||
|
||||
public function renderJS()
|
||||
{
|
||||
return implode('', $this->JS) . implode('', $this->inlineJS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if a string is a valid internal or external ressource like js-file or css-file
|
||||
* @params $path string
|
||||
* @return string or false
|
||||
*/
|
||||
public function getFileUrl($path)
|
||||
{
|
||||
$internalFile = __DIR__ . '/../plugins' . $path;
|
||||
|
||||
if(file_exists($internalFile))
|
||||
{
|
||||
return $this->baseUrl . '/plugins' . $path;
|
||||
}
|
||||
|
||||
return $path;
|
||||
|
||||
if(fopen($path, "r"))
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Assets
|
||||
{
|
||||
protected $baseUrl;
|
||||
|
||||
public function __construct($baseUrl)
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->JS = array();
|
||||
$this->CSS = array();
|
||||
$this->inlineJS = array();
|
||||
$this->inlineCSS = array();
|
||||
}
|
||||
|
||||
public function addCSS($CSS)
|
||||
{
|
||||
$CSSfile = $this->getFileUrl($CSS);
|
||||
|
||||
if($CSSfile)
|
||||
{
|
||||
$this->CSS[] = '<link rel="stylesheet" href="' . $CSSfile . '" />';
|
||||
}
|
||||
}
|
||||
|
||||
public function addInlineCSS($CSS)
|
||||
{
|
||||
$this->inlineCSS[] = '<style>' . $CSS . '</style>';
|
||||
}
|
||||
|
||||
public function addJS($JS)
|
||||
{
|
||||
$JSfile = $this->getFileUrl($JS);
|
||||
|
||||
if($JSfile)
|
||||
{
|
||||
$this->JS[] = '<script src="' . $JSfile . '"></script>';
|
||||
}
|
||||
}
|
||||
|
||||
public function addInlineJS($JS)
|
||||
{
|
||||
$this->inlineJS[] = '<script>' . $JS . '</script>';
|
||||
}
|
||||
|
||||
public function activateVue()
|
||||
{
|
||||
$vueUrl = '<script src="' . $this->baseUrl . '/system/author/js/vue.min.js"></script>';
|
||||
if(!in_array($vueUrl, $this->JS))
|
||||
{
|
||||
$this->JS[] = $vueUrl;
|
||||
}
|
||||
}
|
||||
|
||||
public function activateAxios()
|
||||
{
|
||||
$axiosUrl = '<script src="' . $this->baseUrl . '/system/author/js/axios.min.js"></script>';
|
||||
if(!in_array($axiosUrl, $this->JS))
|
||||
{
|
||||
$this->JS[] = $axiosUrl;
|
||||
|
||||
$axios = '<script>const myaxios = axios.create({ baseURL: \'' . $this->baseUrl . '\' });</script>';
|
||||
$this->JS[] = $axios;
|
||||
}
|
||||
}
|
||||
|
||||
public function activateTachyons()
|
||||
{
|
||||
$tachyonsUrl = '<link rel="stylesheet" href="' . $this->baseUrl . '/system/author/css/tachyons.min.css" />';
|
||||
if(!in_array($tachyonsUrl, $this->CSS))
|
||||
{
|
||||
$this->CSS[] = $tachyonsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
public function renderCSS()
|
||||
{
|
||||
return implode('', $this->CSS) . implode('', $this->inlineCSS);
|
||||
}
|
||||
|
||||
public function renderJS()
|
||||
{
|
||||
return implode('', $this->JS) . implode('', $this->inlineJS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if a string is a valid internal or external ressource like js-file or css-file
|
||||
* @params $path string
|
||||
* @return string or false
|
||||
*/
|
||||
public function getFileUrl($path)
|
||||
{
|
||||
$internalFile = __DIR__ . '/../plugins' . $path;
|
||||
|
||||
if(file_exists($internalFile))
|
||||
{
|
||||
return $this->baseUrl . '/plugins' . $path;
|
||||
}
|
||||
|
||||
return $path;
|
||||
|
||||
if(fopen($path, "r"))
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,179 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
public function redirect(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show login form
|
||||
*
|
||||
* @param obj $request the slim request object.
|
||||
* @param obj $response the slim response object.
|
||||
* @param array $args with arguments past to the slim router
|
||||
* @return obj $response and string route.
|
||||
*/
|
||||
|
||||
public function show(Request $request, Response $response, $args)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
/* check previous login attemps */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it. */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 3)
|
||||
{
|
||||
$timeleft = 60 - (time() - $lastLogin);
|
||||
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, '/auth/login.twig', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* signin an existing user
|
||||
*
|
||||
* @param obj $request the slim request object with form data in the post params.
|
||||
* @param obj $response the slim response object.
|
||||
* @return obj $response with redirect to route.
|
||||
*/
|
||||
|
||||
public function login(Request $request, Response $response)
|
||||
{
|
||||
/* log user attemps to authenticate */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 2)
|
||||
{
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/* authentication */
|
||||
$params = $request->getParams();
|
||||
$validation = new Validation();
|
||||
|
||||
if($validation->signin($params))
|
||||
{
|
||||
$user = new User();
|
||||
$userdata = $user->getUser($params['username']);
|
||||
|
||||
if($userdata && password_verify($params['password'], $userdata['password']))
|
||||
{
|
||||
$user->login($userdata['username']);
|
||||
|
||||
/* clear the user login attemps */
|
||||
if($userLogins)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
}
|
||||
|
||||
/* if authentication failed, add attempt to log file */
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
/**
|
||||
* log out a user
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function logout(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION))
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
private function getUserIP()
|
||||
{
|
||||
$client = @$_SERVER['HTTP_CLIENT_IP'];
|
||||
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if(filter_var($client, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $client;
|
||||
}
|
||||
elseif(filter_var($forward, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ip = $remote;
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
public function redirect(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show login form
|
||||
*
|
||||
* @param obj $request the slim request object.
|
||||
* @param obj $response the slim response object.
|
||||
* @param array $args with arguments past to the slim router
|
||||
* @return obj $response and string route.
|
||||
*/
|
||||
|
||||
public function show(Request $request, Response $response, $args)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
/* check previous login attemps */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it. */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 3)
|
||||
{
|
||||
$timeleft = 60 - (time() - $lastLogin);
|
||||
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, '/auth/login.twig', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* signin an existing user
|
||||
*
|
||||
* @param obj $request the slim request object with form data in the post params.
|
||||
* @param obj $response the slim response object.
|
||||
* @return obj $response with redirect to route.
|
||||
*/
|
||||
|
||||
public function login(Request $request, Response $response)
|
||||
{
|
||||
/* log user attemps to authenticate */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 2)
|
||||
{
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/* authentication */
|
||||
$params = $request->getParams();
|
||||
$validation = new Validation();
|
||||
|
||||
if($validation->signin($params))
|
||||
{
|
||||
$user = new User();
|
||||
$userdata = $user->getUser($params['username']);
|
||||
|
||||
if($userdata && password_verify($params['password'], $userdata['password']))
|
||||
{
|
||||
$user->login($userdata['username']);
|
||||
|
||||
/* clear the user login attemps */
|
||||
if($userLogins)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
}
|
||||
|
||||
/* if authentication failed, add attempt to log file */
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
/**
|
||||
* log out a user
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function logout(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION))
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
private function getUserIP()
|
||||
{
|
||||
$client = @$_SERVER['HTTP_CLIENT_IP'];
|
||||
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if(filter_var($client, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $client;
|
||||
}
|
||||
elseif(filter_var($forward, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ip = $remote;
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,160 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class ContentBackendController extends ContentController
|
||||
{
|
||||
/**
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showContent(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# get the breadcrumb (here we need it only to mark the actual item active in navigation)
|
||||
$breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false;
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
|
||||
# if content is an array, then it is a draft
|
||||
if(is_array($content))
|
||||
{
|
||||
# transform array to markdown
|
||||
$parsedown = new ParsedownExtension();
|
||||
$content = $parsedown->arrayBlocksToMarkdown($content);
|
||||
}
|
||||
|
||||
# if there is content
|
||||
if($content != '')
|
||||
{
|
||||
# normalize linebreaks
|
||||
$content = str_replace(array("\r\n", "\r"), "\n", $content);
|
||||
$content = trim($content, "\n");
|
||||
|
||||
# and strip out title
|
||||
if($content[0] == '#')
|
||||
{
|
||||
$contentParts = explode("\n", $content, 2);
|
||||
$title = trim($contentParts[0], "# \t\n\r\0\x0B");
|
||||
$content = trim($contentParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Content for blox editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($content))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$content = $parsedown->markdownToArrayBlocks($content);
|
||||
}
|
||||
|
||||
# needed for ToC links
|
||||
$relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
|
||||
|
||||
foreach($content as $key => $block)
|
||||
{
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($block);
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$content[$key] = $parsedown->markup($contentArray, $relurl);
|
||||
}
|
||||
|
||||
# extract title and delete from content array, array will start at 1 after that.
|
||||
$title = '# add title';
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$title = $content[0];
|
||||
unset($content[0]);
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
public function showEmpty(Request $request, Response $response, $args)
|
||||
{
|
||||
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class ContentBackendController extends ContentController
|
||||
{
|
||||
/**
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showContent(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# get the breadcrumb (here we need it only to mark the actual item active in navigation)
|
||||
$breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false;
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
|
||||
# if content is an array, then it is a draft
|
||||
if(is_array($content))
|
||||
{
|
||||
# transform array to markdown
|
||||
$parsedown = new ParsedownExtension();
|
||||
$content = $parsedown->arrayBlocksToMarkdown($content);
|
||||
}
|
||||
|
||||
# if there is content
|
||||
if($content != '')
|
||||
{
|
||||
# normalize linebreaks
|
||||
$content = str_replace(array("\r\n", "\r"), "\n", $content);
|
||||
$content = trim($content, "\n");
|
||||
|
||||
# and strip out title
|
||||
if($content[0] == '#')
|
||||
{
|
||||
$contentParts = explode("\n", $content, 2);
|
||||
$title = trim($contentParts[0], "# \t\n\r\0\x0B");
|
||||
$content = trim($contentParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Content for blox editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# to fix footnote-logic in parsedown, set visual mode to true
|
||||
$parsedown->setVisualMode();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($content))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$content = $parsedown->markdownToArrayBlocks($content);
|
||||
}
|
||||
|
||||
# needed for ToC links
|
||||
$relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
|
||||
|
||||
foreach($content as $key => $block)
|
||||
{
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($block);
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$content[$key] = $parsedown->markup($contentArray, $relurl);
|
||||
}
|
||||
|
||||
# extract title and delete from content array, array will start at 1 after that.
|
||||
$title = '# add title';
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$title = $content[0];
|
||||
unset($content[0]);
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
public function showEmpty(Request $request, Response $response, $args)
|
||||
{
|
||||
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
|
||||
}
|
||||
}
|
@ -1,370 +1,370 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\Write;
|
||||
use Typemill\Models\WriteCache;
|
||||
|
||||
abstract class ContentController
|
||||
{
|
||||
# holds the pimple container
|
||||
protected $c;
|
||||
|
||||
# holds the params from request
|
||||
protected $params;
|
||||
|
||||
# holds the slim-uri-object
|
||||
protected $uri;
|
||||
|
||||
# holds the errors to output in frontend
|
||||
protected $errors = false;
|
||||
|
||||
# holds a write object to write files
|
||||
protected $write;
|
||||
|
||||
# holds the structure of content folder as a serialized array of objects
|
||||
protected $structure;
|
||||
|
||||
# holds the name of the structure-file with drafts for author environment
|
||||
protected $structureDraftName;
|
||||
|
||||
# holds the name of the structure-file without drafts for live site
|
||||
protected $structureLiveName;
|
||||
|
||||
# holds informations about the homepage
|
||||
protected $homepage;
|
||||
|
||||
# hold the page-item as an object
|
||||
protected $item;
|
||||
|
||||
# hold the breadcrumb as an object
|
||||
protected $breadcrumb;
|
||||
|
||||
# holds the path to the requested file
|
||||
protected $path = false;
|
||||
|
||||
# holds the content of the page
|
||||
protected $content;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
$this->settings = $this->c->get('settings');
|
||||
$this->structureLiveName = 'structure.txt';
|
||||
$this->structureDraftName = 'structure-draft.txt';
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
|
||||
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
|
||||
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
|
||||
return $this->c->view->render($response, $route, $data);
|
||||
}
|
||||
|
||||
protected function render404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
|
||||
}
|
||||
|
||||
protected function renderIntern404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data);
|
||||
}
|
||||
|
||||
protected function validateEditorInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->editorInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateBlockInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->blockInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNavigationSort()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationSort($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNaviItem()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationItem($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setStructure($draft = false, $cache = true)
|
||||
{
|
||||
# set initial structure to false
|
||||
$structure = false;
|
||||
|
||||
# name of structure-file for draft or live
|
||||
$filename = $draft ? $this->structureDraftName : $this->structureLiveName;
|
||||
|
||||
# set variables and objects
|
||||
$this->write = new writeCache();
|
||||
|
||||
# check, if cached structure is still valid
|
||||
if($cache && $this->write->validate('cache', 'lastCache.txt', 600))
|
||||
{
|
||||
# get the cached structure
|
||||
$structure = $this->write->getCache('cache', $filename);
|
||||
}
|
||||
|
||||
# if no structure was found or cache is deactivated
|
||||
if(!$structure)
|
||||
{
|
||||
# scan the content of the folder
|
||||
$structure = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft);
|
||||
|
||||
# if there is content, then get the content details
|
||||
if(count($structure) > 0)
|
||||
{
|
||||
# create an array of object with the whole content of the folder
|
||||
$structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath());
|
||||
}
|
||||
|
||||
# cache navigation
|
||||
$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
|
||||
}
|
||||
|
||||
$this->structure = $structure;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setHomepage()
|
||||
{
|
||||
$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
|
||||
|
||||
if(in_array('index.md', $contentFolder))
|
||||
{
|
||||
$md = true;
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $contentFolder))
|
||||
{
|
||||
$txt = true;
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(isset($txt) && isset($md))
|
||||
{
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$active = false;
|
||||
if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() )
|
||||
{
|
||||
$active = 'active';
|
||||
}
|
||||
|
||||
$this->homepage = ['status' => $status, 'active' => $active];
|
||||
}
|
||||
|
||||
protected function setItem()
|
||||
{
|
||||
# if it is the homepage
|
||||
if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/')
|
||||
{
|
||||
$item = new \stdClass;
|
||||
$item->elementType = 'folder';
|
||||
$item->path = '';
|
||||
$item->urlRel = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
# search for the url in the structure
|
||||
$item = Folder::getItemForUrl($this->structure, $this->params['url']);
|
||||
}
|
||||
|
||||
if($item)
|
||||
{
|
||||
if($item->elementType == 'file')
|
||||
{
|
||||
$pathParts = explode('.', $item->path);
|
||||
$fileType = array_pop($pathParts);
|
||||
$pathWithoutType = implode('.', $pathParts);
|
||||
$item->pathWithoutType = $pathWithoutType;
|
||||
}
|
||||
elseif($item->elementType == 'folder')
|
||||
{
|
||||
$item->pathWithoutItem = $item->path;
|
||||
$item->path = $item->path . DIRECTORY_SEPARATOR . 'index';
|
||||
$item->pathWithoutType = $item->path;
|
||||
}
|
||||
$this->item = $item;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors = ['errors' => ['message' => 'requested page-url not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
# determine if you want to write to published file (md) or to draft (txt)
|
||||
protected function setItemPath($fileType)
|
||||
{
|
||||
$this->path = $this->item->pathWithoutType . '.' . $fileType;
|
||||
}
|
||||
|
||||
protected function setPublishStatus()
|
||||
{
|
||||
$this->item->published = false;
|
||||
$this->item->drafted = false;
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md'))
|
||||
{
|
||||
$this->item->published = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "md";
|
||||
}
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt'))
|
||||
{
|
||||
$this->item->drafted = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
|
||||
if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder")
|
||||
{
|
||||
# set txt as default for a folder, so that we can create an index.txt for a folder.
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteContentFiles($fileTypes, $folder = false)
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
|
||||
foreach($fileTypes as $fileType)
|
||||
{
|
||||
if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) )
|
||||
{
|
||||
$this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.'];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->errors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function deleteContentFolder()
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
$path = $basePath . $this->item->pathWithoutItem;
|
||||
|
||||
if(file_exists($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('.', '..'));
|
||||
|
||||
# check if there are folders first, then stop the operation
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
$this->errors = ['message' => 'Please delete the sub-folder first.'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->errors)
|
||||
{
|
||||
foreach ($files as $file)
|
||||
{
|
||||
unlink(realpath($path) . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return rmdir($path);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function setContent()
|
||||
{
|
||||
# if the file exists
|
||||
if($this->item->published OR $this->item->drafted)
|
||||
{
|
||||
$content = $this->write->getFile($this->settings['contentFolder'], $this->path);
|
||||
if($this->item->fileType == 'txt')
|
||||
{
|
||||
# decode the json-draft to an array
|
||||
$content = json_decode($content);
|
||||
}
|
||||
}
|
||||
elseif($this->item->elementType == "folder")
|
||||
{
|
||||
$content = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors = ['errors' => ['message' => 'requested file not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->content = $content;
|
||||
return true;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\Write;
|
||||
use Typemill\Models\WriteCache;
|
||||
|
||||
abstract class ContentController
|
||||
{
|
||||
# holds the pimple container
|
||||
protected $c;
|
||||
|
||||
# holds the params from request
|
||||
protected $params;
|
||||
|
||||
# holds the slim-uri-object
|
||||
protected $uri;
|
||||
|
||||
# holds the errors to output in frontend
|
||||
protected $errors = false;
|
||||
|
||||
# holds a write object to write files
|
||||
protected $write;
|
||||
|
||||
# holds the structure of content folder as a serialized array of objects
|
||||
protected $structure;
|
||||
|
||||
# holds the name of the structure-file with drafts for author environment
|
||||
protected $structureDraftName;
|
||||
|
||||
# holds the name of the structure-file without drafts for live site
|
||||
protected $structureLiveName;
|
||||
|
||||
# holds informations about the homepage
|
||||
protected $homepage;
|
||||
|
||||
# hold the page-item as an object
|
||||
protected $item;
|
||||
|
||||
# hold the breadcrumb as an object
|
||||
protected $breadcrumb;
|
||||
|
||||
# holds the path to the requested file
|
||||
protected $path = false;
|
||||
|
||||
# holds the content of the page
|
||||
protected $content;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
$this->settings = $this->c->get('settings');
|
||||
$this->structureLiveName = 'structure.txt';
|
||||
$this->structureDraftName = 'structure-draft.txt';
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
|
||||
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
|
||||
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
|
||||
return $this->c->view->render($response, $route, $data);
|
||||
}
|
||||
|
||||
protected function render404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
|
||||
}
|
||||
|
||||
protected function renderIntern404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data);
|
||||
}
|
||||
|
||||
protected function validateEditorInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->editorInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateBlockInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->blockInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNavigationSort()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationSort($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNaviItem()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationItem($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setStructure($draft = false, $cache = true)
|
||||
{
|
||||
# set initial structure to false
|
||||
$structure = false;
|
||||
|
||||
# name of structure-file for draft or live
|
||||
$filename = $draft ? $this->structureDraftName : $this->structureLiveName;
|
||||
|
||||
# set variables and objects
|
||||
$this->write = new writeCache();
|
||||
|
||||
# check, if cached structure is still valid
|
||||
if($cache && $this->write->validate('cache', 'lastCache.txt', 600))
|
||||
{
|
||||
# get the cached structure
|
||||
$structure = $this->write->getCache('cache', $filename);
|
||||
}
|
||||
|
||||
# if no structure was found or cache is deactivated
|
||||
if(!$structure)
|
||||
{
|
||||
# scan the content of the folder
|
||||
$structure = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft);
|
||||
|
||||
# if there is content, then get the content details
|
||||
if(count($structure) > 0)
|
||||
{
|
||||
# create an array of object with the whole content of the folder
|
||||
$structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath());
|
||||
}
|
||||
|
||||
# cache navigation
|
||||
$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
|
||||
}
|
||||
|
||||
$this->structure = $structure;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setHomepage()
|
||||
{
|
||||
$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
|
||||
|
||||
if(in_array('index.md', $contentFolder))
|
||||
{
|
||||
$md = true;
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $contentFolder))
|
||||
{
|
||||
$txt = true;
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(isset($txt) && isset($md))
|
||||
{
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$active = false;
|
||||
if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() )
|
||||
{
|
||||
$active = 'active';
|
||||
}
|
||||
|
||||
$this->homepage = ['status' => $status, 'active' => $active];
|
||||
}
|
||||
|
||||
protected function setItem()
|
||||
{
|
||||
# if it is the homepage
|
||||
if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/')
|
||||
{
|
||||
$item = new \stdClass;
|
||||
$item->elementType = 'folder';
|
||||
$item->path = '';
|
||||
$item->urlRel = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
# search for the url in the structure
|
||||
$item = Folder::getItemForUrl($this->structure, $this->params['url']);
|
||||
}
|
||||
|
||||
if($item)
|
||||
{
|
||||
if($item->elementType == 'file')
|
||||
{
|
||||
$pathParts = explode('.', $item->path);
|
||||
$fileType = array_pop($pathParts);
|
||||
$pathWithoutType = implode('.', $pathParts);
|
||||
$item->pathWithoutType = $pathWithoutType;
|
||||
}
|
||||
elseif($item->elementType == 'folder')
|
||||
{
|
||||
$item->pathWithoutItem = $item->path;
|
||||
$item->path = $item->path . DIRECTORY_SEPARATOR . 'index';
|
||||
$item->pathWithoutType = $item->path;
|
||||
}
|
||||
$this->item = $item;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors = ['errors' => ['message' => 'requested page-url not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
# determine if you want to write to published file (md) or to draft (txt)
|
||||
protected function setItemPath($fileType)
|
||||
{
|
||||
$this->path = $this->item->pathWithoutType . '.' . $fileType;
|
||||
}
|
||||
|
||||
protected function setPublishStatus()
|
||||
{
|
||||
$this->item->published = false;
|
||||
$this->item->drafted = false;
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md'))
|
||||
{
|
||||
$this->item->published = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "md";
|
||||
}
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt'))
|
||||
{
|
||||
$this->item->drafted = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
|
||||
if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder")
|
||||
{
|
||||
# set txt as default for a folder, so that we can create an index.txt for a folder.
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteContentFiles($fileTypes, $folder = false)
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
|
||||
foreach($fileTypes as $fileType)
|
||||
{
|
||||
if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) )
|
||||
{
|
||||
$this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.'];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->errors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function deleteContentFolder()
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
$path = $basePath . $this->item->pathWithoutItem;
|
||||
|
||||
if(file_exists($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('.', '..'));
|
||||
|
||||
# check if there are folders first, then stop the operation
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
$this->errors = ['message' => 'Please delete the sub-folder first.'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->errors)
|
||||
{
|
||||
foreach ($files as $file)
|
||||
{
|
||||
unlink(realpath($path) . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return rmdir($path);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function setContent()
|
||||
{
|
||||
# if the file exists
|
||||
if($this->item->published OR $this->item->drafted)
|
||||
{
|
||||
$content = $this->write->getFile($this->settings['contentFolder'], $this->path);
|
||||
if($this->item->fileType == 'txt')
|
||||
{
|
||||
# decode the json-draft to an array
|
||||
$content = json_decode($content);
|
||||
}
|
||||
}
|
||||
elseif($this->item->elementType == "folder")
|
||||
{
|
||||
$content = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors = ['errors' => ['message' => 'requested file not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->content = $content;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,50 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
/* Use the slim-container */
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Events\OnPageReady;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
protected $c;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
// $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
|
||||
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
|
||||
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
|
||||
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
|
||||
return $this->c->view->render($response, $route, $data);
|
||||
}
|
||||
|
||||
protected function render404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
/* Use the slim-container */
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Events\OnPageReady;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
protected $c;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
# why commented this out??
|
||||
$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
|
||||
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
|
||||
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
|
||||
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
|
||||
return $this->c->view->render($response, $route, $data);
|
||||
}
|
||||
|
||||
protected function render404($response, $data = NULL)
|
||||
{
|
||||
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
|
||||
}
|
||||
}
|
@ -1,135 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
/*************************************
|
||||
** SAVE THEME- AND PLUGIN-SETTINGS **
|
||||
*************************************/
|
||||
|
||||
public function savePublicForm($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
reset($params);
|
||||
$pluginName = key($params);
|
||||
$referer = $request->getHeader('HTTP_REFERER');
|
||||
|
||||
# simple bot check with honeypot
|
||||
if(isset($params[$pluginName]['personal-mail']))
|
||||
{
|
||||
if($params[$pluginName]['personal-mail'] != '')
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
unset($params[$pluginName]['personal-mail']);
|
||||
}
|
||||
|
||||
#recaptcha check
|
||||
if(isset($params['g-recaptcha-response']))
|
||||
{
|
||||
$recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
$settings = $this->c->get('settings');
|
||||
$secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false;
|
||||
$recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']];
|
||||
|
||||
# use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($recaptchaRequest),
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($recaptchaApi, false, $context);
|
||||
$result = json_decode($result);
|
||||
|
||||
if ($result === FALSE || $result->success === FALSE)
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($params[$pluginName]))
|
||||
{
|
||||
# validate the user-input
|
||||
$this->validateInput('plugins', $pluginName, $params[$pluginName]);
|
||||
}
|
||||
|
||||
# check for errors and redirect to path, if errors found
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->c->flash->addMessage('error', 'Please correct the errors');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
|
||||
# clean up and make sure that only validated data are stored
|
||||
$data = [ $pluginName => $params[$pluginName]];
|
||||
|
||||
# create write object
|
||||
$writeYaml = new WriteYaml();
|
||||
|
||||
# write the form data into yaml file
|
||||
$writeYaml->updateYaml('settings', 'formdata.yaml', $data);
|
||||
|
||||
# add message and return to original site
|
||||
$this->c->flash->addMessage('formdata', $pluginName);
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateInput($objectType, $objectName, $userInput)
|
||||
{
|
||||
# get settings and start validation
|
||||
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
|
||||
$userSettings = \Typemill\Settings::getUserSettings();
|
||||
$validate = new Validation();
|
||||
|
||||
if(isset($originalSettings['public']['fields']))
|
||||
{
|
||||
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
|
||||
$originalFields = array();
|
||||
foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue)
|
||||
{
|
||||
if(isset($fieldValue['fields']))
|
||||
{
|
||||
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
|
||||
{
|
||||
$originalFields[$subFieldName] = $subFieldValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$originalFields[$fieldName] = $fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* take the user input data and iterate over all fields and values */
|
||||
foreach($userInput as $fieldName => $fieldValue)
|
||||
{
|
||||
/* get the corresponding field definition from original plugin settings */
|
||||
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
|
||||
|
||||
if($fieldDefinition)
|
||||
{
|
||||
/* validate user input for this field */
|
||||
$validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
|
||||
}
|
||||
if(!$fieldDefinition && $fieldName != 'active')
|
||||
{
|
||||
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
/*************************************
|
||||
** SAVE THEME- AND PLUGIN-SETTINGS **
|
||||
*************************************/
|
||||
|
||||
public function savePublicForm($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
reset($params);
|
||||
$pluginName = key($params);
|
||||
$referer = $request->getHeader('HTTP_REFERER');
|
||||
|
||||
# simple bot check with honeypot
|
||||
if(isset($params[$pluginName]['personal-mail']))
|
||||
{
|
||||
if($params[$pluginName]['personal-mail'] != '')
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
unset($params[$pluginName]['personal-mail']);
|
||||
}
|
||||
|
||||
#recaptcha check
|
||||
if(isset($params['g-recaptcha-response']))
|
||||
{
|
||||
$recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
$settings = $this->c->get('settings');
|
||||
$secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false;
|
||||
$recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']];
|
||||
|
||||
# use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($recaptchaRequest),
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($recaptchaApi, false, $context);
|
||||
$result = json_decode($result);
|
||||
|
||||
if ($result === FALSE || $result->success === FALSE)
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($params[$pluginName]))
|
||||
{
|
||||
# validate the user-input
|
||||
$this->validateInput('plugins', $pluginName, $params[$pluginName]);
|
||||
}
|
||||
|
||||
# check for errors and redirect to path, if errors found
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->c->flash->addMessage('error', 'Please correct the errors');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
|
||||
# clean up and make sure that only validated data are stored
|
||||
$data = [ $pluginName => $params[$pluginName]];
|
||||
|
||||
# create write object
|
||||
$writeYaml = new WriteYaml();
|
||||
|
||||
# write the form data into yaml file
|
||||
$writeYaml->updateYaml('settings', 'formdata.yaml', $data);
|
||||
|
||||
# add message and return to original site
|
||||
$this->c->flash->addMessage('formdata', $pluginName);
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateInput($objectType, $objectName, $userInput)
|
||||
{
|
||||
# get settings and start validation
|
||||
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
|
||||
$userSettings = \Typemill\Settings::getUserSettings();
|
||||
$validate = new Validation();
|
||||
|
||||
if(isset($originalSettings['public']['fields']))
|
||||
{
|
||||
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
|
||||
$originalFields = array();
|
||||
foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue)
|
||||
{
|
||||
if(isset($fieldValue['fields']))
|
||||
{
|
||||
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
|
||||
{
|
||||
$originalFields[$subFieldName] = $subFieldValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$originalFields[$fieldName] = $fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* take the user input data and iterate over all fields and values */
|
||||
foreach($userInput as $fieldName => $fieldValue)
|
||||
{
|
||||
/* get the corresponding field definition from original plugin settings */
|
||||
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
|
||||
|
||||
if($fieldDefinition)
|
||||
{
|
||||
/* validate user input for this field */
|
||||
$validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
|
||||
}
|
||||
if(!$fieldDefinition && $fieldName != 'active')
|
||||
{
|
||||
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,236 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\WriteCache;
|
||||
use Typemill\Models\WriteSitemap;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\VersionCheck;
|
||||
use Typemill\Models\Helpers;
|
||||
use Typemill\Models\Markdown;
|
||||
use Typemill\Events\OnPagetreeLoaded;
|
||||
use Typemill\Events\OnBreadcrumbLoaded;
|
||||
use Typemill\Events\OnItemLoaded;
|
||||
use Typemill\Events\OnOriginalLoaded;
|
||||
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;
|
||||
$item = false;
|
||||
$breadcrumb = false;
|
||||
$description = '';
|
||||
$settings = $this->c->get('settings');
|
||||
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
|
||||
$cache = new WriteCache();
|
||||
$uri = $request->getUri();
|
||||
$base_url = $uri->getBaseUrl();
|
||||
|
||||
try
|
||||
{
|
||||
/* if the cached structure is still valid, use it */
|
||||
if($cache->validate('cache', 'lastCache.txt',600))
|
||||
{
|
||||
$structure = $this->getCachedStructure($cache);
|
||||
}
|
||||
if(!isset($structure) OR !$structure)
|
||||
{
|
||||
/* if not, get a fresh structure of the content folder */
|
||||
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
|
||||
|
||||
/* if there is no structure at all, the content folder is probably empty */
|
||||
if(!$structure)
|
||||
{
|
||||
$content = '<h1>No Content</h1><p>Your content folder is empty.</p>';
|
||||
|
||||
return $this->render($response, '/index.twig', array( 'content' => $content ));
|
||||
}
|
||||
elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400))
|
||||
{
|
||||
/* update sitemap */
|
||||
$sitemap = new WriteSitemap();
|
||||
$sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl());
|
||||
|
||||
/* check and update the typemill-version in the user settings */
|
||||
$this->updateVersion($uri->getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/* dispatch event and let others manipulate the structure */
|
||||
$structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
echo $e->getMessage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* if the user is on startpage */
|
||||
if(empty($args))
|
||||
{
|
||||
/* check, if there is an index-file in the root of the content folder */
|
||||
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the request url */
|
||||
$urlRel = $uri->getBasePath() . '/' . $args['params'];
|
||||
|
||||
/* find the url in the content-item-tree and return the item-object for the file */
|
||||
$item = Folder::getItemForUrl($structure, $urlRel);
|
||||
|
||||
/* if there is still no item, return a 404-page */
|
||||
if(!$item)
|
||||
{
|
||||
return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url ));
|
||||
}
|
||||
|
||||
/* get breadcrumb for page */
|
||||
$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
|
||||
$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 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')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md';
|
||||
}
|
||||
elseif($item->elementType == 'file')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path;
|
||||
}
|
||||
|
||||
/* add the modified date for the file */
|
||||
$item->modified = isset($filePath) ? filemtime($filePath) : false;
|
||||
|
||||
/* read the content of the file */
|
||||
$contentMD = isset($filePath) ? file_get_contents($filePath) : false;
|
||||
}
|
||||
|
||||
# dispatch the original content without plugin-manipulations for case anyone wants to use it
|
||||
$this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
|
||||
|
||||
$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* 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);
|
||||
|
||||
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$contentHTML = $parsedown->markup($contentArray, $itemUrl);
|
||||
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
|
||||
|
||||
/* extract the h1 headline*/
|
||||
$contentParts = explode("</h1>", $contentHTML);
|
||||
$title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title'];
|
||||
|
||||
$contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML;
|
||||
|
||||
/* create excerpt from content */
|
||||
$excerpt = substr($contentHTML,0,500);
|
||||
|
||||
/* create description from excerpt */
|
||||
$description = isset($excerpt) ? strip_tags($excerpt) : false;
|
||||
if($description)
|
||||
{
|
||||
$description = trim(preg_replace('/\s+/', ' ', $description));
|
||||
$description = substr($description, 0, 300);
|
||||
$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]);
|
||||
}
|
||||
}
|
||||
|
||||
$route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig';
|
||||
|
||||
return $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');
|
||||
}
|
||||
|
||||
protected function getFreshStructure($pathToContent, $cache, $uri)
|
||||
{
|
||||
/* scan the content of the folder */
|
||||
$structure = Folder::scanFolder($pathToContent);
|
||||
|
||||
/* if there is no content, render an empty page */
|
||||
if(count($structure) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* create an array of object with the whole content of the folder */
|
||||
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
|
||||
|
||||
/* cache navigation */
|
||||
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
|
||||
|
||||
return $structure;
|
||||
}
|
||||
|
||||
protected function updateVersion($baseUrl)
|
||||
{
|
||||
/* check the latest public typemill version */
|
||||
$version = new VersionCheck();
|
||||
$latestVersion = $version->checkVersion($baseUrl);
|
||||
|
||||
if($latestVersion)
|
||||
{
|
||||
/* store latest version */
|
||||
\Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFirstImage(array $contentBlocks)
|
||||
{
|
||||
foreach($contentBlocks as $block)
|
||||
{
|
||||
/* is it a paragraph? */
|
||||
if(isset($block['name']) && $block['name'] == 'p')
|
||||
{
|
||||
if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
|
||||
{
|
||||
return $block['handler']['argument'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\WriteCache;
|
||||
use Typemill\Models\WriteSitemap;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\VersionCheck;
|
||||
use Typemill\Models\Helpers;
|
||||
use Typemill\Models\Markdown;
|
||||
use Typemill\Events\OnPagetreeLoaded;
|
||||
use Typemill\Events\OnBreadcrumbLoaded;
|
||||
use Typemill\Events\OnItemLoaded;
|
||||
use Typemill\Events\OnOriginalLoaded;
|
||||
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;
|
||||
$item = false;
|
||||
$breadcrumb = false;
|
||||
$description = '';
|
||||
$settings = $this->c->get('settings');
|
||||
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
|
||||
$cache = new WriteCache();
|
||||
$uri = $request->getUri();
|
||||
$base_url = $uri->getBaseUrl();
|
||||
|
||||
try
|
||||
{
|
||||
/* if the cached structure is still valid, use it */
|
||||
if($cache->validate('cache', 'lastCache.txt',600))
|
||||
{
|
||||
$structure = $this->getCachedStructure($cache);
|
||||
}
|
||||
if(!isset($structure) OR !$structure)
|
||||
{
|
||||
/* if not, get a fresh structure of the content folder */
|
||||
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
|
||||
|
||||
/* if there is no structure at all, the content folder is probably empty */
|
||||
if(!$structure)
|
||||
{
|
||||
$content = '<h1>No Content</h1><p>Your content folder is empty.</p>';
|
||||
|
||||
return $this->render($response, '/index.twig', array( 'content' => $content ));
|
||||
}
|
||||
elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400))
|
||||
{
|
||||
/* update sitemap */
|
||||
$sitemap = new WriteSitemap();
|
||||
$sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl());
|
||||
|
||||
/* check and update the typemill-version in the user settings */
|
||||
$this->updateVersion($uri->getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/* dispatch event and let others manipulate the structure */
|
||||
$structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
echo $e->getMessage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* if the user is on startpage */
|
||||
if(empty($args))
|
||||
{
|
||||
/* check, if there is an index-file in the root of the content folder */
|
||||
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the request url */
|
||||
$urlRel = $uri->getBasePath() . '/' . $args['params'];
|
||||
|
||||
/* find the url in the content-item-tree and return the item-object for the file */
|
||||
$item = Folder::getItemForUrl($structure, $urlRel);
|
||||
|
||||
/* if there is still no item, return a 404-page */
|
||||
if(!$item)
|
||||
{
|
||||
return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url ));
|
||||
}
|
||||
|
||||
/* get breadcrumb for page */
|
||||
$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
|
||||
$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 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')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md';
|
||||
}
|
||||
elseif($item->elementType == 'file')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path;
|
||||
}
|
||||
|
||||
/* add the modified date for the file */
|
||||
$item->modified = isset($filePath) ? filemtime($filePath) : false;
|
||||
|
||||
/* read the content of the file */
|
||||
$contentMD = isset($filePath) ? file_get_contents($filePath) : false;
|
||||
}
|
||||
|
||||
# dispatch the original content without plugin-manipulations for case anyone wants to use it
|
||||
$this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
|
||||
|
||||
$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* 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);
|
||||
|
||||
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$contentHTML = $parsedown->markup($contentArray, $itemUrl);
|
||||
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
|
||||
|
||||
/* extract the h1 headline*/
|
||||
$contentParts = explode("</h1>", $contentHTML);
|
||||
$title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title'];
|
||||
|
||||
$contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML;
|
||||
|
||||
/* create excerpt from content */
|
||||
$excerpt = substr($contentHTML,0,500);
|
||||
|
||||
/* create description from excerpt */
|
||||
$description = isset($excerpt) ? strip_tags($excerpt) : false;
|
||||
if($description)
|
||||
{
|
||||
$description = trim(preg_replace('/\s+/', ' ', $description));
|
||||
$description = substr($description, 0, 300);
|
||||
$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]);
|
||||
}
|
||||
}
|
||||
|
||||
$route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig';
|
||||
|
||||
return $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');
|
||||
}
|
||||
|
||||
protected function getFreshStructure($pathToContent, $cache, $uri)
|
||||
{
|
||||
/* scan the content of the folder */
|
||||
$structure = Folder::scanFolder($pathToContent);
|
||||
|
||||
/* if there is no content, render an empty page */
|
||||
if(count($structure) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* create an array of object with the whole content of the folder */
|
||||
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
|
||||
|
||||
/* cache navigation */
|
||||
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
|
||||
|
||||
return $structure;
|
||||
}
|
||||
|
||||
protected function updateVersion($baseUrl)
|
||||
{
|
||||
/* check the latest public typemill version */
|
||||
$version = new VersionCheck();
|
||||
$latestVersion = $version->checkVersion($baseUrl);
|
||||
|
||||
if($latestVersion)
|
||||
{
|
||||
/* store latest version */
|
||||
\Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFirstImage(array $contentBlocks)
|
||||
{
|
||||
foreach($contentBlocks as $block)
|
||||
{
|
||||
/* is it a paragraph? */
|
||||
if(isset($block['name']) && $block['name'] == 'p')
|
||||
{
|
||||
if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
|
||||
{
|
||||
return $block['handler']['argument'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,76 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\Write;
|
||||
|
||||
class SetupController extends Controller
|
||||
{
|
||||
public function show($request, $response, $args)
|
||||
{
|
||||
/* make some checks befor you install */
|
||||
$checkFolder = new Write();
|
||||
|
||||
$systemcheck = array();
|
||||
|
||||
try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
|
||||
$systemcheck = empty($systemcheck) ? false : $systemcheck;
|
||||
|
||||
return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
|
||||
}
|
||||
|
||||
public function create($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
$validate = new Validation();
|
||||
$user = new User();
|
||||
|
||||
/* set user as admin */
|
||||
$params['userrole'] = 'administrator';
|
||||
|
||||
/* get userroles for validation */
|
||||
$userroles = $user->getUserroles();
|
||||
|
||||
/* validate user */
|
||||
if($validate->newUser($params, $userroles))
|
||||
{
|
||||
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
|
||||
|
||||
/* create initial user */
|
||||
$username = $user->createUser($userdata);
|
||||
|
||||
if($username)
|
||||
{
|
||||
/* login user */
|
||||
$user->login($username);
|
||||
|
||||
# create initial settings file
|
||||
\Typemill\Settings::createSettings();
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.welcome'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->c->flash->addMessage('error', 'Please check your input and try again');
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.show'));
|
||||
}
|
||||
}
|
||||
|
||||
public function welcome($request, $response, $args)
|
||||
{
|
||||
/* store updated settings */
|
||||
\Typemill\Settings::updateSettings(array('welcome' => false));
|
||||
|
||||
return $this->render($response, 'auth/welcome.twig', array());
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\Write;
|
||||
|
||||
class SetupController extends Controller
|
||||
{
|
||||
public function show($request, $response, $args)
|
||||
{
|
||||
/* make some checks befor you install */
|
||||
$checkFolder = new Write();
|
||||
|
||||
$systemcheck = array();
|
||||
|
||||
try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
|
||||
$systemcheck = empty($systemcheck) ? false : $systemcheck;
|
||||
|
||||
return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
|
||||
}
|
||||
|
||||
public function create($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
$validate = new Validation();
|
||||
$user = new User();
|
||||
|
||||
/* set user as admin */
|
||||
$params['userrole'] = 'administrator';
|
||||
|
||||
/* get userroles for validation */
|
||||
$userroles = $user->getUserroles();
|
||||
|
||||
/* validate user */
|
||||
if($validate->newUser($params, $userroles))
|
||||
{
|
||||
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
|
||||
|
||||
/* create initial user */
|
||||
$username = $user->createUser($userdata);
|
||||
|
||||
if($username)
|
||||
{
|
||||
/* login user */
|
||||
$user->login($username);
|
||||
|
||||
# create initial settings file
|
||||
\Typemill\Settings::createSettings();
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.welcome'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->c->flash->addMessage('error', 'Please check your input and try again');
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.show'));
|
||||
}
|
||||
}
|
||||
|
||||
public function welcome($request, $response, $args)
|
||||
{
|
||||
/* store updated settings */
|
||||
\Typemill\Settings::updateSettings(array('welcome' => false));
|
||||
|
||||
return $this->render($response, 'auth/welcome.twig', array());
|
||||
}
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class BaseEvent extends Event
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class BaseEvent extends Event
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnBreadcrumbLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnBreadcrumbLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the page rendering data.
|
||||
*/
|
||||
|
||||
class OnContentArrayLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the page rendering data.
|
||||
*/
|
||||
|
||||
class OnContentArrayLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for html page.
|
||||
*/
|
||||
|
||||
class OnHtmlLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for html page.
|
||||
*/
|
||||
|
||||
class OnHtmlLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for item.
|
||||
*/
|
||||
|
||||
class OnItemLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for item.
|
||||
*/
|
||||
|
||||
class OnItemLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for markdown.
|
||||
*/
|
||||
|
||||
class OnMarkdownLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for markdown.
|
||||
*/
|
||||
|
||||
class OnMarkdownLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
/**
|
||||
* Event for html page.
|
||||
*/
|
||||
|
||||
class OnOriginalLoaded extends Event
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getMarkdown()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getHTML($urlrel)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
$contentArray = $parsedown->text($this->data);
|
||||
$contentHTML = $parsedown->markup($contentArray, $urlrel);
|
||||
|
||||
return $contentHTML;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
/**
|
||||
* Event for html page.
|
||||
*/
|
||||
|
||||
class OnOriginalLoaded extends Event
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getMarkdown()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getHTML($urlrel)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
$contentArray = $parsedown->text($this->data);
|
||||
$contentHTML = $parsedown->markup($contentArray, $urlrel);
|
||||
|
||||
return $contentHTML;
|
||||
}
|
||||
}
|
14
system/Events/OnPageDeleted.php
Normal file
14
system/Events/OnPageDeleted.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnPageDeleted extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
14
system/Events/OnPagePublished.php
Normal file
14
system/Events/OnPagePublished.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnPagePublished extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for html data page.
|
||||
*/
|
||||
|
||||
class OnPageReady extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for html data page.
|
||||
*/
|
||||
|
||||
class OnPageReady extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
14
system/Events/OnPageSorted.php
Normal file
14
system/Events/OnPageSorted.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnPageSorted extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
14
system/Events/OnPageUnpublished.php
Normal file
14
system/Events/OnPageUnpublished.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnPageUnpublished extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the page tree.
|
||||
*/
|
||||
|
||||
class OnPagetreeLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the page tree.
|
||||
*/
|
||||
|
||||
class OnPagetreeLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the folder structure.
|
||||
*/
|
||||
|
||||
class OnPluginsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the folder structure.
|
||||
*/
|
||||
|
||||
class OnPluginsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnSessionSegmentsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnSessionSegmentsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for settings
|
||||
*/
|
||||
|
||||
class OnSettingsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for settings
|
||||
*/
|
||||
|
||||
class OnSettingsLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
use Slim\Csrf\Guard;
|
||||
|
||||
class TwigCsrfExtension extends \Twig_Extension
|
||||
{
|
||||
|
||||
protected $guard;
|
||||
|
||||
public function __construct(Guard $guard)
|
||||
{
|
||||
$this->guard = $guard;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('csrf_field', array($this, 'csrfField' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function csrfField()
|
||||
{
|
||||
return '
|
||||
<input type="hidden" id="' . $this->guard->getTokenNameKey() . '" name="' . $this->guard->getTokenNameKey() . '" value="' . $this->guard->getTokenName() . '">
|
||||
<input type="hidden" id="' . $this->guard->getTokenValueKey() . '" name="' . $this->guard->getTokenValueKey() . '" value="' . $this->guard->getTokenValue() . '">
|
||||
';
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
use Slim\Csrf\Guard;
|
||||
|
||||
class TwigCsrfExtension extends \Twig_Extension
|
||||
{
|
||||
|
||||
protected $guard;
|
||||
|
||||
public function __construct(Guard $guard)
|
||||
{
|
||||
$this->guard = $guard;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('csrf_field', array($this, 'csrfField' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function csrfField()
|
||||
{
|
||||
return '
|
||||
<input type="hidden" id="' . $this->guard->getTokenNameKey() . '" name="' . $this->guard->getTokenNameKey() . '" value="' . $this->guard->getTokenName() . '">
|
||||
<input type="hidden" id="' . $this->guard->getTokenValueKey() . '" name="' . $this->guard->getTokenValueKey() . '" value="' . $this->guard->getTokenValue() . '">
|
||||
';
|
||||
}
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class TwigMarkdownExtension extends \Twig_Extension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('markdown', array($this, 'renderMarkdown' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function renderMarkdown($markdown)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
$markdownArray = $parsedown->text($markdown);
|
||||
|
||||
return $parsedown->markup($markdownArray, false);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class TwigMarkdownExtension extends \Twig_Extension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('markdown', array($this, 'renderMarkdown' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function renderMarkdown($markdown)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
$markdownArray = $parsedown->text($markdown);
|
||||
|
||||
return $parsedown->markup($markdownArray, false);
|
||||
}
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
class TwigUserExtension extends \Twig_Extension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('is_role', array($this, 'isRole' )),
|
||||
new \Twig_SimpleFunction('get_username', array($this, 'getUsername' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function isRole($role)
|
||||
{
|
||||
if(isset($_SESSION['role']) && $_SESSION['role'] == $role)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
if(isset($_SESSION['user']))
|
||||
{
|
||||
return $_SESSION['user'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
class TwigUserExtension extends \Twig_Extension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFunction('is_role', array($this, 'isRole' )),
|
||||
new \Twig_SimpleFunction('get_username', array($this, 'getUsername' ))
|
||||
];
|
||||
}
|
||||
|
||||
public function isRole($role)
|
||||
{
|
||||
if(isset($_SESSION['role']) && $_SESSION['role'] == $role)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
if(isset($_SESSION['user']))
|
||||
{
|
||||
return $_SESSION['user'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2016 Taufik Nurrohman, latitudu.com
|
||||
|
||||
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
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2016 Taufik Nurrohman, latitudu.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,21 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Benjamin Høegh
|
||||
|
||||
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
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Benjamin Høegh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,32 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class OldInputMiddleware
|
||||
{
|
||||
protected $view;
|
||||
|
||||
public function __construct(Twig $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
$this->view->getEnvironment()->addGlobal('old', $_SESSION['old']);
|
||||
}
|
||||
if(!empty($request->getParams()))
|
||||
{
|
||||
$_SESSION['old'] = $request->getParams();
|
||||
}
|
||||
|
||||
$response = $next($request, $response);
|
||||
return $response;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class OldInputMiddleware
|
||||
{
|
||||
protected $view;
|
||||
|
||||
public function __construct(Twig $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
$this->view->getEnvironment()->addGlobal('old', $_SESSION['old']);
|
||||
}
|
||||
if(!empty($request->getParams()))
|
||||
{
|
||||
$_SESSION['old'] = $request->getParams();
|
||||
}
|
||||
|
||||
$response = $next($request, $response);
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@ -1,32 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfNoAdmin
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router, $flash)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']) || !isset($_SESSION['role']))
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
if($_SESSION['role'] != 'administrator')
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfNoAdmin
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router, $flash)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']) || !isset($_SESSION['role']))
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
if($_SESSION['role'] != 'administrator')
|
||||
{
|
||||
$response = $response->withRedirect($this->router->pathFor('content.raw'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfUnauthenticated
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router, $flash)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RedirectIfUnauthenticated
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router, $flash)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RestrictApiAccess
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']) || !isset($_SESSION['role']))
|
||||
{
|
||||
return $response->withJson(['errors' => ['access denied']], 403);
|
||||
}
|
||||
return $next($request, $response);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Interfaces\RouterInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class RestrictApiAccess
|
||||
{
|
||||
protected $router;
|
||||
|
||||
public function __construct(RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(!isset($_SESSION['login']) || !isset($_SESSION['role']))
|
||||
{
|
||||
return $response->withJson(['errors' => ['access denied']], 403);
|
||||
}
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class ValidationErrorsMiddleware
|
||||
{
|
||||
protected $view;
|
||||
|
||||
public function __construct(Twig $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->view->getEnvironment()->addGlobal('errors', $_SESSION['errors']);
|
||||
|
||||
unset($_SESSION['errors']);
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Middleware;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class ValidationErrorsMiddleware
|
||||
{
|
||||
protected $view;
|
||||
|
||||
public function __construct(Twig $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->view->getEnvironment()->addGlobal('errors', $_SESSION['errors']);
|
||||
|
||||
unset($_SESSION['errors']);
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@ -1,286 +1,286 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Field
|
||||
{
|
||||
private $type;
|
||||
|
||||
private $label;
|
||||
|
||||
private $name;
|
||||
|
||||
private $content;
|
||||
|
||||
/* holds all simple attributes for this field like "required" */
|
||||
private $attributes = array();
|
||||
|
||||
/* holds all attribute value pairs for this field like "id=''" */
|
||||
private $attributeValues = array();
|
||||
|
||||
/* holds all options for this field (e.g. select options) */
|
||||
private $options = array();
|
||||
|
||||
/* defines all field types, that are allowed */
|
||||
private $types = array(
|
||||
'checkbox',
|
||||
'checkboxlist',
|
||||
'color',
|
||||
'date',
|
||||
'datetime',
|
||||
'datetime-local',
|
||||
'email',
|
||||
'file',
|
||||
'hidden',
|
||||
'image',
|
||||
'month',
|
||||
'number',
|
||||
'password',
|
||||
'radio',
|
||||
'range',
|
||||
'tel',
|
||||
'text',
|
||||
'time',
|
||||
'url',
|
||||
'week',
|
||||
'textarea',
|
||||
'select',
|
||||
'paragraph'
|
||||
);
|
||||
|
||||
/* defines all boolean attributes, that are allowed for fields */
|
||||
private $attr = array(
|
||||
'autofocus',
|
||||
'checked',
|
||||
'disabled',
|
||||
'formnovalidate',
|
||||
'multiple',
|
||||
'readonly',
|
||||
'required'
|
||||
);
|
||||
|
||||
/* defines all attribute value paires, that are allowed for fields */
|
||||
private $attrValues = array(
|
||||
'id',
|
||||
'autocomplete',
|
||||
'placeholder',
|
||||
'size',
|
||||
'rows',
|
||||
'cols',
|
||||
'min',
|
||||
'max',
|
||||
'class',
|
||||
'pattern'
|
||||
);
|
||||
|
||||
/* defines additional data, that are allowed for fields */
|
||||
private $helpers = array(
|
||||
'help',
|
||||
'description'
|
||||
);
|
||||
|
||||
public function __construct($fieldName, array $fieldConfigs)
|
||||
{
|
||||
$this->setName($fieldName);
|
||||
|
||||
$type = isset($fieldConfigs['type']) ? $fieldConfigs['type'] : false;
|
||||
$this->setType($type);
|
||||
|
||||
$label = isset($fieldConfigs['label']) ? $fieldConfigs['label'] : false;
|
||||
$this->setLabel($label);
|
||||
|
||||
$checkboxlabel = isset($fieldConfigs['checkboxlabel']) ? $fieldConfigs['checkboxlabel'] : false;
|
||||
$this->setCheckboxLabel($checkboxlabel);
|
||||
|
||||
$options = isset($fieldConfigs['options']) ? $fieldConfigs['options'] : array();
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->setAttributes($fieldConfigs);
|
||||
|
||||
$this->setAttributeValues($fieldConfigs);
|
||||
|
||||
$this->setHelpers($fieldConfigs);
|
||||
}
|
||||
|
||||
private function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function setType($type)
|
||||
{
|
||||
if(in_array($type, $this->types))
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setCheckboxLabel($label)
|
||||
{
|
||||
$this->checkboxLabel = $label;
|
||||
}
|
||||
|
||||
public function getCheckboxLabel()
|
||||
{
|
||||
return $this->checkboxLabel;
|
||||
}
|
||||
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
private function setOptions(array $options)
|
||||
{
|
||||
foreach($options as $key => $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
if(isset($this->options))
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function setAttributes($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $value)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->attr))
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get all attributes of the field and return them as a string. For usage in templates */
|
||||
public function getAttributes()
|
||||
{
|
||||
$string = false;
|
||||
|
||||
foreach($this->attributes as $key => $attribute)
|
||||
{
|
||||
$string .= ' ' . $key;
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/* set a single attribute. Used e.g. in controller to change the value */
|
||||
public function setAttribute($key, $value)
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
|
||||
public function unsetAttribute($key)
|
||||
{
|
||||
unset($this->attributes[$key]);
|
||||
}
|
||||
|
||||
/* get a single attribute, if it is defined. For usage in templates like getAttribute('required') */
|
||||
public function getAttribute($key)
|
||||
{
|
||||
if(isset($this->attributes[$key]))
|
||||
{
|
||||
return $this->attributes[$key];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function setAttributeValues($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $value)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->attrValues))
|
||||
{
|
||||
$this->attributeValues[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get all attributes as string. For usage in template */
|
||||
public function getAttributeValues()
|
||||
{
|
||||
$string = false;
|
||||
|
||||
foreach($this->attributeValues as $key => $attribute)
|
||||
{
|
||||
$string .= ' ' . $key . '="' . $attribute . '"';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function setAttributeValue($key, $value)
|
||||
{
|
||||
/* pretty dirty, but you should not add a value for a simple checkbox */
|
||||
if($key == 'value' && $this->type == 'checkbox')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attributeValues[$key] = $value;
|
||||
}
|
||||
|
||||
public function getAttributeValue($key)
|
||||
{
|
||||
if(isset($this->attributeValues[$key]))
|
||||
{
|
||||
return $this->attributeValues[$key];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function setHelpers($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $config)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->helpers))
|
||||
{
|
||||
$this->$key = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getHelper($helperName)
|
||||
{
|
||||
if(isset($this->$helperName))
|
||||
{
|
||||
return $this->$helperName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Field
|
||||
{
|
||||
private $type;
|
||||
|
||||
private $label;
|
||||
|
||||
private $name;
|
||||
|
||||
private $content;
|
||||
|
||||
/* holds all simple attributes for this field like "required" */
|
||||
private $attributes = array();
|
||||
|
||||
/* holds all attribute value pairs for this field like "id=''" */
|
||||
private $attributeValues = array();
|
||||
|
||||
/* holds all options for this field (e.g. select options) */
|
||||
private $options = array();
|
||||
|
||||
/* defines all field types, that are allowed */
|
||||
private $types = array(
|
||||
'checkbox',
|
||||
'checkboxlist',
|
||||
'color',
|
||||
'date',
|
||||
'datetime',
|
||||
'datetime-local',
|
||||
'email',
|
||||
'file',
|
||||
'hidden',
|
||||
'image',
|
||||
'month',
|
||||
'number',
|
||||
'password',
|
||||
'radio',
|
||||
'range',
|
||||
'tel',
|
||||
'text',
|
||||
'time',
|
||||
'url',
|
||||
'week',
|
||||
'textarea',
|
||||
'select',
|
||||
'paragraph'
|
||||
);
|
||||
|
||||
/* defines all boolean attributes, that are allowed for fields */
|
||||
private $attr = array(
|
||||
'autofocus',
|
||||
'checked',
|
||||
'disabled',
|
||||
'formnovalidate',
|
||||
'multiple',
|
||||
'readonly',
|
||||
'required'
|
||||
);
|
||||
|
||||
/* defines all attribute value paires, that are allowed for fields */
|
||||
private $attrValues = array(
|
||||
'id',
|
||||
'autocomplete',
|
||||
'placeholder',
|
||||
'size',
|
||||
'rows',
|
||||
'cols',
|
||||
'min',
|
||||
'max',
|
||||
'class',
|
||||
'pattern'
|
||||
);
|
||||
|
||||
/* defines additional data, that are allowed for fields */
|
||||
private $helpers = array(
|
||||
'help',
|
||||
'description'
|
||||
);
|
||||
|
||||
public function __construct($fieldName, array $fieldConfigs)
|
||||
{
|
||||
$this->setName($fieldName);
|
||||
|
||||
$type = isset($fieldConfigs['type']) ? $fieldConfigs['type'] : false;
|
||||
$this->setType($type);
|
||||
|
||||
$label = isset($fieldConfigs['label']) ? $fieldConfigs['label'] : false;
|
||||
$this->setLabel($label);
|
||||
|
||||
$checkboxlabel = isset($fieldConfigs['checkboxlabel']) ? $fieldConfigs['checkboxlabel'] : false;
|
||||
$this->setCheckboxLabel($checkboxlabel);
|
||||
|
||||
$options = isset($fieldConfigs['options']) ? $fieldConfigs['options'] : array();
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->setAttributes($fieldConfigs);
|
||||
|
||||
$this->setAttributeValues($fieldConfigs);
|
||||
|
||||
$this->setHelpers($fieldConfigs);
|
||||
}
|
||||
|
||||
private function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function setType($type)
|
||||
{
|
||||
if(in_array($type, $this->types))
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setCheckboxLabel($label)
|
||||
{
|
||||
$this->checkboxLabel = $label;
|
||||
}
|
||||
|
||||
public function getCheckboxLabel()
|
||||
{
|
||||
return $this->checkboxLabel;
|
||||
}
|
||||
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
private function setOptions(array $options)
|
||||
{
|
||||
foreach($options as $key => $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
if(isset($this->options))
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function setAttributes($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $value)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->attr))
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get all attributes of the field and return them as a string. For usage in templates */
|
||||
public function getAttributes()
|
||||
{
|
||||
$string = false;
|
||||
|
||||
foreach($this->attributes as $key => $attribute)
|
||||
{
|
||||
$string .= ' ' . $key;
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/* set a single attribute. Used e.g. in controller to change the value */
|
||||
public function setAttribute($key, $value)
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
|
||||
public function unsetAttribute($key)
|
||||
{
|
||||
unset($this->attributes[$key]);
|
||||
}
|
||||
|
||||
/* get a single attribute, if it is defined. For usage in templates like getAttribute('required') */
|
||||
public function getAttribute($key)
|
||||
{
|
||||
if(isset($this->attributes[$key]))
|
||||
{
|
||||
return $this->attributes[$key];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function setAttributeValues($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $value)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->attrValues))
|
||||
{
|
||||
$this->attributeValues[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get all attributes as string. For usage in template */
|
||||
public function getAttributeValues()
|
||||
{
|
||||
$string = false;
|
||||
|
||||
foreach($this->attributeValues as $key => $attribute)
|
||||
{
|
||||
$string .= ' ' . $key . '="' . $attribute . '"';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function setAttributeValue($key, $value)
|
||||
{
|
||||
/* pretty dirty, but you should not add a value for a simple checkbox */
|
||||
if($key == 'value' && $this->type == 'checkbox')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attributeValues[$key] = $value;
|
||||
}
|
||||
|
||||
public function getAttributeValue($key)
|
||||
{
|
||||
if(isset($this->attributeValues[$key]))
|
||||
{
|
||||
return $this->attributeValues[$key];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function setHelpers($fieldConfigs)
|
||||
{
|
||||
foreach($fieldConfigs as $key => $config)
|
||||
{
|
||||
if(is_string($key) && in_array($key, $this->helpers))
|
||||
{
|
||||
$this->$key = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getHelper($helperName)
|
||||
{
|
||||
if(isset($this->$helperName))
|
||||
{
|
||||
return $this->$helperName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,104 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use Typemill\Models\Field;
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function getFields($userSettings, $objectType, $objectName, $objectSettings, $formType = false)
|
||||
{
|
||||
# hold all fields in array
|
||||
$fields = array();
|
||||
|
||||
# formtype are backend forms or public forms, only relevant for plugins for now
|
||||
$formType = $formType ? $formType : 'forms';
|
||||
|
||||
# iterate through all fields of the objectSetting (theme or plugin)
|
||||
foreach($objectSettings[$formType]['fields'] as $fieldName => $fieldConfigurations)
|
||||
{
|
||||
if($fieldConfigurations['type'] == 'fieldset')
|
||||
{
|
||||
# if it is a fieldset, then create a subset for the containing field and read them with a recursive function
|
||||
$subSettings = $objectSettings;
|
||||
$subSettings['forms'] = $fieldConfigurations;
|
||||
|
||||
$fieldset = array();
|
||||
$fieldset['type'] = 'fieldset';
|
||||
$fieldset['legend'] = $fieldConfigurations['legend'];
|
||||
$fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings, $formType);
|
||||
$fields[] = $fieldset;
|
||||
}
|
||||
else
|
||||
{
|
||||
# For label, helptext and description you can use the value of another field. This is useful e.g. to localize the label of public forms via plugin settings.
|
||||
if(isset($fieldConfigurations['label']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['label']]))
|
||||
{
|
||||
$fieldConfigurations['label'] = $userSettings[$objectType][$objectName][$fieldConfigurations['label']];
|
||||
}
|
||||
if(isset($fieldConfigurations['help']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['help']]))
|
||||
{
|
||||
$fieldConfigurations['help'] = $userSettings[$objectType][$objectName][$fieldConfigurations['help']];
|
||||
}
|
||||
if(isset($fieldConfigurations['description']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['description']]))
|
||||
{
|
||||
$fieldConfigurations['description'] = $userSettings[$objectType][$objectName][$fieldConfigurations['description']];
|
||||
}
|
||||
|
||||
# for each field generate a new field object with the field name and the field configurations
|
||||
$field = new Field($fieldName, $fieldConfigurations);
|
||||
|
||||
# handle the value for the field
|
||||
$userValue = false;
|
||||
|
||||
# first, add the default value from the original plugin or theme settings
|
||||
if(isset($objectSettings['settings'][$fieldName]))
|
||||
{
|
||||
$userValue = $objectSettings['settings'][$fieldName];
|
||||
}
|
||||
|
||||
# now overwrite the default values with the user values stored in the user settings
|
||||
if(isset($userSettings[$objectType][$objectName][$fieldName]))
|
||||
{
|
||||
$userValue = $userSettings[$objectType][$objectName][$fieldName];
|
||||
}
|
||||
|
||||
# now overwrite user-values, if there are old-input values from the actual form (e.g. after input error)
|
||||
if(isset($_SESSION['old'][$objectName][$fieldName]))
|
||||
{
|
||||
$userValue = $_SESSION['old'][$objectName][$fieldName];
|
||||
}
|
||||
|
||||
# Now prepopulate the field object with the value */
|
||||
if($field->getType() == "textarea" || $field->getType() == "paragraph")
|
||||
{
|
||||
if($userValue)
|
||||
{
|
||||
$field->setContent($userValue);
|
||||
}
|
||||
}
|
||||
elseif($field->getType() == "checkbox")
|
||||
{
|
||||
# checkboxes need a special treatment, because field does not exist in settings if unchecked by user
|
||||
if(isset($userSettings[$objectType][$objectName][$fieldName]))
|
||||
{
|
||||
$field->setAttribute('checked', 'true');
|
||||
}
|
||||
else
|
||||
{
|
||||
$field->unsetAttribute('chhecked');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$field->setAttributeValue('value', $userValue);
|
||||
}
|
||||
|
||||
# add the field to the field-List
|
||||
$fields[] = $field;
|
||||
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use Typemill\Models\Field;
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function getFields($userSettings, $objectType, $objectName, $objectSettings, $formType = false)
|
||||
{
|
||||
# hold all fields in array
|
||||
$fields = array();
|
||||
|
||||
# formtype are backend forms or public forms, only relevant for plugins for now
|
||||
$formType = $formType ? $formType : 'forms';
|
||||
|
||||
# iterate through all fields of the objectSetting (theme or plugin)
|
||||
foreach($objectSettings[$formType]['fields'] as $fieldName => $fieldConfigurations)
|
||||
{
|
||||
if($fieldConfigurations['type'] == 'fieldset')
|
||||
{
|
||||
# if it is a fieldset, then create a subset for the containing field and read them with a recursive function
|
||||
$subSettings = $objectSettings;
|
||||
$subSettings['forms'] = $fieldConfigurations;
|
||||
|
||||
$fieldset = array();
|
||||
$fieldset['type'] = 'fieldset';
|
||||
$fieldset['legend'] = $fieldConfigurations['legend'];
|
||||
$fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings, $formType);
|
||||
$fields[] = $fieldset;
|
||||
}
|
||||
else
|
||||
{
|
||||
# For label, helptext and description you can use the value of another field. This is useful e.g. to localize the label of public forms via plugin settings.
|
||||
if(isset($fieldConfigurations['label']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['label']]))
|
||||
{
|
||||
$fieldConfigurations['label'] = $userSettings[$objectType][$objectName][$fieldConfigurations['label']];
|
||||
}
|
||||
if(isset($fieldConfigurations['help']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['help']]))
|
||||
{
|
||||
$fieldConfigurations['help'] = $userSettings[$objectType][$objectName][$fieldConfigurations['help']];
|
||||
}
|
||||
if(isset($fieldConfigurations['description']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['description']]))
|
||||
{
|
||||
$fieldConfigurations['description'] = $userSettings[$objectType][$objectName][$fieldConfigurations['description']];
|
||||
}
|
||||
|
||||
# for each field generate a new field object with the field name and the field configurations
|
||||
$field = new Field($fieldName, $fieldConfigurations);
|
||||
|
||||
# handle the value for the field
|
||||
$userValue = false;
|
||||
|
||||
# first, add the default value from the original plugin or theme settings
|
||||
if(isset($objectSettings['settings'][$fieldName]))
|
||||
{
|
||||
$userValue = $objectSettings['settings'][$fieldName];
|
||||
}
|
||||
|
||||
# now overwrite the default values with the user values stored in the user settings
|
||||
if(isset($userSettings[$objectType][$objectName][$fieldName]))
|
||||
{
|
||||
$userValue = $userSettings[$objectType][$objectName][$fieldName];
|
||||
}
|
||||
|
||||
# now overwrite user-values, if there are old-input values from the actual form (e.g. after input error)
|
||||
if(isset($_SESSION['old'][$objectName][$fieldName]))
|
||||
{
|
||||
$userValue = $_SESSION['old'][$objectName][$fieldName];
|
||||
}
|
||||
|
||||
# Now prepopulate the field object with the value */
|
||||
if($field->getType() == "textarea" || $field->getType() == "paragraph")
|
||||
{
|
||||
if($userValue)
|
||||
{
|
||||
$field->setContent($userValue);
|
||||
}
|
||||
}
|
||||
elseif($field->getType() == "checkbox")
|
||||
{
|
||||
# checkboxes need a special treatment, because field does not exist in settings if unchecked by user
|
||||
if(isset($userSettings[$objectType][$objectName][$fieldName]))
|
||||
{
|
||||
$field->setAttribute('checked', 'true');
|
||||
}
|
||||
else
|
||||
{
|
||||
$field->unsetAttribute('chhecked');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$field->setAttributeValue('value', $userValue);
|
||||
}
|
||||
|
||||
# add the field to the field-List
|
||||
$fields[] = $field;
|
||||
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
@ -1,434 +1,434 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use \URLify;
|
||||
|
||||
class Folder
|
||||
{
|
||||
|
||||
/*
|
||||
* scans content of a folder (without recursion)
|
||||
* vars: folder path as string
|
||||
* returns: one-dimensional array with names of folders and files
|
||||
*/
|
||||
public static function scanFolderFlat($folderPath)
|
||||
{
|
||||
$folderItems = scandir($folderPath);
|
||||
$folderContent = array();
|
||||
|
||||
foreach ($folderItems as $key => $item)
|
||||
{
|
||||
if (!in_array($item, array(".","..")))
|
||||
{
|
||||
$nameParts = self::getStringParts($item);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md' OR $fileType == 'txt' )
|
||||
{
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* scans content of a folder recursively
|
||||
* vars: folder path as string
|
||||
* returns: multi-dimensional array with names of folders and files
|
||||
*/
|
||||
public static function scanFolder($folderPath, $draft = false)
|
||||
{
|
||||
$folderItems = scandir($folderPath);
|
||||
$folderContent = array();
|
||||
|
||||
# if it is the live version and if it is a folder that is not published, then do not show the folder and its content.
|
||||
if(!$draft && !in_array('index.md', $folderItems)){ return false; }
|
||||
|
||||
foreach ($folderItems as $key => $item)
|
||||
{
|
||||
if (!in_array($item, array(".","..")))
|
||||
{
|
||||
if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item))
|
||||
{
|
||||
$subFolder = $item;
|
||||
$folderContent[$subFolder] = self::scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft);
|
||||
}
|
||||
else
|
||||
{
|
||||
$nameParts = self::getStringParts($item);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md')
|
||||
{
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
if($draft === true && $fileType == 'txt')
|
||||
{
|
||||
if(isset($last) && ($last == implode($nameParts)) )
|
||||
{
|
||||
array_pop($folderContent);
|
||||
$item = $item . 'md';
|
||||
}
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
/* store the name of the last file */
|
||||
$last = implode($nameParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transforms array of folder item into an array of item-objects with additional information for each item
|
||||
* vars: multidimensional array with folder- and file-names
|
||||
* returns: array of objects. Each object contains information about an item (file or folder).
|
||||
*/
|
||||
public static function getFolderContentDetails(array $folderContent, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
|
||||
{
|
||||
$contentDetails = [];
|
||||
$iteration = 0;
|
||||
$chapternr = 1;
|
||||
|
||||
foreach($folderContent as $key => $name)
|
||||
{
|
||||
$item = new \stdClass();
|
||||
|
||||
if(is_array($name))
|
||||
{
|
||||
$nameParts = self::getStringParts($key);
|
||||
|
||||
$fileType = '';
|
||||
if(in_array('index.md', $name))
|
||||
{
|
||||
$fileType = 'md';
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $name))
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(in_array('index.txtmd', $name))
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$item->originalName = $key;
|
||||
$item->elementType = 'folder';
|
||||
$item->status = $status;
|
||||
$item->fileType = $fileType;
|
||||
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
|
||||
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
|
||||
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->key = $iteration;
|
||||
$item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
|
||||
$item->keyPathArray = explode('.', $item->keyPath);
|
||||
$item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr;
|
||||
$item->active = false;
|
||||
$item->activeParent = false;
|
||||
|
||||
$item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
|
||||
}
|
||||
else
|
||||
{
|
||||
# do not use files in base folder (only folders are allowed)
|
||||
if(!isset($keyPath)) continue;
|
||||
|
||||
# do not use index files
|
||||
if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue;
|
||||
|
||||
$nameParts = self::getStringParts($name);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md')
|
||||
{
|
||||
$status = 'published';
|
||||
}
|
||||
elseif($fileType == 'txt')
|
||||
{
|
||||
$status = 'unpublished';
|
||||
}
|
||||
else
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$item->originalName = $name;
|
||||
$item->elementType = 'file';
|
||||
$item->status = $status;
|
||||
$item->fileType = $fileType;
|
||||
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $name;
|
||||
$item->key = $iteration;
|
||||
$item->keyPath = $keyPath . '.' . $iteration;
|
||||
$item->keyPathArray = explode('.',$item->keyPath);
|
||||
$item->chapter = $chapter . '.' . $chapternr;
|
||||
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
|
||||
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->active = false;
|
||||
$item->activeParent = false;
|
||||
}
|
||||
$iteration++;
|
||||
$chapternr++;
|
||||
$contentDetails[] = $item;
|
||||
}
|
||||
return $contentDetails;
|
||||
}
|
||||
|
||||
public static function getItemForUrl($folderContentDetails, $url, $result = NULL)
|
||||
{
|
||||
foreach($folderContentDetails as $key => $item)
|
||||
{
|
||||
if($item->urlRel === $url)
|
||||
{
|
||||
# set item active, needed for move item in navigation
|
||||
$item->active = true;
|
||||
$result = $item;
|
||||
}
|
||||
elseif($item->elementType === "folder")
|
||||
{
|
||||
$result = self::getItemForUrl($item->folderContent, $url, $result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getPagingForItem($content, $item)
|
||||
{
|
||||
$keyPos = count($item->keyPathArray)-1;
|
||||
$thisChapArray = $item->keyPathArray;
|
||||
$nextItemArray = $item->keyPathArray;
|
||||
$prevItemArray = $item->keyPathArray;
|
||||
|
||||
$item->thisChapter = false;
|
||||
$item->prevItem = false;
|
||||
$item->nextItem = false;
|
||||
|
||||
|
||||
/************************
|
||||
* ADD THIS CHAPTER *
|
||||
************************/
|
||||
|
||||
if($keyPos > 0)
|
||||
{
|
||||
array_pop($thisChapArray);
|
||||
$item->thisChapter = self::getItemWithKeyPath($content, $thisChapArray);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD NEXT ITEM *
|
||||
************************/
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
/* get the first element in the folder */
|
||||
$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
|
||||
}
|
||||
|
||||
if(!$item->nextItem)
|
||||
{
|
||||
$nextItemArray[$keyPos]++;
|
||||
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
|
||||
}
|
||||
|
||||
while(!$item->nextItem)
|
||||
{
|
||||
array_pop($nextItemArray);
|
||||
if(empty($nextItemArray)) break;
|
||||
$newKeyPos = count($nextItemArray)-1;
|
||||
$nextItemArray[$newKeyPos]++;
|
||||
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD PREVIOUS ITEM *
|
||||
************************/
|
||||
|
||||
if($prevItemArray[$keyPos] > 0)
|
||||
{
|
||||
$prevItemArray[$keyPos]--;
|
||||
$item->prevItem = self::getItemWithKeyPath($content, $prevItemArray);
|
||||
|
||||
if($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
|
||||
{
|
||||
/* get last item in folder */
|
||||
$item->prevItem = self::getLastItemOfFolder($item->prevItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->prevItem = $item->thisChapter;
|
||||
}
|
||||
|
||||
if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
|
||||
if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
|
||||
if($item->thisChapter){unset($item->thisChapter->folderContent); }
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a copy of an item with a key
|
||||
* @param array $content with the full structure of the content as multidimensional array
|
||||
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
|
||||
* @return array $item
|
||||
*/
|
||||
|
||||
public static function getItemWithKeyPath($content, array $searchArray)
|
||||
{
|
||||
$item = false;
|
||||
|
||||
foreach($searchArray as $key => $itemKey)
|
||||
{
|
||||
$item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false;
|
||||
|
||||
unset($searchArray[$key]);
|
||||
if(!empty($searchArray) && $item)
|
||||
{
|
||||
return self::getItemWithKeyPath($item->folderContent, $searchArray);
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
# https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable
|
||||
# NOT IN USE
|
||||
public static function getItemWithKeyPathNew($array, array $keys)
|
||||
{
|
||||
$item = $array;
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
$item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key];
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key
|
||||
* @param array $content with the full structure of the content as multidimensional array
|
||||
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
|
||||
* @return array $item
|
||||
* NOT IN USE ??
|
||||
*/
|
||||
|
||||
public static function extractItemWithKeyPath($structure, array $keys)
|
||||
{
|
||||
$result = &$structure;
|
||||
$last = array_pop($keys);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if(isset($result[$key]->folderContent))
|
||||
{
|
||||
$result = &$result[$key]->folderContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = &$result[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$item = $result[$last];
|
||||
unset($result[$last]);
|
||||
|
||||
return array('structure' => $structure, 'item' => $item);
|
||||
}
|
||||
|
||||
/* get breadcrumb as copied array, set elements active in original and mark parent element in original */
|
||||
public static function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL)
|
||||
{
|
||||
if(!$i){ $i = 0; $breadcrumb = array();}
|
||||
|
||||
while($i < count($searchArray))
|
||||
{
|
||||
$item = $content[$searchArray[$i]];
|
||||
|
||||
if($i == count($searchArray)-1)
|
||||
{
|
||||
$item->active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->activeParent = true;
|
||||
}
|
||||
/*
|
||||
$item->active = true;
|
||||
if($i == count($searchArray)-2)
|
||||
{
|
||||
$item->activeParent = true;
|
||||
}
|
||||
*/
|
||||
|
||||
$copy = clone($item);
|
||||
if($copy->elementType == 'folder')
|
||||
{
|
||||
unset($copy->folderContent);
|
||||
$content = $item->folderContent;
|
||||
}
|
||||
$breadcrumb[] = $copy;
|
||||
|
||||
$i++;
|
||||
return self::getBreadcrumb($content, $searchArray, $i++, $breadcrumb);
|
||||
}
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
public static function getParentItem($content, $searchArray, $iteration = NULL)
|
||||
{
|
||||
if(!$iteration){ $iteration = 0; }
|
||||
while($iteration < count($searchArray)-2)
|
||||
{
|
||||
$content = $content[$searchArray[$iteration]]->folderContent;
|
||||
$iteration++;
|
||||
return self::getParentItem($content, $searchArray, $iteration);
|
||||
}
|
||||
return $content[$searchArray[$iteration]];
|
||||
}
|
||||
|
||||
private static function getLastItemOfFolder($folder)
|
||||
{
|
||||
$lastItem = end($folder->folderContent);
|
||||
if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
|
||||
{
|
||||
return self::getLastItemOfFolder($lastItem);
|
||||
}
|
||||
return $lastItem;
|
||||
}
|
||||
|
||||
public static function getStringParts($name)
|
||||
{
|
||||
return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name);
|
||||
}
|
||||
|
||||
public static function getFileType($fileName)
|
||||
{
|
||||
$parts = preg_split('/\./',$fileName);
|
||||
return end($parts);
|
||||
}
|
||||
|
||||
public static function splitFileName($fileName)
|
||||
{
|
||||
$parts = preg_split('/\./',$fileName);
|
||||
return $parts;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use \URLify;
|
||||
|
||||
class Folder
|
||||
{
|
||||
|
||||
/*
|
||||
* scans content of a folder (without recursion)
|
||||
* vars: folder path as string
|
||||
* returns: one-dimensional array with names of folders and files
|
||||
*/
|
||||
public static function scanFolderFlat($folderPath)
|
||||
{
|
||||
$folderItems = scandir($folderPath);
|
||||
$folderContent = array();
|
||||
|
||||
foreach ($folderItems as $key => $item)
|
||||
{
|
||||
if (!in_array($item, array(".","..")))
|
||||
{
|
||||
$nameParts = self::getStringParts($item);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md' OR $fileType == 'txt' )
|
||||
{
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* scans content of a folder recursively
|
||||
* vars: folder path as string
|
||||
* returns: multi-dimensional array with names of folders and files
|
||||
*/
|
||||
public static function scanFolder($folderPath, $draft = false)
|
||||
{
|
||||
$folderItems = scandir($folderPath);
|
||||
$folderContent = array();
|
||||
|
||||
# if it is the live version and if it is a folder that is not published, then do not show the folder and its content.
|
||||
if(!$draft && !in_array('index.md', $folderItems)){ return false; }
|
||||
|
||||
foreach ($folderItems as $key => $item)
|
||||
{
|
||||
if (!in_array($item, array(".","..")))
|
||||
{
|
||||
if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item))
|
||||
{
|
||||
$subFolder = $item;
|
||||
$folderContent[$subFolder] = self::scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft);
|
||||
}
|
||||
else
|
||||
{
|
||||
$nameParts = self::getStringParts($item);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md')
|
||||
{
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
if($draft === true && $fileType == 'txt')
|
||||
{
|
||||
if(isset($last) && ($last == implode($nameParts)) )
|
||||
{
|
||||
array_pop($folderContent);
|
||||
$item = $item . 'md';
|
||||
}
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
/* store the name of the last file */
|
||||
$last = implode($nameParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transforms array of folder item into an array of item-objects with additional information for each item
|
||||
* vars: multidimensional array with folder- and file-names
|
||||
* returns: array of objects. Each object contains information about an item (file or folder).
|
||||
*/
|
||||
public static function getFolderContentDetails(array $folderContent, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
|
||||
{
|
||||
$contentDetails = [];
|
||||
$iteration = 0;
|
||||
$chapternr = 1;
|
||||
|
||||
foreach($folderContent as $key => $name)
|
||||
{
|
||||
$item = new \stdClass();
|
||||
|
||||
if(is_array($name))
|
||||
{
|
||||
$nameParts = self::getStringParts($key);
|
||||
|
||||
$fileType = '';
|
||||
if(in_array('index.md', $name))
|
||||
{
|
||||
$fileType = 'md';
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $name))
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(in_array('index.txtmd', $name))
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$item->originalName = $key;
|
||||
$item->elementType = 'folder';
|
||||
$item->status = $status;
|
||||
$item->fileType = $fileType;
|
||||
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
|
||||
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
|
||||
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->key = $iteration;
|
||||
$item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
|
||||
$item->keyPathArray = explode('.', $item->keyPath);
|
||||
$item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr;
|
||||
$item->active = false;
|
||||
$item->activeParent = false;
|
||||
|
||||
$item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
|
||||
}
|
||||
else
|
||||
{
|
||||
# do not use files in base folder (only folders are allowed)
|
||||
if(!isset($keyPath)) continue;
|
||||
|
||||
# do not use index files
|
||||
if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue;
|
||||
|
||||
$nameParts = self::getStringParts($name);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md')
|
||||
{
|
||||
$status = 'published';
|
||||
}
|
||||
elseif($fileType == 'txt')
|
||||
{
|
||||
$status = 'unpublished';
|
||||
}
|
||||
else
|
||||
{
|
||||
$fileType = 'txt';
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$item->originalName = $name;
|
||||
$item->elementType = 'file';
|
||||
$item->status = $status;
|
||||
$item->fileType = $fileType;
|
||||
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $name;
|
||||
$item->key = $iteration;
|
||||
$item->keyPath = $keyPath . '.' . $iteration;
|
||||
$item->keyPathArray = explode('.',$item->keyPath);
|
||||
$item->chapter = $chapter . '.' . $chapternr;
|
||||
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
|
||||
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
$item->active = false;
|
||||
$item->activeParent = false;
|
||||
}
|
||||
$iteration++;
|
||||
$chapternr++;
|
||||
$contentDetails[] = $item;
|
||||
}
|
||||
return $contentDetails;
|
||||
}
|
||||
|
||||
public static function getItemForUrl($folderContentDetails, $url, $result = NULL)
|
||||
{
|
||||
foreach($folderContentDetails as $key => $item)
|
||||
{
|
||||
if($item->urlRel === $url)
|
||||
{
|
||||
# set item active, needed for move item in navigation
|
||||
$item->active = true;
|
||||
$result = $item;
|
||||
}
|
||||
elseif($item->elementType === "folder")
|
||||
{
|
||||
$result = self::getItemForUrl($item->folderContent, $url, $result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getPagingForItem($content, $item)
|
||||
{
|
||||
$keyPos = count($item->keyPathArray)-1;
|
||||
$thisChapArray = $item->keyPathArray;
|
||||
$nextItemArray = $item->keyPathArray;
|
||||
$prevItemArray = $item->keyPathArray;
|
||||
|
||||
$item->thisChapter = false;
|
||||
$item->prevItem = false;
|
||||
$item->nextItem = false;
|
||||
|
||||
|
||||
/************************
|
||||
* ADD THIS CHAPTER *
|
||||
************************/
|
||||
|
||||
if($keyPos > 0)
|
||||
{
|
||||
array_pop($thisChapArray);
|
||||
$item->thisChapter = self::getItemWithKeyPath($content, $thisChapArray);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD NEXT ITEM *
|
||||
************************/
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
/* get the first element in the folder */
|
||||
$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
|
||||
}
|
||||
|
||||
if(!$item->nextItem)
|
||||
{
|
||||
$nextItemArray[$keyPos]++;
|
||||
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
|
||||
}
|
||||
|
||||
while(!$item->nextItem)
|
||||
{
|
||||
array_pop($nextItemArray);
|
||||
if(empty($nextItemArray)) break;
|
||||
$newKeyPos = count($nextItemArray)-1;
|
||||
$nextItemArray[$newKeyPos]++;
|
||||
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD PREVIOUS ITEM *
|
||||
************************/
|
||||
|
||||
if($prevItemArray[$keyPos] > 0)
|
||||
{
|
||||
$prevItemArray[$keyPos]--;
|
||||
$item->prevItem = self::getItemWithKeyPath($content, $prevItemArray);
|
||||
|
||||
if($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
|
||||
{
|
||||
/* get last item in folder */
|
||||
$item->prevItem = self::getLastItemOfFolder($item->prevItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->prevItem = $item->thisChapter;
|
||||
}
|
||||
|
||||
if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
|
||||
if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
|
||||
if($item->thisChapter){unset($item->thisChapter->folderContent); }
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a copy of an item with a key
|
||||
* @param array $content with the full structure of the content as multidimensional array
|
||||
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
|
||||
* @return array $item
|
||||
*/
|
||||
|
||||
public static function getItemWithKeyPath($content, array $searchArray)
|
||||
{
|
||||
$item = false;
|
||||
|
||||
foreach($searchArray as $key => $itemKey)
|
||||
{
|
||||
$item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false;
|
||||
|
||||
unset($searchArray[$key]);
|
||||
if(!empty($searchArray) && $item)
|
||||
{
|
||||
return self::getItemWithKeyPath($item->folderContent, $searchArray);
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
# https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable
|
||||
# NOT IN USE
|
||||
public static function getItemWithKeyPathNew($array, array $keys)
|
||||
{
|
||||
$item = $array;
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
$item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key];
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key
|
||||
* @param array $content with the full structure of the content as multidimensional array
|
||||
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
|
||||
* @return array $item
|
||||
* NOT IN USE ??
|
||||
*/
|
||||
|
||||
public static function extractItemWithKeyPath($structure, array $keys)
|
||||
{
|
||||
$result = &$structure;
|
||||
$last = array_pop($keys);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if(isset($result[$key]->folderContent))
|
||||
{
|
||||
$result = &$result[$key]->folderContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = &$result[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$item = $result[$last];
|
||||
unset($result[$last]);
|
||||
|
||||
return array('structure' => $structure, 'item' => $item);
|
||||
}
|
||||
|
||||
/* get breadcrumb as copied array, set elements active in original and mark parent element in original */
|
||||
public static function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL)
|
||||
{
|
||||
if(!$i){ $i = 0; $breadcrumb = array();}
|
||||
|
||||
while($i < count($searchArray))
|
||||
{
|
||||
$item = $content[$searchArray[$i]];
|
||||
|
||||
if($i == count($searchArray)-1)
|
||||
{
|
||||
$item->active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->activeParent = true;
|
||||
}
|
||||
/*
|
||||
$item->active = true;
|
||||
if($i == count($searchArray)-2)
|
||||
{
|
||||
$item->activeParent = true;
|
||||
}
|
||||
*/
|
||||
|
||||
$copy = clone($item);
|
||||
if($copy->elementType == 'folder')
|
||||
{
|
||||
unset($copy->folderContent);
|
||||
$content = $item->folderContent;
|
||||
}
|
||||
$breadcrumb[] = $copy;
|
||||
|
||||
$i++;
|
||||
return self::getBreadcrumb($content, $searchArray, $i++, $breadcrumb);
|
||||
}
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
public static function getParentItem($content, $searchArray, $iteration = NULL)
|
||||
{
|
||||
if(!$iteration){ $iteration = 0; }
|
||||
while($iteration < count($searchArray)-2)
|
||||
{
|
||||
$content = $content[$searchArray[$iteration]]->folderContent;
|
||||
$iteration++;
|
||||
return self::getParentItem($content, $searchArray, $iteration);
|
||||
}
|
||||
return $content[$searchArray[$iteration]];
|
||||
}
|
||||
|
||||
private static function getLastItemOfFolder($folder)
|
||||
{
|
||||
$lastItem = end($folder->folderContent);
|
||||
if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
|
||||
{
|
||||
return self::getLastItemOfFolder($lastItem);
|
||||
}
|
||||
return $lastItem;
|
||||
}
|
||||
|
||||
public static function getStringParts($name)
|
||||
{
|
||||
return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name);
|
||||
}
|
||||
|
||||
public static function getFileType($fileName)
|
||||
{
|
||||
$parts = preg_split('/\./',$fileName);
|
||||
return end($parts);
|
||||
}
|
||||
|
||||
public static function splitFileName($fileName)
|
||||
{
|
||||
$parts = preg_split('/\./',$fileName);
|
||||
return $parts;
|
||||
}
|
||||
}
|
@ -1,28 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Helpers{
|
||||
|
||||
public static function printTimer($timer)
|
||||
{
|
||||
$lastTime = NULL;
|
||||
$table = '<html><body><table>';
|
||||
$table .= '<tr><th>Breakpoint</th><th>Time</th><th>Duration</th></tr>';
|
||||
foreach($timer as $breakpoint => $time)
|
||||
{
|
||||
$duration = $time - $lastTime;
|
||||
|
||||
$table .= '<tr>';
|
||||
$table .= '<td>' . $breakpoint . '</td>';
|
||||
$table .= '<td>' . $time . '</td>';
|
||||
$table .= '<td>' . $duration . '</td>';
|
||||
$table .= '</tr>';
|
||||
|
||||
$lastTime = $time;
|
||||
}
|
||||
$table .= '</table></body></html>';
|
||||
echo $table;
|
||||
exit;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Helpers{
|
||||
|
||||
public static function printTimer($timer)
|
||||
{
|
||||
$lastTime = NULL;
|
||||
$table = '<html><body><table>';
|
||||
$table .= '<tr><th>Breakpoint</th><th>Time</th><th>Duration</th></tr>';
|
||||
foreach($timer as $breakpoint => $time)
|
||||
{
|
||||
$duration = $time - $lastTime;
|
||||
|
||||
$table .= '<tr>';
|
||||
$table .= '<td>' . $breakpoint . '</td>';
|
||||
$table .= '<td>' . $time . '</td>';
|
||||
$table .= '<td>' . $duration . '</td>';
|
||||
$table .= '</tr>';
|
||||
|
||||
$lastTime = $time;
|
||||
}
|
||||
$table .= '</table></body></html>';
|
||||
echo $table;
|
||||
exit;
|
||||
}
|
||||
}
|
@ -1,336 +1,336 @@
|
||||
<?php
|
||||
namespace Typemill\Models;
|
||||
|
||||
class ProcessImage
|
||||
{
|
||||
public function createImage(string $image, array $desiredSizes)
|
||||
{
|
||||
# fix error from jpeg-library
|
||||
ini_set ('gd.jpeg_ignore_warning', 1);
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
|
||||
# clear temporary folder
|
||||
$this->clearTempFolder();
|
||||
|
||||
# decode the image from base64-string
|
||||
$imageDecoded = $this->decodeImage($image);
|
||||
$imageData = $imageDecoded["image"];
|
||||
$imageType = $imageDecoded["type"];
|
||||
|
||||
# transform image-stream into image
|
||||
$image = imagecreatefromstring($imageData);
|
||||
|
||||
# get the size of the original image
|
||||
$imageSize = $this->getImageSize($image);
|
||||
|
||||
# check the desired sizes and calculate the height, if not set
|
||||
$desiredSizes = $this->setHeight($imageSize, $desiredSizes);
|
||||
|
||||
# resize the images
|
||||
$resizedImages = $this->imageResize($image, $imageSize, $desiredSizes, $imageType);
|
||||
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR . 'media';
|
||||
$tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
|
||||
$this->saveOriginal($tmpFolder, $imageData, 'original', $imageType);
|
||||
|
||||
if($imageType == "gif" && $this->detectAnimatedGif($imageData))
|
||||
{
|
||||
$this->saveOriginal($tmpFolder, $imageData, 'live', $imageType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
# temporary store resized images
|
||||
foreach($resizedImages as $key => $resizedImage)
|
||||
{
|
||||
$this->saveImage($tmpFolder, $resizedImage, $key, $imageType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function detectAnimatedGif($image_file_contents)
|
||||
{
|
||||
$is_animated = preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', $image_file_contents);
|
||||
if ($is_animated == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function publishImage(array $desiredSizes, $name = false)
|
||||
{
|
||||
/* get images from tmp folder */
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR . 'media';
|
||||
$tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
$originalFolder = $basePath . DIRECTORY_SEPARATOR . 'original' . DIRECTORY_SEPARATOR;
|
||||
$liveFolder = $basePath . DIRECTORY_SEPARATOR . 'live' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if(!file_exists($originalFolder)){ mkdir($originalFolder, 0774, true); }
|
||||
if(!file_exists($liveFolder)){ mkdir($liveFolder, 0774, true); }
|
||||
|
||||
$name = $name ? $name : uniqid();
|
||||
|
||||
$files = scandir($tmpFolder);
|
||||
$success = true;
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if (!in_array($file, array(".","..")))
|
||||
{
|
||||
$tmpfilename = explode(".", $file);
|
||||
|
||||
if($tmpfilename[0] == 'original')
|
||||
{
|
||||
$success = rename($tmpFolder . $file, $originalFolder . $name . '-' . $file);
|
||||
}
|
||||
else
|
||||
{
|
||||
$success = rename($tmpFolder . $file, $liveFolder . $name . '-' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($success)
|
||||
{
|
||||
return 'media/live/' . $name . '-live.' . $tmpfilename[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function decodeImage(string $image)
|
||||
{
|
||||
$imageParts = explode(";base64,", $image);
|
||||
$imageType = explode("/", $imageParts[0]);
|
||||
$imageData = base64_decode($imageParts[1]);
|
||||
|
||||
if ($imageData !== false)
|
||||
{
|
||||
return array("image" => $imageData, "type" => $imageType[1]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getImageSize($image)
|
||||
{
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
return array('width' => $width, 'height' => $height);
|
||||
}
|
||||
|
||||
public function setHeight(array $imageSize, array $desiredSizes)
|
||||
{
|
||||
foreach($desiredSizes as $key => $desiredSize)
|
||||
{
|
||||
if($desiredSize['width'] > $imageSize['width'])
|
||||
{
|
||||
$desiredSizes[$key] = $imageSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($desiredSize['height']))
|
||||
{
|
||||
$resizeFactor = $imageSize['width'] / $desiredSize['width'];
|
||||
$desiredSizes[$key]['height'] = round( ($imageSize['height'] / $resizeFactor), 0);
|
||||
}
|
||||
}
|
||||
return $desiredSizes;
|
||||
}
|
||||
|
||||
public function imageResize($imageData, array $imageSize, array $desiredSizes, $imageType)
|
||||
{
|
||||
$copiedImages = array();
|
||||
$source_aspect_ratio = $imageSize['width'] / $imageSize['height'];
|
||||
|
||||
foreach($desiredSizes as $key => $desiredSize)
|
||||
{
|
||||
$desired_aspect_ratio = $desiredSize['width'] / $desiredSize['height'];
|
||||
|
||||
if ( $source_aspect_ratio > $desired_aspect_ratio )
|
||||
{
|
||||
# when source image is wider
|
||||
$temp_height = $desiredSize['height'];
|
||||
$temp_width = ( int ) ($desiredSize['height'] * $source_aspect_ratio);
|
||||
$temp_width = round($temp_width, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
# when source image is similar or taller
|
||||
$temp_width = $desiredSize['width'];
|
||||
$temp_height = ( int ) ($desiredSize['width'] / $source_aspect_ratio);
|
||||
$temp_height = round($temp_height, 0);
|
||||
}
|
||||
|
||||
# Create a temporary GD image with desired size
|
||||
$temp_gdim = imagecreatetruecolor( $temp_width, $temp_height );
|
||||
|
||||
if ($imageType == "gif")
|
||||
{
|
||||
$transparent_index = imagecolortransparent($imageData);
|
||||
imagepalettecopy($imageData, $temp_gdim);
|
||||
imagefill($temp_gdim, 0, 0, $transparent_index);
|
||||
imagecolortransparent($temp_gdim, $transparent_index);
|
||||
imagetruecolortopalette($temp_gdim, true, 256);
|
||||
}
|
||||
elseif($imageType == "png")
|
||||
{
|
||||
imagealphablending($temp_gdim, false);
|
||||
imagesavealpha($temp_gdim, true);
|
||||
$transparent = imagecolorallocatealpha($temp_gdim, 255, 255, 255, 127);
|
||||
imagefilledrectangle($temp_gdim, 0, 0, $temp_width, $temp_height, $transparent);
|
||||
}
|
||||
|
||||
# resize image
|
||||
imagecopyresampled(
|
||||
$temp_gdim,
|
||||
$imageData,
|
||||
0, 0,
|
||||
0, 0,
|
||||
$temp_width, $temp_height,
|
||||
$imageSize['width'], $imageSize['height']
|
||||
);
|
||||
|
||||
$copiedImages[$key] = $temp_gdim;
|
||||
|
||||
/*
|
||||
|
||||
# Copy cropped region from temporary image into the desired GD image
|
||||
$x0 = ( $temp_width - $desiredSize['width'] ) / 2;
|
||||
$y0 = ( $temp_height - $desiredSize['height'] ) / 2;
|
||||
|
||||
$desired_gdim = imagecreatetruecolor( $desiredSize['width'], $desiredSize['height'] );
|
||||
|
||||
if ($imageType == "gif")
|
||||
{
|
||||
imagepalettecopy($temp_gdim, $desired_gdim);
|
||||
imagefill($desired_gdim, 0, 0, $transparent_index);
|
||||
imagecolortransparent($desired_gdim, $transparent_index);
|
||||
imagetruecolortopalette($desired_gdim, true, 256);
|
||||
}
|
||||
elseif($imageType == "png")
|
||||
{
|
||||
imagealphablending($desired_gdim, false);
|
||||
imagesavealpha($desired_gdim,true);
|
||||
$transparent = imagecolorallocatealpha($desired_gdim, 255, 255, 255, 127);
|
||||
imagefilledrectangle($desired_gdim, 0, 0, $desired_size['with'], $desired_size['height'], $transparent);
|
||||
}
|
||||
|
||||
imagecopyresampled(
|
||||
$desired_gdim,
|
||||
$temp_gdim,
|
||||
0, 0,
|
||||
0, 0,
|
||||
$x0, $y0,
|
||||
$desiredSize['width'], $desiredSize['height']
|
||||
);
|
||||
$copiedImages[$key] = $desired_gdim;
|
||||
|
||||
*/
|
||||
}
|
||||
return $copiedImages;
|
||||
}
|
||||
|
||||
public function saveOriginal($folder, $image, $name, $type)
|
||||
{
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
}
|
||||
|
||||
$path = $folder . $name . '.' . $type;
|
||||
|
||||
file_put_contents($path, $image);
|
||||
}
|
||||
|
||||
public function saveImage($folder, $image, $name, $type)
|
||||
{
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
}
|
||||
|
||||
if($type == "png")
|
||||
{
|
||||
$result = imagepng( $image, $folder . '/' . $name . '.png' );
|
||||
}
|
||||
elseif($type == "gif")
|
||||
{
|
||||
$result = imagegif( $image, $folder . '/' . $name . '.gif' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = imagejpeg( $image, $folder . '/' . $name . '.jpeg' );
|
||||
$type = 'jpeg';
|
||||
}
|
||||
|
||||
imagedestroy($image);
|
||||
|
||||
if($result)
|
||||
{
|
||||
return $name . '.' . $type;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function clearTempFolder()
|
||||
{
|
||||
$folder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
$files = scandir($folder);
|
||||
$result = true;
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if (!in_array($file, array(".","..")))
|
||||
{
|
||||
$filelink = $folder . $file;
|
||||
if(!unlink($filelink))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function deleteImage($name)
|
||||
{
|
||||
$baseFolder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR;
|
||||
$original = $baseFolder . 'original' . DIRECTORY_SEPARATOR . $name . '*';
|
||||
$live = $baseFolder . 'live' . DIRECTORY_SEPARATOR . $name . '*';
|
||||
$success = true;
|
||||
|
||||
foreach(glob($original) as $image)
|
||||
{
|
||||
if(!unlink($image))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(glob($live) as $image)
|
||||
{
|
||||
if(!unlink($image))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
<?php
|
||||
namespace Typemill\Models;
|
||||
|
||||
class ProcessImage
|
||||
{
|
||||
public function createImage(string $image, array $desiredSizes)
|
||||
{
|
||||
# fix error from jpeg-library
|
||||
ini_set ('gd.jpeg_ignore_warning', 1);
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
|
||||
# clear temporary folder
|
||||
$this->clearTempFolder();
|
||||
|
||||
# decode the image from base64-string
|
||||
$imageDecoded = $this->decodeImage($image);
|
||||
$imageData = $imageDecoded["image"];
|
||||
$imageType = $imageDecoded["type"];
|
||||
|
||||
# transform image-stream into image
|
||||
$image = imagecreatefromstring($imageData);
|
||||
|
||||
# get the size of the original image
|
||||
$imageSize = $this->getImageSize($image);
|
||||
|
||||
# check the desired sizes and calculate the height, if not set
|
||||
$desiredSizes = $this->setHeight($imageSize, $desiredSizes);
|
||||
|
||||
# resize the images
|
||||
$resizedImages = $this->imageResize($image, $imageSize, $desiredSizes, $imageType);
|
||||
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR . 'media';
|
||||
$tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
|
||||
$this->saveOriginal($tmpFolder, $imageData, 'original', $imageType);
|
||||
|
||||
if($imageType == "gif" && $this->detectAnimatedGif($imageData))
|
||||
{
|
||||
$this->saveOriginal($tmpFolder, $imageData, 'live', $imageType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
# temporary store resized images
|
||||
foreach($resizedImages as $key => $resizedImage)
|
||||
{
|
||||
$this->saveImage($tmpFolder, $resizedImage, $key, $imageType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function detectAnimatedGif($image_file_contents)
|
||||
{
|
||||
$is_animated = preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', $image_file_contents);
|
||||
if ($is_animated == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function publishImage(array $desiredSizes, $name = false)
|
||||
{
|
||||
/* get images from tmp folder */
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR . 'media';
|
||||
$tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
$originalFolder = $basePath . DIRECTORY_SEPARATOR . 'original' . DIRECTORY_SEPARATOR;
|
||||
$liveFolder = $basePath . DIRECTORY_SEPARATOR . 'live' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if(!file_exists($originalFolder)){ mkdir($originalFolder, 0774, true); }
|
||||
if(!file_exists($liveFolder)){ mkdir($liveFolder, 0774, true); }
|
||||
|
||||
$name = $name ? $name : uniqid();
|
||||
|
||||
$files = scandir($tmpFolder);
|
||||
$success = true;
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if (!in_array($file, array(".","..")))
|
||||
{
|
||||
$tmpfilename = explode(".", $file);
|
||||
|
||||
if($tmpfilename[0] == 'original')
|
||||
{
|
||||
$success = rename($tmpFolder . $file, $originalFolder . $name . '-' . $file);
|
||||
}
|
||||
else
|
||||
{
|
||||
$success = rename($tmpFolder . $file, $liveFolder . $name . '-' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($success)
|
||||
{
|
||||
return 'media/live/' . $name . '-live.' . $tmpfilename[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function decodeImage(string $image)
|
||||
{
|
||||
$imageParts = explode(";base64,", $image);
|
||||
$imageType = explode("/", $imageParts[0]);
|
||||
$imageData = base64_decode($imageParts[1]);
|
||||
|
||||
if ($imageData !== false)
|
||||
{
|
||||
return array("image" => $imageData, "type" => $imageType[1]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getImageSize($image)
|
||||
{
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
return array('width' => $width, 'height' => $height);
|
||||
}
|
||||
|
||||
public function setHeight(array $imageSize, array $desiredSizes)
|
||||
{
|
||||
foreach($desiredSizes as $key => $desiredSize)
|
||||
{
|
||||
if($desiredSize['width'] > $imageSize['width'])
|
||||
{
|
||||
$desiredSizes[$key] = $imageSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($desiredSize['height']))
|
||||
{
|
||||
$resizeFactor = $imageSize['width'] / $desiredSize['width'];
|
||||
$desiredSizes[$key]['height'] = round( ($imageSize['height'] / $resizeFactor), 0);
|
||||
}
|
||||
}
|
||||
return $desiredSizes;
|
||||
}
|
||||
|
||||
public function imageResize($imageData, array $imageSize, array $desiredSizes, $imageType)
|
||||
{
|
||||
$copiedImages = array();
|
||||
$source_aspect_ratio = $imageSize['width'] / $imageSize['height'];
|
||||
|
||||
foreach($desiredSizes as $key => $desiredSize)
|
||||
{
|
||||
$desired_aspect_ratio = $desiredSize['width'] / $desiredSize['height'];
|
||||
|
||||
if ( $source_aspect_ratio > $desired_aspect_ratio )
|
||||
{
|
||||
# when source image is wider
|
||||
$temp_height = $desiredSize['height'];
|
||||
$temp_width = ( int ) ($desiredSize['height'] * $source_aspect_ratio);
|
||||
$temp_width = round($temp_width, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
# when source image is similar or taller
|
||||
$temp_width = $desiredSize['width'];
|
||||
$temp_height = ( int ) ($desiredSize['width'] / $source_aspect_ratio);
|
||||
$temp_height = round($temp_height, 0);
|
||||
}
|
||||
|
||||
# Create a temporary GD image with desired size
|
||||
$temp_gdim = imagecreatetruecolor( $temp_width, $temp_height );
|
||||
|
||||
if ($imageType == "gif")
|
||||
{
|
||||
$transparent_index = imagecolortransparent($imageData);
|
||||
imagepalettecopy($imageData, $temp_gdim);
|
||||
imagefill($temp_gdim, 0, 0, $transparent_index);
|
||||
imagecolortransparent($temp_gdim, $transparent_index);
|
||||
imagetruecolortopalette($temp_gdim, true, 256);
|
||||
}
|
||||
elseif($imageType == "png")
|
||||
{
|
||||
imagealphablending($temp_gdim, false);
|
||||
imagesavealpha($temp_gdim, true);
|
||||
$transparent = imagecolorallocatealpha($temp_gdim, 255, 255, 255, 127);
|
||||
imagefilledrectangle($temp_gdim, 0, 0, $temp_width, $temp_height, $transparent);
|
||||
}
|
||||
|
||||
# resize image
|
||||
imagecopyresampled(
|
||||
$temp_gdim,
|
||||
$imageData,
|
||||
0, 0,
|
||||
0, 0,
|
||||
$temp_width, $temp_height,
|
||||
$imageSize['width'], $imageSize['height']
|
||||
);
|
||||
|
||||
$copiedImages[$key] = $temp_gdim;
|
||||
|
||||
/*
|
||||
|
||||
# Copy cropped region from temporary image into the desired GD image
|
||||
$x0 = ( $temp_width - $desiredSize['width'] ) / 2;
|
||||
$y0 = ( $temp_height - $desiredSize['height'] ) / 2;
|
||||
|
||||
$desired_gdim = imagecreatetruecolor( $desiredSize['width'], $desiredSize['height'] );
|
||||
|
||||
if ($imageType == "gif")
|
||||
{
|
||||
imagepalettecopy($temp_gdim, $desired_gdim);
|
||||
imagefill($desired_gdim, 0, 0, $transparent_index);
|
||||
imagecolortransparent($desired_gdim, $transparent_index);
|
||||
imagetruecolortopalette($desired_gdim, true, 256);
|
||||
}
|
||||
elseif($imageType == "png")
|
||||
{
|
||||
imagealphablending($desired_gdim, false);
|
||||
imagesavealpha($desired_gdim,true);
|
||||
$transparent = imagecolorallocatealpha($desired_gdim, 255, 255, 255, 127);
|
||||
imagefilledrectangle($desired_gdim, 0, 0, $desired_size['with'], $desired_size['height'], $transparent);
|
||||
}
|
||||
|
||||
imagecopyresampled(
|
||||
$desired_gdim,
|
||||
$temp_gdim,
|
||||
0, 0,
|
||||
0, 0,
|
||||
$x0, $y0,
|
||||
$desiredSize['width'], $desiredSize['height']
|
||||
);
|
||||
$copiedImages[$key] = $desired_gdim;
|
||||
|
||||
*/
|
||||
}
|
||||
return $copiedImages;
|
||||
}
|
||||
|
||||
public function saveOriginal($folder, $image, $name, $type)
|
||||
{
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
}
|
||||
|
||||
$path = $folder . $name . '.' . $type;
|
||||
|
||||
file_put_contents($path, $image);
|
||||
}
|
||||
|
||||
public function saveImage($folder, $image, $name, $type)
|
||||
{
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
}
|
||||
|
||||
if($type == "png")
|
||||
{
|
||||
$result = imagepng( $image, $folder . '/' . $name . '.png' );
|
||||
}
|
||||
elseif($type == "gif")
|
||||
{
|
||||
$result = imagegif( $image, $folder . '/' . $name . '.gif' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = imagejpeg( $image, $folder . '/' . $name . '.jpeg' );
|
||||
$type = 'jpeg';
|
||||
}
|
||||
|
||||
imagedestroy($image);
|
||||
|
||||
if($result)
|
||||
{
|
||||
return $name . '.' . $type;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function clearTempFolder()
|
||||
{
|
||||
$folder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if(!file_exists($folder))
|
||||
{
|
||||
mkdir($folder, 0774, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
$files = scandir($folder);
|
||||
$result = true;
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if (!in_array($file, array(".","..")))
|
||||
{
|
||||
$filelink = $folder . $file;
|
||||
if(!unlink($filelink))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function deleteImage($name)
|
||||
{
|
||||
$baseFolder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR;
|
||||
$original = $baseFolder . 'original' . DIRECTORY_SEPARATOR . $name . '*';
|
||||
$live = $baseFolder . 'live' . DIRECTORY_SEPARATOR . $name . '*';
|
||||
$success = true;
|
||||
|
||||
foreach(glob($original) as $image)
|
||||
{
|
||||
if(!unlink($image))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(glob($live) as $image)
|
||||
{
|
||||
if(!unlink($image))
|
||||
{
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
@ -1,98 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class User extends WriteYaml
|
||||
{
|
||||
public function getUsers()
|
||||
{
|
||||
$userDir = __DIR__ . '/../../settings/users';
|
||||
|
||||
/* check if plugins directory exists */
|
||||
if(!is_dir($userDir)){ return array(); }
|
||||
|
||||
/* get all plugins folder */
|
||||
$users = array_diff(scandir($userDir), array('..', '.'));
|
||||
|
||||
$cleanUser = array();
|
||||
foreach($users as $key => $user)
|
||||
{
|
||||
if($user == '.logins'){ continue; }
|
||||
$cleanUser[] = str_replace('.yaml', '', $user);
|
||||
}
|
||||
|
||||
return $cleanUser;
|
||||
}
|
||||
|
||||
public function getUser($username)
|
||||
{
|
||||
$user = $this->getYaml('settings/users', $username . '.yaml');
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function createUser($params)
|
||||
{
|
||||
$userdata = array(
|
||||
'username' => $params['username'],
|
||||
'email' => $params['email'],
|
||||
'password' => $this->generatePassword($params['password']),
|
||||
'userrole' => $params['userrole']
|
||||
);
|
||||
|
||||
if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata))
|
||||
{
|
||||
return $userdata['username'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateUser($params)
|
||||
{
|
||||
$userdata = $this->getUser($params['username']);
|
||||
|
||||
if(isset($params['password']))
|
||||
{
|
||||
$params['password'] = $this->generatePassword($params['password']);
|
||||
}
|
||||
|
||||
$update = array_merge($userdata, $params);
|
||||
|
||||
$this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update);
|
||||
|
||||
return $userdata['username'];
|
||||
}
|
||||
|
||||
public function deleteUser($username)
|
||||
{
|
||||
if($this->getUser($username))
|
||||
{
|
||||
unlink('settings/users/' . $username . '.yaml');
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserroles()
|
||||
{
|
||||
return array('administrator', 'editor');
|
||||
}
|
||||
|
||||
public function login($username)
|
||||
{
|
||||
$user = $this->getUser($username);
|
||||
|
||||
if($user)
|
||||
{
|
||||
$user['lastlogin'] = time();
|
||||
unset($user['password']);
|
||||
$this->updateUser($user);
|
||||
|
||||
$_SESSION['user'] = $user['username'];
|
||||
$_SESSION['role'] = $user['userrole'];
|
||||
$_SESSION['login'] = $user['lastlogin'];
|
||||
}
|
||||
}
|
||||
|
||||
public function generatePassword($password)
|
||||
{
|
||||
return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class User extends WriteYaml
|
||||
{
|
||||
public function getUsers()
|
||||
{
|
||||
$userDir = __DIR__ . '/../../settings/users';
|
||||
|
||||
/* check if plugins directory exists */
|
||||
if(!is_dir($userDir)){ return array(); }
|
||||
|
||||
/* get all plugins folder */
|
||||
$users = array_diff(scandir($userDir), array('..', '.'));
|
||||
|
||||
$cleanUser = array();
|
||||
foreach($users as $key => $user)
|
||||
{
|
||||
if($user == '.logins'){ continue; }
|
||||
$cleanUser[] = str_replace('.yaml', '', $user);
|
||||
}
|
||||
|
||||
return $cleanUser;
|
||||
}
|
||||
|
||||
public function getUser($username)
|
||||
{
|
||||
$user = $this->getYaml('settings/users', $username . '.yaml');
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function createUser($params)
|
||||
{
|
||||
$userdata = array(
|
||||
'username' => $params['username'],
|
||||
'email' => $params['email'],
|
||||
'password' => $this->generatePassword($params['password']),
|
||||
'userrole' => $params['userrole']
|
||||
);
|
||||
|
||||
if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata))
|
||||
{
|
||||
return $userdata['username'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateUser($params)
|
||||
{
|
||||
$userdata = $this->getUser($params['username']);
|
||||
|
||||
if(isset($params['password']))
|
||||
{
|
||||
$params['password'] = $this->generatePassword($params['password']);
|
||||
}
|
||||
|
||||
$update = array_merge($userdata, $params);
|
||||
|
||||
$this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update);
|
||||
|
||||
return $userdata['username'];
|
||||
}
|
||||
|
||||
public function deleteUser($username)
|
||||
{
|
||||
if($this->getUser($username))
|
||||
{
|
||||
unlink('settings/users/' . $username . '.yaml');
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserroles()
|
||||
{
|
||||
return array('administrator', 'editor');
|
||||
}
|
||||
|
||||
public function login($username)
|
||||
{
|
||||
$user = $this->getUser($username);
|
||||
|
||||
if($user)
|
||||
{
|
||||
$user['lastlogin'] = time();
|
||||
unset($user['password']);
|
||||
$this->updateUser($user);
|
||||
|
||||
$_SESSION['user'] = $user['username'];
|
||||
$_SESSION['role'] = $user['userrole'];
|
||||
$_SESSION['login'] = $user['lastlogin'];
|
||||
}
|
||||
}
|
||||
|
||||
public function generatePassword($password)
|
||||
{
|
||||
return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]);
|
||||
}
|
||||
}
|
@ -1,418 +1,418 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use Typemill\Models\User;
|
||||
use Valitron\Validator;
|
||||
|
||||
class Validation
|
||||
{
|
||||
/**
|
||||
* Constructor with custom validation rules
|
||||
*
|
||||
* @param obj $db the database connection.
|
||||
*/
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$user = new User();
|
||||
|
||||
Validator::langDir(__DIR__.'/../vendor/vlucas/valitron/lang'); // always set langDir before lang.
|
||||
Validator::lang('en');
|
||||
|
||||
Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($value);
|
||||
if($userdata){ return false; }
|
||||
return true;
|
||||
}, 'taken');
|
||||
|
||||
Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($value);
|
||||
if($userdata){ return true; }
|
||||
return false;
|
||||
}, 'does not exist');
|
||||
|
||||
Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($fields['username']);
|
||||
if($userdata && password_verify($value, $userdata['password'])){ return true; }
|
||||
return false;
|
||||
}, 'wrong password');
|
||||
|
||||
Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$email = 'testmail@gmail.com';
|
||||
if($email){ return false; }
|
||||
return true;
|
||||
}, 'taken');
|
||||
|
||||
Validator::addRule('emailKnown', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$email = 'testmail@gmail.com';
|
||||
if(!$email){ return false; }
|
||||
return true;
|
||||
}, 'unknown');
|
||||
|
||||
Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/';
|
||||
if ( preg_match($format, $value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, 'contains special characters');
|
||||
|
||||
Validator::addRule('noHTML', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
if ( $value == strip_tags($value) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, 'contains html');
|
||||
|
||||
Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
/* strip out code blocks and blockquotes */
|
||||
$value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value);
|
||||
$value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value);
|
||||
$value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value);
|
||||
$value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value);
|
||||
$value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value);
|
||||
|
||||
if ( $value == strip_tags($value) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.');
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for signup form
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function signin(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'password'])->message("Required");
|
||||
$v->rule('alphaNum', 'username')->message("Invalid characters");
|
||||
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for signup form
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function newUser(array $params, $userroles)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'email', 'password'])->message("required");
|
||||
$v->rule('alphaNum', 'username')->message("invalid characters");
|
||||
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
$v->rule('userAvailable', 'username')->message("User already exists");
|
||||
$v->rule('email', 'email')->message("e-mail is invalid");
|
||||
$v->rule('in', 'userrole', $userroles);
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
public function existingUser(array $params, $userroles)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'email', 'userrole'])->message("required");
|
||||
$v->rule('alphaNum', 'username')->message("invalid");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
$v->rule('userExists', 'username')->message("user does not exist");
|
||||
$v->rule('email', 'email')->message("e-mail is invalid");
|
||||
$v->rule('in', 'userrole', $userroles);
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
public function username($username)
|
||||
{
|
||||
$v = new Validator($username);
|
||||
$v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for changing the password
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function newPassword(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['password', 'newpassword']);
|
||||
$v->rule('lengthBetween', 'newpassword', 5, 20);
|
||||
$v->rule('checkPassword', 'password')->message("Password is wrong");
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for system settings
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function settings(array $params, array $copyright, $name = false)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']);
|
||||
$v->rule('lengthBetween', 'title', 2, 20);
|
||||
$v->rule('lengthBetween', 'author', 2, 40);
|
||||
$v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
|
||||
$v->rule('regex', 'author', '/^[\pL_ \-]*$/u');
|
||||
$v->rule('integer', 'year');
|
||||
$v->rule('length', 'year', 4);
|
||||
$v->rule('in', 'editor', ['raw', 'visual']);
|
||||
$v->rule('in', 'copyright', $copyright);
|
||||
|
||||
return $this->validationResult($v, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for content editor
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function editorInput(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['title', 'content', 'url']);
|
||||
$v->rule('lengthBetween', 'title', 2, 100);
|
||||
$v->rule('noHTML', 'title');
|
||||
$v->rule('markdownSecure', 'content');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
public function blockInput(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['markdown', 'block_id', 'url']);
|
||||
$v->rule('markdownSecure', 'markdown');
|
||||
$v->rule('regex', 'block_id', '/^[0-9.]+$/i');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for resort navigation
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationSort(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
|
||||
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('integer', 'index_new');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for new navigation items
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationItem(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
|
||||
$v->rule('regex', 'folder_id', '/^[0-9.]+$/i');
|
||||
$v->rule('noSpecialChars', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 40);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for dynamic fields ( settings for themes and plugins)
|
||||
*
|
||||
* @param string $fieldName with the name of the field.
|
||||
* @param array or string $fieldValue with the values of the field.
|
||||
* @param string $objectName with the name of the plugin or theme.
|
||||
* @param array $fieldDefinitions with the field definitions as multi-dimensional array.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions)
|
||||
{
|
||||
$v = new Validator(array($fieldName => $fieldValue));
|
||||
|
||||
if(isset($fieldDefinitions['required']))
|
||||
{
|
||||
$v->rule('required', $fieldName);
|
||||
}
|
||||
|
||||
switch($fieldDefinitions['type'])
|
||||
{
|
||||
case "select":
|
||||
/* create array with option keys as value */
|
||||
$options = array();
|
||||
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
|
||||
$v->rule('in', $fieldName, $options);
|
||||
break;
|
||||
case "radio":
|
||||
$v->rule('in', $fieldName, $fieldDefinitions['options']);
|
||||
break;
|
||||
case "checkboxlist":
|
||||
/* create array with option keys as value */
|
||||
$options = array();
|
||||
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
|
||||
/* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */
|
||||
foreach($fieldValue as $key => $value)
|
||||
{
|
||||
$v->rule('in', $key, $options);
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
$v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/');
|
||||
break;
|
||||
case "email":
|
||||
$v->rule('email', $fieldName);
|
||||
break;
|
||||
case "date":
|
||||
$v->rule('date', $fieldName);
|
||||
break;
|
||||
case "checkbox":
|
||||
$v->rule('accepted', $fieldName);
|
||||
break;
|
||||
case "url":
|
||||
$v->rule('lengthMax', $fieldName, 200);
|
||||
$v->rule('url', $fieldName);
|
||||
break;
|
||||
case "text":
|
||||
$v->rule('lengthMax', $fieldName, 200);
|
||||
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
|
||||
break;
|
||||
case "textarea":
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('noHTML', $fieldName);
|
||||
// $v->rule('regex', $fieldName, '/<[^<]+>/');
|
||||
break;
|
||||
case "paragraph":
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('noHTML', $fieldName);
|
||||
break;
|
||||
case "password":
|
||||
$v->rule('lengthMax', $fieldName, 100);
|
||||
break;
|
||||
default:
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');
|
||||
}
|
||||
|
||||
return $this->validationResult($v, $objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* result for validation
|
||||
*
|
||||
* @param obj $v the validation object.
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function validationResult($v, $name = false)
|
||||
{
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if($name)
|
||||
{
|
||||
if(isset($_SESSION['errors'][$name]))
|
||||
{
|
||||
foreach ($v->errors() as $key => $val)
|
||||
{
|
||||
$_SESSION['errors'][$name][$key] = $val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$_SESSION['errors'][$name] = $v->errors();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$_SESSION['errors'] = $v->errors();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
use Typemill\Models\User;
|
||||
use Valitron\Validator;
|
||||
|
||||
class Validation
|
||||
{
|
||||
/**
|
||||
* Constructor with custom validation rules
|
||||
*
|
||||
* @param obj $db the database connection.
|
||||
*/
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$user = new User();
|
||||
|
||||
Validator::langDir(__DIR__.'/../vendor/vlucas/valitron/lang'); // always set langDir before lang.
|
||||
Validator::lang('en');
|
||||
|
||||
Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($value);
|
||||
if($userdata){ return false; }
|
||||
return true;
|
||||
}, 'taken');
|
||||
|
||||
Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($value);
|
||||
if($userdata){ return true; }
|
||||
return false;
|
||||
}, 'does not exist');
|
||||
|
||||
Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user)
|
||||
{
|
||||
$userdata = $user->getUser($fields['username']);
|
||||
if($userdata && password_verify($value, $userdata['password'])){ return true; }
|
||||
return false;
|
||||
}, 'wrong password');
|
||||
|
||||
Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$email = 'testmail@gmail.com';
|
||||
if($email){ return false; }
|
||||
return true;
|
||||
}, 'taken');
|
||||
|
||||
Validator::addRule('emailKnown', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$email = 'testmail@gmail.com';
|
||||
if(!$email){ return false; }
|
||||
return true;
|
||||
}, 'unknown');
|
||||
|
||||
Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/';
|
||||
if ( preg_match($format, $value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, 'contains special characters');
|
||||
|
||||
Validator::addRule('noHTML', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
if ( $value == strip_tags($value) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, 'contains html');
|
||||
|
||||
Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
/* strip out code blocks and blockquotes */
|
||||
$value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value);
|
||||
$value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value);
|
||||
$value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value);
|
||||
$value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value);
|
||||
$value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value);
|
||||
|
||||
if ( $value == strip_tags($value) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.');
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for signup form
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function signin(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'password'])->message("Required");
|
||||
$v->rule('alphaNum', 'username')->message("Invalid characters");
|
||||
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for signup form
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function newUser(array $params, $userroles)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'email', 'password'])->message("required");
|
||||
$v->rule('alphaNum', 'username')->message("invalid characters");
|
||||
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
$v->rule('userAvailable', 'username')->message("User already exists");
|
||||
$v->rule('email', 'email')->message("e-mail is invalid");
|
||||
$v->rule('in', 'userrole', $userroles);
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
public function existingUser(array $params, $userroles)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['username', 'email', 'userrole'])->message("required");
|
||||
$v->rule('alphaNum', 'username')->message("invalid");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
$v->rule('userExists', 'username')->message("user does not exist");
|
||||
$v->rule('email', 'email')->message("e-mail is invalid");
|
||||
$v->rule('in', 'userrole', $userroles);
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
public function username($username)
|
||||
{
|
||||
$v = new Validator($username);
|
||||
$v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed");
|
||||
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for changing the password
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function newPassword(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
$v->rule('required', ['password', 'newpassword']);
|
||||
$v->rule('lengthBetween', 'newpassword', 5, 20);
|
||||
$v->rule('checkPassword', 'password')->message("Password is wrong");
|
||||
|
||||
return $this->validationResult($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for system settings
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function settings(array $params, array $copyright, $name = false)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']);
|
||||
$v->rule('lengthBetween', 'title', 2, 20);
|
||||
$v->rule('lengthBetween', 'author', 2, 40);
|
||||
$v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
|
||||
$v->rule('regex', 'author', '/^[\pL_ \-]*$/u');
|
||||
$v->rule('integer', 'year');
|
||||
$v->rule('length', 'year', 4);
|
||||
$v->rule('in', 'editor', ['raw', 'visual']);
|
||||
$v->rule('in', 'copyright', $copyright);
|
||||
|
||||
return $this->validationResult($v, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for content editor
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function editorInput(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['title', 'content', 'url']);
|
||||
$v->rule('lengthBetween', 'title', 2, 100);
|
||||
$v->rule('noHTML', 'title');
|
||||
$v->rule('markdownSecure', 'content');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
public function blockInput(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['markdown', 'block_id', 'url']);
|
||||
$v->rule('markdownSecure', 'markdown');
|
||||
$v->rule('regex', 'block_id', '/^[0-9.]+$/i');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for resort navigation
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationSort(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
|
||||
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('integer', 'index_new');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for new navigation items
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationItem(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
|
||||
$v->rule('regex', 'folder_id', '/^[0-9.]+$/i');
|
||||
$v->rule('noSpecialChars', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 40);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for dynamic fields ( settings for themes and plugins)
|
||||
*
|
||||
* @param string $fieldName with the name of the field.
|
||||
* @param array or string $fieldValue with the values of the field.
|
||||
* @param string $objectName with the name of the plugin or theme.
|
||||
* @param array $fieldDefinitions with the field definitions as multi-dimensional array.
|
||||
* @return obj $v the validation object passed to a result method.
|
||||
*/
|
||||
|
||||
public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions)
|
||||
{
|
||||
$v = new Validator(array($fieldName => $fieldValue));
|
||||
|
||||
if(isset($fieldDefinitions['required']))
|
||||
{
|
||||
$v->rule('required', $fieldName);
|
||||
}
|
||||
|
||||
switch($fieldDefinitions['type'])
|
||||
{
|
||||
case "select":
|
||||
/* create array with option keys as value */
|
||||
$options = array();
|
||||
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
|
||||
$v->rule('in', $fieldName, $options);
|
||||
break;
|
||||
case "radio":
|
||||
$v->rule('in', $fieldName, $fieldDefinitions['options']);
|
||||
break;
|
||||
case "checkboxlist":
|
||||
/* create array with option keys as value */
|
||||
$options = array();
|
||||
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
|
||||
/* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */
|
||||
foreach($fieldValue as $key => $value)
|
||||
{
|
||||
$v->rule('in', $key, $options);
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
$v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/');
|
||||
break;
|
||||
case "email":
|
||||
$v->rule('email', $fieldName);
|
||||
break;
|
||||
case "date":
|
||||
$v->rule('date', $fieldName);
|
||||
break;
|
||||
case "checkbox":
|
||||
$v->rule('accepted', $fieldName);
|
||||
break;
|
||||
case "url":
|
||||
$v->rule('lengthMax', $fieldName, 200);
|
||||
$v->rule('url', $fieldName);
|
||||
break;
|
||||
case "text":
|
||||
$v->rule('lengthMax', $fieldName, 200);
|
||||
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
|
||||
break;
|
||||
case "textarea":
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('noHTML', $fieldName);
|
||||
// $v->rule('regex', $fieldName, '/<[^<]+>/');
|
||||
break;
|
||||
case "paragraph":
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('noHTML', $fieldName);
|
||||
break;
|
||||
case "password":
|
||||
$v->rule('lengthMax', $fieldName, 100);
|
||||
break;
|
||||
default:
|
||||
$v->rule('lengthMax', $fieldName, 1000);
|
||||
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');
|
||||
}
|
||||
|
||||
return $this->validationResult($v, $objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* result for validation
|
||||
*
|
||||
* @param obj $v the validation object.
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function validationResult($v, $name = false)
|
||||
{
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if($name)
|
||||
{
|
||||
if(isset($_SESSION['errors'][$name]))
|
||||
{
|
||||
foreach ($v->errors() as $key => $val)
|
||||
{
|
||||
$_SESSION['errors'][$name][$key] = $val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$_SESSION['errors'][$name] = $v->errors();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$_SESSION['errors'] = $v->errors();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class VersionCheck
|
||||
{
|
||||
function checkVersion($url)
|
||||
{
|
||||
$opts = array(
|
||||
'http'=>array(
|
||||
'method' => "GET",
|
||||
'header' => "Referer: $url\r\n"
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
if(false === ($version = @file_get_contents('http://typemill.net/api/v1/checkversion', false, $context)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$version = json_decode($version);
|
||||
return $version->system->typemill;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class VersionCheck
|
||||
{
|
||||
function checkVersion($url)
|
||||
{
|
||||
$opts = array(
|
||||
'http'=>array(
|
||||
'method' => "GET",
|
||||
'header' => "Referer: $url\r\n"
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
if(false === ($version = @file_get_contents('http://typemill.net/api/v1/checkversion', false, $context)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$version = json_decode($version);
|
||||
return $version->system->typemill;
|
||||
}
|
||||
}
|
@ -1,131 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Write
|
||||
{
|
||||
protected $basePath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR;
|
||||
$this->basePath = $basePath;
|
||||
}
|
||||
|
||||
public function checkPath($folder)
|
||||
{
|
||||
$folderPath = $this->basePath . $folder;
|
||||
|
||||
if(!is_dir($folderPath))
|
||||
{
|
||||
if(@mkdir($folderPath, 0774, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
# throw new \Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(@is_writable($folderPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
# throw new \Exception("Please make the folder '{$folder}' writable.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkFile($folder, $file)
|
||||
{
|
||||
if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function writeFile($folder, $file, $data)
|
||||
{
|
||||
if($this->checkPath($folder))
|
||||
{
|
||||
$filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
|
||||
|
||||
$openFile = @fopen($filePath, "w");
|
||||
|
||||
if(!$openFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fwrite($openFile, $data);
|
||||
fclose($openFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFile($folderName, $fileName)
|
||||
{
|
||||
if($this->checkFile($folderName, $fileName))
|
||||
{
|
||||
$fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
|
||||
return $fileContent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveElement($item, $folderPath, $index)
|
||||
{
|
||||
$filetypes = array('md', 'txt');
|
||||
|
||||
# set new order as string
|
||||
$newOrder = ($index < 10) ? '0' . $index : $index;
|
||||
|
||||
# create new path with foldername or filename but without file-type
|
||||
$newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$oldPath = $this->basePath . 'content' . $item->path;
|
||||
if(@rename($oldPath, $newPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# create old path but without filetype
|
||||
$oldPath = substr($item->path, 0, strpos($item->path, "."));
|
||||
$oldPath = $this->basePath . 'content' . $oldPath;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldPath . '.' . $filetype;
|
||||
$newFilePath = $newPath . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class Write
|
||||
{
|
||||
protected $basePath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$basePath = getcwd() . DIRECTORY_SEPARATOR;
|
||||
$this->basePath = $basePath;
|
||||
}
|
||||
|
||||
public function checkPath($folder)
|
||||
{
|
||||
$folderPath = $this->basePath . $folder;
|
||||
|
||||
if(!is_dir($folderPath))
|
||||
{
|
||||
if(@mkdir($folderPath, 0774, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
# throw new \Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(@is_writable($folderPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
# throw new \Exception("Please make the folder '{$folder}' writable.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkFile($folder, $file)
|
||||
{
|
||||
if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkFileWithPath($filepath)
|
||||
{
|
||||
if(!file_exists($this->basePath . $filepath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function writeFile($folder, $file, $data)
|
||||
{
|
||||
if($this->checkPath($folder))
|
||||
{
|
||||
$filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
|
||||
|
||||
$openFile = @fopen($filePath, "w");
|
||||
|
||||
if(!$openFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fwrite($openFile, $data);
|
||||
fclose($openFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFile($folderName, $fileName)
|
||||
{
|
||||
if($this->checkFile($folderName, $fileName))
|
||||
{
|
||||
$fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
|
||||
return $fileContent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFileWithPath($filepath)
|
||||
{
|
||||
if($this->checkFileWithPath($filepath))
|
||||
{
|
||||
$fileContent = file_get_contents($filepath);
|
||||
return $fileContent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deleteFileWithPath($filepath)
|
||||
{
|
||||
if($this->checkFileWithPath($filepath))
|
||||
{
|
||||
unlink($this->basePath . $filepath);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveElement($item, $folderPath, $index)
|
||||
{
|
||||
$filetypes = array('md', 'txt');
|
||||
|
||||
# set new order as string
|
||||
$newOrder = ($index < 10) ? '0' . $index : $index;
|
||||
|
||||
# create new path with foldername or filename but without file-type
|
||||
$newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$oldPath = $this->basePath . 'content' . $item->path;
|
||||
if(@rename($oldPath, $newPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# create old path but without filetype
|
||||
$oldPath = substr($item->path, 0, strpos($item->path, "."));
|
||||
$oldPath = $this->basePath . 'content' . $oldPath;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldPath . '.' . $filetype;
|
||||
$newFilePath = $newPath . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -1,84 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteCache extends Write
|
||||
{
|
||||
/**
|
||||
* Validates, if the cache is valid or invalid and has to be refreshed
|
||||
* @param int $duration how many seconds the cache is valid.
|
||||
* @return boolean for an invalid cache (false) and for a valid cache (true).
|
||||
*/
|
||||
public function validate($folderName, $fileName, $duration)
|
||||
{
|
||||
if(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->checkPath($folderName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->checkFile($folderName, $fileName))
|
||||
{
|
||||
$this->writeFile($folderName, $fileName, time());
|
||||
return false;
|
||||
}
|
||||
|
||||
$lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
|
||||
|
||||
if(time() - $lastRefresh > $duration)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a cache file.
|
||||
* Serializes an object and writes it to the cache file together with a file that holds the last refresh time.
|
||||
* @param object $cacheData has to be an object (e.g. navigation object).
|
||||
* @param string $cacheFile has to be the name of the file you want to update (in case there are more than one cache files.
|
||||
*/
|
||||
public function updateCache($folderName, $cacheFileName, $requestFileName, $cacheData)
|
||||
{
|
||||
$sCacheData = serialize($cacheData);
|
||||
$this->writeFile($folderName, $cacheFileName, $sCacheData);
|
||||
if($requestFileName)
|
||||
{
|
||||
$this->writeFile($folderName, $requestFileName, time());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recent cache.
|
||||
* Takes a filename, gets the file and unserializes the cache into an object.
|
||||
* @param string $fileName is the name of the cache file.
|
||||
*/
|
||||
public function getCache($folderName, $cacheFileName)
|
||||
{
|
||||
$sCacheData = $this->getFile($folderName, $cacheFileName);
|
||||
if($sCacheData)
|
||||
{
|
||||
return unserialize($sCacheData);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Create a function to clear a specific cache file
|
||||
*/
|
||||
public function clearCache($name)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Create a function to clear all cache files
|
||||
*/
|
||||
public function clearAllCacheFiles()
|
||||
{
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteCache extends Write
|
||||
{
|
||||
/**
|
||||
* Validates, if the cache is valid or invalid and has to be refreshed
|
||||
* @param int $duration how many seconds the cache is valid.
|
||||
* @return boolean for an invalid cache (false) and for a valid cache (true).
|
||||
*/
|
||||
public function validate($folderName, $fileName, $duration)
|
||||
{
|
||||
if(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->checkPath($folderName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->checkFile($folderName, $fileName))
|
||||
{
|
||||
$this->writeFile($folderName, $fileName, time());
|
||||
return false;
|
||||
}
|
||||
|
||||
$lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
|
||||
|
||||
if(time() - $lastRefresh > $duration)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a cache file.
|
||||
* Serializes an object and writes it to the cache file together with a file that holds the last refresh time.
|
||||
* @param object $cacheData has to be an object (e.g. navigation object).
|
||||
* @param string $cacheFile has to be the name of the file you want to update (in case there are more than one cache files.
|
||||
*/
|
||||
public function updateCache($folderName, $cacheFileName, $requestFileName, $cacheData)
|
||||
{
|
||||
$sCacheData = serialize($cacheData);
|
||||
$this->writeFile($folderName, $cacheFileName, $sCacheData);
|
||||
if($requestFileName)
|
||||
{
|
||||
$this->writeFile($folderName, $requestFileName, time());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recent cache.
|
||||
* Takes a filename, gets the file and unserializes the cache into an object.
|
||||
* @param string $fileName is the name of the cache file.
|
||||
*/
|
||||
public function getCache($folderName, $cacheFileName)
|
||||
{
|
||||
$sCacheData = $this->getFile($folderName, $cacheFileName);
|
||||
if($sCacheData)
|
||||
{
|
||||
return unserialize($sCacheData);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Create a function to clear a specific cache file
|
||||
*/
|
||||
public function clearCache($name)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Create a function to clear all cache files
|
||||
*/
|
||||
public function clearAllCacheFiles()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,45 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteSitemap extends Write
|
||||
{
|
||||
public function updateSitemap($folderName, $sitemapFileName, $requestFileName, $data, $baseUrl)
|
||||
{
|
||||
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
|
||||
$sitemap = $this->addUrlSet($sitemap, $baseUrl);
|
||||
$sitemap .= $this->generateUrlSets($data);
|
||||
$sitemap .= '</urlset>';
|
||||
|
||||
$this->writeFile($folderName, $sitemapFileName, $sitemap);
|
||||
$this->writeFile($folderName, $requestFileName, time());
|
||||
}
|
||||
|
||||
public function generateUrlSets($data)
|
||||
{
|
||||
$urlset = '';
|
||||
|
||||
foreach($data as $item)
|
||||
{
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
|
||||
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
|
||||
}
|
||||
else
|
||||
{
|
||||
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
|
||||
}
|
||||
}
|
||||
return $urlset;
|
||||
}
|
||||
|
||||
public function addUrlSet($urlset, $url)
|
||||
{
|
||||
$urlset .= ' <url>' . "\n";
|
||||
$urlset .= ' <loc>' . $url . '</loc>' . "\n";
|
||||
$urlset .= ' </url>' . "\n";
|
||||
return $urlset;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteSitemap extends Write
|
||||
{
|
||||
public function updateSitemap($folderName, $sitemapFileName, $requestFileName, $data, $baseUrl)
|
||||
{
|
||||
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
|
||||
$sitemap = $this->addUrlSet($sitemap, $baseUrl);
|
||||
$sitemap .= $this->generateUrlSets($data);
|
||||
$sitemap .= '</urlset>';
|
||||
|
||||
$this->writeFile($folderName, $sitemapFileName, $sitemap);
|
||||
$this->writeFile($folderName, $requestFileName, time());
|
||||
}
|
||||
|
||||
public function generateUrlSets($data)
|
||||
{
|
||||
$urlset = '';
|
||||
|
||||
foreach($data as $item)
|
||||
{
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
|
||||
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
|
||||
}
|
||||
else
|
||||
{
|
||||
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
|
||||
}
|
||||
}
|
||||
return $urlset;
|
||||
}
|
||||
|
||||
public function addUrlSet($urlset, $url)
|
||||
{
|
||||
$urlset .= ' <url>' . "\n";
|
||||
$urlset .= ' <loc>' . $url . '</loc>' . "\n";
|
||||
$urlset .= ' </url>' . "\n";
|
||||
return $urlset;
|
||||
}
|
||||
}
|
@ -1,38 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteYaml extends Write
|
||||
{
|
||||
/**
|
||||
* Get the a yaml file.
|
||||
* @param string $fileName is the name of the Yaml Folder.
|
||||
* @param string $yamlFileName is the name of the Yaml File.
|
||||
*/
|
||||
public function getYaml($folderName, $yamlFileName)
|
||||
{
|
||||
$yaml = $this->getFile($folderName, $yamlFileName);
|
||||
|
||||
if($yaml)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse($yaml);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a yaml file.
|
||||
* @param string $fileName is the name of the Yaml Folder.
|
||||
* @param string $yamlFileName is the name of the Yaml File.
|
||||
* @param array $contentArray is the content as an array.
|
||||
*/
|
||||
public function updateYaml($folderName, $yamlFileName, $contentArray)
|
||||
{
|
||||
$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6);
|
||||
if($this->writeFile($folderName, $yamlFileName, $yaml))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class WriteYaml extends Write
|
||||
{
|
||||
/**
|
||||
* Get the a yaml file.
|
||||
* @param string $fileName is the name of the Yaml Folder.
|
||||
* @param string $yamlFileName is the name of the Yaml File.
|
||||
*/
|
||||
public function getYaml($folderName, $yamlFileName)
|
||||
{
|
||||
$yaml = $this->getFile($folderName, $yamlFileName);
|
||||
|
||||
if($yaml)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse($yaml);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a yaml file.
|
||||
* @param string $fileName is the name of the Yaml Folder.
|
||||
* @param string $yamlFileName is the name of the Yaml File.
|
||||
* @param array $contentArray is the content as an array.
|
||||
*/
|
||||
public function updateYaml($folderName, $yamlFileName, $contentArray)
|
||||
{
|
||||
$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6);
|
||||
if($this->writeFile($folderName, $yamlFileName, $yaml))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,149 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Typemill\Models\Fields;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
abstract class Plugin implements EventSubscriberInterface
|
||||
{
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
|
||||
public function __construct($container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function getSettings()
|
||||
{
|
||||
return $this->container->get('settings');
|
||||
}
|
||||
|
||||
protected function getPluginSettings($plugin)
|
||||
{
|
||||
return $this->container->get('settings')['plugins'][$plugin];
|
||||
}
|
||||
|
||||
protected function getRoute()
|
||||
{
|
||||
return $this->container['request']->getUri();
|
||||
}
|
||||
|
||||
protected function getPath()
|
||||
{
|
||||
return $this->container['request']->getUri()->getPath();
|
||||
}
|
||||
|
||||
protected function getDispatcher()
|
||||
{
|
||||
return $this->$dispatcher;
|
||||
}
|
||||
|
||||
protected function getTwig()
|
||||
{
|
||||
return $this->container['view'];
|
||||
}
|
||||
|
||||
protected function addTwigGlobal($name, $class)
|
||||
{
|
||||
$this->container->view->getEnvironment()->addGlobal($name, $class);
|
||||
}
|
||||
|
||||
protected function addTwigFilter($name, $filter)
|
||||
{
|
||||
$filter = new \Twig_SimpleFilter($name, $filter);
|
||||
$this->container->view->getEnvironment()->addFilter($filter);
|
||||
}
|
||||
|
||||
protected function addTwigFunction($name, $function)
|
||||
{
|
||||
$function = new \Twig_SimpleFunction($name, $function);
|
||||
$this->container->view->getEnvironment()->addFunction($function);
|
||||
}
|
||||
|
||||
protected function addJS($JS)
|
||||
{
|
||||
$this->container->assets->addJS($JS);
|
||||
}
|
||||
|
||||
protected function addInlineJS($JS)
|
||||
{
|
||||
$this->container->assets->addInlineJS($JS);
|
||||
}
|
||||
|
||||
protected function addCSS($CSS)
|
||||
{
|
||||
$this->container->assets->addCSS($CSS);
|
||||
}
|
||||
|
||||
protected function addInlineCSS($CSS)
|
||||
{
|
||||
$this->container->assets->addInlineCSS($CSS);
|
||||
}
|
||||
|
||||
protected function markdownToHtml($markdown)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
$contentArray = $parsedown->text($markdown);
|
||||
$html = $parsedown->markup($contentArray,false);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function getFormData($pluginName)
|
||||
{
|
||||
$flash = $this->container->flash->getMessages();
|
||||
|
||||
if(isset($flash['formdata']))
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
$formdata = $yaml->getYaml('settings', 'formdata.yaml');
|
||||
$yaml->updateYaml('settings', 'formdata.yaml', '');
|
||||
|
||||
if($flash['formdata'][0] == $pluginName && isset($formdata[$pluginName]))
|
||||
{
|
||||
return $formdata[$pluginName];
|
||||
}
|
||||
}
|
||||
elseif(isset($flash['publicform']) && $flash['publicform'][0] == 'bot')
|
||||
{
|
||||
return 'bot';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function generateForm($pluginName)
|
||||
{
|
||||
$fieldsModel = new Fields();
|
||||
|
||||
$pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
|
||||
$settings = $this->getSettings();
|
||||
$buttonlabel = isset($settings['plugins'][$pluginName]['button_label']) ? $settings['plugins'][$pluginName]['button_label'] : false;
|
||||
$recaptcha = isset($settings['plugins'][$pluginName]['recaptcha']) ? $settings['plugins'][$pluginName]['recaptcha_webkey'] : false;
|
||||
|
||||
if(isset($pluginDefinitions['public']['fields']))
|
||||
{
|
||||
# add simple honeypot spam protection
|
||||
$pluginDefinitions['public']['fields']['personal-mail'] = ['type' => 'text', 'class' => 'personal-mail'];
|
||||
|
||||
# get all the fields and prefill them with the dafault-data, the user-data or old input data
|
||||
$fields = $fieldsModel->getFields($settings, 'plugins', $pluginName, $pluginDefinitions, 'public');
|
||||
|
||||
# get Twig Instance
|
||||
$twig = $this->getTwig();
|
||||
|
||||
# render each field and add it to the form
|
||||
$form = $twig->fetch('/partials/form.twig', ['fields' => $fields, 'itemName' => $pluginName, 'object' => 'plugins', 'recaptcha_webkey' => $recaptcha, 'buttonlabel' => $buttonlabel]);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Typemill\Models\Fields;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
abstract class Plugin implements EventSubscriberInterface
|
||||
{
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
|
||||
public function __construct($container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function isXhr()
|
||||
{
|
||||
if($this->container['request']->isXhr())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getParams()
|
||||
{
|
||||
return $this->container['request']->getParams();
|
||||
}
|
||||
|
||||
protected function returnJson($data)
|
||||
{
|
||||
return $this->container['response']
|
||||
->withHeader("Content-Type", "application/json")
|
||||
->withStatus(200)
|
||||
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
protected function returnJsonError($data)
|
||||
{
|
||||
return $this->container['response']
|
||||
->withHeader("Content-Type", "application/json")
|
||||
->withStatus(400)
|
||||
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
protected function getSettings()
|
||||
{
|
||||
return $this->container->get('settings');
|
||||
}
|
||||
|
||||
protected function getPluginSettings($plugin)
|
||||
{
|
||||
return $this->container->get('settings')['plugins'][$plugin];
|
||||
}
|
||||
|
||||
protected function getRoute()
|
||||
{
|
||||
return $this->container['request']->getUri();
|
||||
}
|
||||
|
||||
protected function getPath()
|
||||
{
|
||||
return $this->container['request']->getUri()->getPath();
|
||||
}
|
||||
|
||||
protected function getDispatcher()
|
||||
{
|
||||
return $this->$dispatcher;
|
||||
}
|
||||
|
||||
protected function getTwig()
|
||||
{
|
||||
return $this->container['view'];
|
||||
}
|
||||
|
||||
protected function addTwigGlobal($name, $class)
|
||||
{
|
||||
$this->container->view->getEnvironment()->addGlobal($name, $class);
|
||||
}
|
||||
|
||||
protected function addTwigFilter($name, $filter)
|
||||
{
|
||||
$filter = new \Twig_SimpleFilter($name, $filter);
|
||||
$this->container->view->getEnvironment()->addFilter($filter);
|
||||
}
|
||||
|
||||
protected function addTwigFunction($name, $function)
|
||||
{
|
||||
$function = new \Twig_SimpleFunction($name, $function);
|
||||
$this->container->view->getEnvironment()->addFunction($function);
|
||||
}
|
||||
|
||||
protected function addJS($JS)
|
||||
{
|
||||
$this->container->assets->addJS($JS);
|
||||
}
|
||||
|
||||
protected function addInlineJS($JS)
|
||||
{
|
||||
$this->container->assets->addInlineJS($JS);
|
||||
}
|
||||
|
||||
protected function addCSS($CSS)
|
||||
{
|
||||
$this->container->assets->addCSS($CSS);
|
||||
}
|
||||
|
||||
protected function addInlineCSS($CSS)
|
||||
{
|
||||
$this->container->assets->addInlineCSS($CSS);
|
||||
}
|
||||
|
||||
protected function activateAxios()
|
||||
{
|
||||
$this->container->assets->activateAxios();
|
||||
}
|
||||
|
||||
protected function activateVue()
|
||||
{
|
||||
$this->container->assets->activateVue();
|
||||
}
|
||||
|
||||
protected function activateTachyons()
|
||||
{
|
||||
$this->container->assets->activateTachyons();
|
||||
}
|
||||
|
||||
protected function markdownToHtml($markdown)
|
||||
{
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
$contentArray = $parsedown->text($markdown);
|
||||
$html = $parsedown->markup($contentArray,false);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function getFormData($pluginName)
|
||||
{
|
||||
$flash = $this->container->flash->getMessages();
|
||||
|
||||
if(isset($flash['formdata']))
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
$formdata = $yaml->getYaml('settings', 'formdata.yaml');
|
||||
$yaml->updateYaml('settings', 'formdata.yaml', '');
|
||||
|
||||
if($flash['formdata'][0] == $pluginName && isset($formdata[$pluginName]))
|
||||
{
|
||||
return $formdata[$pluginName];
|
||||
}
|
||||
}
|
||||
elseif(isset($flash['publicform']) && $flash['publicform'][0] == 'bot')
|
||||
{
|
||||
return 'bot';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function generateForm($pluginName)
|
||||
{
|
||||
$fieldsModel = new Fields();
|
||||
|
||||
$pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
|
||||
$settings = $this->getSettings();
|
||||
$buttonlabel = isset($settings['plugins'][$pluginName]['button_label']) ? $settings['plugins'][$pluginName]['button_label'] : false;
|
||||
$recaptcha = isset($settings['plugins'][$pluginName]['recaptcha']) ? $settings['plugins'][$pluginName]['recaptcha_webkey'] : false;
|
||||
|
||||
if(isset($pluginDefinitions['public']['fields']))
|
||||
{
|
||||
# add simple honeypot spam protection
|
||||
$pluginDefinitions['public']['fields']['personal-mail'] = ['type' => 'text', 'class' => 'personal-mail'];
|
||||
|
||||
# get all the fields and prefill them with the dafault-data, the user-data or old input data
|
||||
$fields = $fieldsModel->getFields($settings, 'plugins', $pluginName, $pluginDefinitions, 'public');
|
||||
|
||||
# get Twig Instance
|
||||
$twig = $this->getTwig();
|
||||
|
||||
# render each field and add it to the form
|
||||
$form = $twig->fetch('/partials/form.twig', ['fields' => $fields, 'itemName' => $pluginName, 'object' => 'plugins', 'recaptcha_webkey' => $recaptcha, 'buttonlabel' => $buttonlabel]);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
@ -1,112 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Plugins
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$pluginFolder = $this->scanPluginFolder();
|
||||
$classNames = array();
|
||||
|
||||
/* iterate over plugin folders */
|
||||
foreach($pluginFolder as $plugin)
|
||||
{
|
||||
$className = '\\Plugins\\' . $plugin . '\\' . $plugin;
|
||||
|
||||
/* if plugin-class and subscribe-method exists, add classname to array */
|
||||
if(class_exists($className) /* && method_exists($className, 'getSubscribedEvents') */)
|
||||
{
|
||||
$classNames[] = array('className' => $className, 'name' => $plugin);
|
||||
}
|
||||
}
|
||||
return $classNames;
|
||||
}
|
||||
|
||||
public function getNewRoutes($className, $routes)
|
||||
{
|
||||
/* if route-method exists in plugin-class */
|
||||
if(method_exists($className, 'addNewRoutes'))
|
||||
{
|
||||
/* add the routes */
|
||||
$pluginRoutes = $className::addNewRoutes();
|
||||
|
||||
/* multi-dimensional or simple array of routes */
|
||||
if(isset($pluginRoutes[0]))
|
||||
{
|
||||
/* if they are properly formatted, add them to routes array */
|
||||
foreach($pluginRoutes as $pluginRoute)
|
||||
{
|
||||
if($this->checkRouteArray($routes,$pluginRoute))
|
||||
{
|
||||
$pluginRoute['route'] = strtolower($pluginRoute['route']);
|
||||
$routes[] = $pluginRoute;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif(is_array($routes))
|
||||
{
|
||||
if($this->checkRouteArray($routes,$pluginRoutes))
|
||||
{
|
||||
$pluginRoutes['route'] = strtolower($pluginRoutes['route']);
|
||||
$routes[] = $pluginRoutes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
public function getNewMiddleware($className, $middleware)
|
||||
{
|
||||
if(method_exists($className, 'addNewMiddleware'))
|
||||
{
|
||||
$pluginMiddleware = $className::addNewMiddleware();
|
||||
|
||||
if($pluginMiddleware)
|
||||
{
|
||||
$middleware[] = $pluginMiddleware;
|
||||
}
|
||||
}
|
||||
|
||||
return $middleware;
|
||||
}
|
||||
|
||||
private function checkRouteArray($routes,$route)
|
||||
{
|
||||
if(
|
||||
isset($route['httpMethod']) AND in_array($route['httpMethod'], array('get','post','put','delete','head','patch','options'))
|
||||
AND isset($route['route']) AND is_string($route['route'])
|
||||
AND isset($route['class']) AND is_string($route['class'])
|
||||
AND !$this->in_array_r(strtolower($route['route']),$routes))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function in_array_r($needle, $haystack, $strict = false)
|
||||
{
|
||||
foreach ($haystack as $item)
|
||||
{
|
||||
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && $this->in_array_r($needle, $item, $strict)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function scanPluginFolder()
|
||||
{
|
||||
$pluginsDir = __DIR__ . '/../plugins';
|
||||
|
||||
/* check if plugins directory exists */
|
||||
if(!is_dir($pluginsDir)){ return array(); }
|
||||
|
||||
/* get all plugins folder */
|
||||
$plugins = array_diff(scandir($pluginsDir), array('..', '.'));
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Plugins
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$pluginFolder = $this->scanPluginFolder();
|
||||
$classNames = array();
|
||||
|
||||
/* iterate over plugin folders */
|
||||
foreach($pluginFolder as $plugin)
|
||||
{
|
||||
$className = '\\Plugins\\' . $plugin . '\\' . $plugin;
|
||||
|
||||
/* if plugin-class and subscribe-method exists, add classname to array */
|
||||
if(class_exists($className) /* && method_exists($className, 'getSubscribedEvents') */)
|
||||
{
|
||||
$classNames[] = array('className' => $className, 'name' => $plugin);
|
||||
}
|
||||
}
|
||||
return $classNames;
|
||||
}
|
||||
|
||||
public function getNewRoutes($className, $routes)
|
||||
{
|
||||
/* if route-method exists in plugin-class */
|
||||
if(method_exists($className, 'addNewRoutes'))
|
||||
{
|
||||
/* add the routes */
|
||||
$pluginRoutes = $className::addNewRoutes();
|
||||
|
||||
/* multi-dimensional or simple array of routes */
|
||||
if(isset($pluginRoutes[0]))
|
||||
{
|
||||
/* if they are properly formatted, add them to routes array */
|
||||
foreach($pluginRoutes as $pluginRoute)
|
||||
{
|
||||
if($this->checkRouteArray($routes,$pluginRoute))
|
||||
{
|
||||
$pluginRoute['route'] = strtolower($pluginRoute['route']);
|
||||
$routes[] = $pluginRoute;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif(is_array($routes))
|
||||
{
|
||||
if($this->checkRouteArray($routes,$pluginRoutes))
|
||||
{
|
||||
$pluginRoutes['route'] = strtolower($pluginRoutes['route']);
|
||||
$routes[] = $pluginRoutes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
public function getNewMiddleware($className, $middleware)
|
||||
{
|
||||
if(method_exists($className, 'addNewMiddleware'))
|
||||
{
|
||||
$pluginMiddleware = $className::addNewMiddleware();
|
||||
|
||||
if($pluginMiddleware)
|
||||
{
|
||||
$middleware[] = $pluginMiddleware;
|
||||
}
|
||||
}
|
||||
|
||||
return $middleware;
|
||||
}
|
||||
|
||||
private function checkRouteArray($routes,$route)
|
||||
{
|
||||
if(
|
||||
isset($route['httpMethod']) AND in_array($route['httpMethod'], array('get','post','put','delete','head','patch','options'))
|
||||
AND isset($route['route']) AND is_string($route['route'])
|
||||
AND isset($route['class']) AND is_string($route['class'])
|
||||
AND !$this->in_array_r(strtolower($route['route']),$routes))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function in_array_r($needle, $haystack, $strict = false)
|
||||
{
|
||||
foreach ($haystack as $item)
|
||||
{
|
||||
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && $this->in_array_r($needle, $item, $strict)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function scanPluginFolder()
|
||||
{
|
||||
$pluginsDir = __DIR__ . '/../plugins';
|
||||
|
||||
/* check if plugins directory exists */
|
||||
if(!is_dir($pluginsDir)){ return array(); }
|
||||
|
||||
/* get all plugins folder */
|
||||
$plugins = array_diff(scandir($pluginsDir), array('..', '.'));
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Typemill\Controllers\SettingsController;
|
||||
use Typemill\Controllers\ContentController;
|
||||
use Typemill\Controllers\ContentApiController;
|
||||
use Typemill\Middleware\RestrictApiAccess;
|
||||
|
||||
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/article', ContentApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->get('/api/v1/navigation', ContentApiController::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/block', ContentApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/moveblock', ContentApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/image', ContentApiController::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/image', ContentApiController::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
<?php
|
||||
|
||||
use Typemill\Controllers\SettingsController;
|
||||
use Typemill\Controllers\ContentController;
|
||||
use Typemill\Controllers\ContentApiController;
|
||||
use Typemill\Middleware\RestrictApiAccess;
|
||||
|
||||
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/article', ContentApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->get('/api/v1/navigation', ContentApiController::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/block', ContentApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/moveblock', ContentApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/image', ContentApiController::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/image', ContentApiController::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->post('/api/v1/video', ContentApiController::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
|
@ -1,72 +1,72 @@
|
||||
<?php
|
||||
|
||||
use Typemill\Controllers\PageController;
|
||||
use Typemill\Controllers\FormController;
|
||||
use Typemill\Controllers\SetupController;
|
||||
use Typemill\Controllers\AuthController;
|
||||
use Typemill\Controllers\SettingsController;
|
||||
use Typemill\Controllers\ContentBackendController;
|
||||
use Typemill\Middleware\RedirectIfUnauthenticated;
|
||||
use Typemill\Middleware\RedirectIfAuthenticated;
|
||||
use Typemill\Middleware\RedirectIfNoAdmin;
|
||||
|
||||
if($settings['settings']['setup'])
|
||||
{
|
||||
$app->get('/setup', SetupController::class . ':show')->setName('setup.show');
|
||||
$app->post('/setup', SetupController::class . ':create')->setName('setup.create');
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->get('/setup', AuthController::class . ':redirect');
|
||||
}
|
||||
if($settings['settings']['welcome'])
|
||||
{
|
||||
$app->get('/setup/welcome', SetupController::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome');
|
||||
}
|
||||
|
||||
$app->post('/tm/formpost', FormController::class . ':savePublicForm')->setName('form.save');
|
||||
|
||||
$app->get('/tm', AuthController::class . ':redirect');
|
||||
$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router']));
|
||||
$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router']));
|
||||
$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
|
||||
$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
|
||||
$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
|
||||
$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
|
||||
foreach($routes as $pluginRoute)
|
||||
{
|
||||
$method = $pluginRoute['httpMethod'];
|
||||
$route = $pluginRoute['route'];
|
||||
$class = $pluginRoute['class'];
|
||||
|
||||
if(isset($pluginRoute['name']))
|
||||
{
|
||||
$app->{$method}($route, $class)->setName($pluginRoute['name']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->{$method}($route, $class);
|
||||
}
|
||||
}
|
||||
|
||||
<?php
|
||||
|
||||
use Typemill\Controllers\PageController;
|
||||
use Typemill\Controllers\FormController;
|
||||
use Typemill\Controllers\SetupController;
|
||||
use Typemill\Controllers\AuthController;
|
||||
use Typemill\Controllers\SettingsController;
|
||||
use Typemill\Controllers\ContentBackendController;
|
||||
use Typemill\Middleware\RedirectIfUnauthenticated;
|
||||
use Typemill\Middleware\RedirectIfAuthenticated;
|
||||
use Typemill\Middleware\RedirectIfNoAdmin;
|
||||
|
||||
if($settings['settings']['setup'])
|
||||
{
|
||||
$app->get('/setup', SetupController::class . ':show')->setName('setup.show');
|
||||
$app->post('/setup', SetupController::class . ':create')->setName('setup.create');
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->get('/setup', AuthController::class . ':redirect');
|
||||
}
|
||||
if($settings['settings']['welcome'])
|
||||
{
|
||||
$app->get('/setup/welcome', SetupController::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome');
|
||||
}
|
||||
|
||||
$app->post('/tm/formpost', FormController::class . ':savePublicForm')->setName('form.save');
|
||||
|
||||
$app->get('/tm', AuthController::class . ':redirect');
|
||||
$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router']));
|
||||
$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router']));
|
||||
$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
|
||||
$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
|
||||
$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
|
||||
|
||||
$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
|
||||
|
||||
foreach($routes as $pluginRoute)
|
||||
{
|
||||
$method = $pluginRoute['httpMethod'];
|
||||
$route = $pluginRoute['route'];
|
||||
$class = $pluginRoute['class'];
|
||||
|
||||
if(isset($pluginRoute['name']))
|
||||
{
|
||||
$app->{$method}($route, $class)->setName($pluginRoute['name']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->{$method}($route, $class);
|
||||
}
|
||||
}
|
||||
|
||||
$app->get('/[{params:.*}]', PageController::class . ':index')->setName('home');
|
@ -1,124 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Settings
|
||||
{
|
||||
public static function loadSettings()
|
||||
{
|
||||
$defaultSettings = self::getDefaultSettings();
|
||||
$userSettings = self::getUserSettings();
|
||||
|
||||
$settings = $defaultSettings;
|
||||
|
||||
if($userSettings)
|
||||
{
|
||||
$settings = array_merge($defaultSettings, $userSettings);
|
||||
}
|
||||
|
||||
return array('settings' => $settings);
|
||||
}
|
||||
|
||||
public static function getDefaultSettings()
|
||||
{
|
||||
$rootPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
|
||||
|
||||
return [
|
||||
'determineRouteBeforeAppMiddleware' => true,
|
||||
'displayErrorDetails' => false,
|
||||
'title' => 'TYPEMILL',
|
||||
'author' => 'Unknown',
|
||||
'copyright' => 'Copyright',
|
||||
'language' => 'en',
|
||||
'startpage' => true,
|
||||
'rootPath' => $rootPath,
|
||||
'theme' => ($theme = 'typemill'),
|
||||
'themeFolder' => ($themeFolder = 'themes'),
|
||||
'themeBasePath' => $rootPath,
|
||||
'themePath' => $rootPath . $themeFolder . DIRECTORY_SEPARATOR . $theme,
|
||||
'settingsPath' => $rootPath . 'settings',
|
||||
'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
|
||||
'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
|
||||
'editor' => 'raw',
|
||||
'contentFolder' => 'content',
|
||||
'cache' => true,
|
||||
'cachePath' => $rootPath . 'cache',
|
||||
'version' => '1.2.15',
|
||||
'setup' => true,
|
||||
'welcome' => true,
|
||||
'images' => ['live' => ['width' => 820], 'mlibrary' => ['width' => 50, 'height' => 50]],
|
||||
];
|
||||
}
|
||||
|
||||
public static function getUserSettings()
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
$userSettings = $yaml->getYaml('settings', 'settings.yaml');
|
||||
|
||||
return $userSettings;
|
||||
}
|
||||
|
||||
public static function getObjectSettings($objectType, $objectName)
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
$objectFolder = $objectType . DIRECTORY_SEPARATOR . $objectName;
|
||||
$objectFile = $objectName . '.yaml';
|
||||
$objectSettings = $yaml->getYaml($objectFolder, $objectFile);
|
||||
|
||||
return $objectSettings;
|
||||
}
|
||||
|
||||
public static function createSettings()
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
# create initial settings file with only setup false
|
||||
if($yaml->updateYaml('settings', 'settings.yaml', array('setup' => false)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function updateSettings($settings)
|
||||
{
|
||||
# only allow if usersettings already exists (setup has been done)
|
||||
$userSettings = self::getUserSettings();
|
||||
|
||||
if($userSettings)
|
||||
{
|
||||
# whitelist settings that can be stored in usersettings (values are not relevant here, only keys)
|
||||
$allowedUserSettings = ['displayErrorDetails' => false,
|
||||
'title' => false,
|
||||
'copyright' => false,
|
||||
'language' => false,
|
||||
'startpage' => false,
|
||||
'author' => false,
|
||||
'year' => false,
|
||||
'theme' => false,
|
||||
'editor' => false,
|
||||
'setup' => false,
|
||||
'welcome' => false,
|
||||
'images' => false,
|
||||
'plugins' => false,
|
||||
'themes' => false,
|
||||
'latestVersion' => false
|
||||
];
|
||||
|
||||
# cleanup the existing usersettings
|
||||
$userSettings = array_intersect_key($userSettings, $allowedUserSettings);
|
||||
|
||||
# cleanup the new settings passed as an argument
|
||||
$settings = array_intersect_key($settings, $allowedUserSettings);
|
||||
|
||||
# merge usersettings with new settings
|
||||
$settings = array_merge($userSettings, $settings);
|
||||
|
||||
/* write settings to yaml */
|
||||
$yaml = new Models\WriteYaml();
|
||||
$yaml->updateYaml('settings', 'settings.yaml', $settings);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill;
|
||||
|
||||
class Settings
|
||||
{
|
||||
public static function loadSettings()
|
||||
{
|
||||
$defaultSettings = self::getDefaultSettings();
|
||||
$userSettings = self::getUserSettings();
|
||||
|
||||
$settings = $defaultSettings;
|
||||
|
||||
if($userSettings)
|
||||
{
|
||||
$settings = array_merge($defaultSettings, $userSettings);
|
||||
}
|
||||
|
||||
return array('settings' => $settings);
|
||||
}
|
||||
|
||||
public static function getDefaultSettings()
|
||||
{
|
||||
$rootPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
|
||||
|
||||
return [
|
||||
'determineRouteBeforeAppMiddleware' => true,
|
||||
'displayErrorDetails' => false,
|
||||
'title' => 'TYPEMILL',
|
||||
'author' => 'Unknown',
|
||||
'copyright' => 'Copyright',
|
||||
'language' => 'en',
|
||||
'startpage' => true,
|
||||
'rootPath' => $rootPath,
|
||||
'theme' => ($theme = 'typemill'),
|
||||
'themeFolder' => ($themeFolder = 'themes'),
|
||||
'themeBasePath' => $rootPath,
|
||||
'themePath' => $rootPath . $themeFolder . DIRECTORY_SEPARATOR . $theme,
|
||||
'settingsPath' => $rootPath . 'settings',
|
||||
'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
|
||||
'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
|
||||
'editor' => 'raw',
|
||||
'contentFolder' => 'content',
|
||||
'cache' => true,
|
||||
'cachePath' => $rootPath . 'cache',
|
||||
'version' => '1.2.15',
|
||||
'setup' => true,
|
||||
'welcome' => true,
|
||||
'images' => ['live' => ['width' => 820], 'mlibrary' => ['width' => 50, 'height' => 50]],
|
||||
];
|
||||
}
|
||||
|
||||
public static function getUserSettings()
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
$userSettings = $yaml->getYaml('settings', 'settings.yaml');
|
||||
|
||||
return $userSettings;
|
||||
}
|
||||
|
||||
public static function getObjectSettings($objectType, $objectName)
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
$objectFolder = $objectType . DIRECTORY_SEPARATOR . $objectName;
|
||||
$objectFile = $objectName . '.yaml';
|
||||
$objectSettings = $yaml->getYaml($objectFolder, $objectFile);
|
||||
|
||||
return $objectSettings;
|
||||
}
|
||||
|
||||
public static function createSettings()
|
||||
{
|
||||
$yaml = new Models\WriteYaml();
|
||||
|
||||
# create initial settings file with only setup false
|
||||
if($yaml->updateYaml('settings', 'settings.yaml', array('setup' => false)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function updateSettings($settings)
|
||||
{
|
||||
# only allow if usersettings already exists (setup has been done)
|
||||
$userSettings = self::getUserSettings();
|
||||
|
||||
if($userSettings)
|
||||
{
|
||||
# whitelist settings that can be stored in usersettings (values are not relevant here, only keys)
|
||||
$allowedUserSettings = ['displayErrorDetails' => false,
|
||||
'title' => false,
|
||||
'copyright' => false,
|
||||
'language' => false,
|
||||
'startpage' => false,
|
||||
'author' => false,
|
||||
'year' => false,
|
||||
'theme' => false,
|
||||
'editor' => false,
|
||||
'setup' => false,
|
||||
'welcome' => false,
|
||||
'images' => false,
|
||||
'plugins' => false,
|
||||
'themes' => false,
|
||||
'latestVersion' => false
|
||||
];
|
||||
|
||||
# cleanup the existing usersettings
|
||||
$userSettings = array_intersect_key($userSettings, $allowedUserSettings);
|
||||
|
||||
# cleanup the new settings passed as an argument
|
||||
$settings = array_intersect_key($settings, $allowedUserSettings);
|
||||
|
||||
# merge usersettings with new settings
|
||||
$settings = array_merge($userSettings, $settings);
|
||||
|
||||
/* write settings to yaml */
|
||||
$yaml = new Models\WriteYaml();
|
||||
$yaml->updateYaml('settings', 'settings.yaml', $settings);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +1,47 @@
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="setupWrapper">
|
||||
|
||||
<div class="setupContent">
|
||||
<p><i class="icon-bookmark-empty"></i>Remember to bookmark this page</p>
|
||||
</div>
|
||||
<div class="authformWrapper">
|
||||
<form method="POST" action="{{ path_for("auth.login") }}" autocomplete="off">
|
||||
|
||||
<fieldset class="auth">
|
||||
<div class="formElement{{ errors.username ? ' errors' : '' }}">
|
||||
<label for="username">Username <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="username" value="{{ old.username }}" required>
|
||||
{% if errors.signup_username %}
|
||||
<span class="error">{{ errors.username | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.password ? ' errors' : '' }}">
|
||||
<label for="password">Password <abbr title="required">*</abbr></label>
|
||||
<input type="password" name="password" required autoomplete="off">
|
||||
{% if errors.password %}
|
||||
<span class="error">{{ errors.password | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="loginarea" id="loginarea">
|
||||
<input type="submit" value="Login" id="loginbutton" class="loginbutton" />
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
{% if messages.time %}
|
||||
<div id="counter" class="counter">wait <span id="wait">{{ messages.time }}</span> sec</div>
|
||||
<div class="forgotpw"><a href="https://typemill.net/writers/forgot-password" target="_blank">Forgot password?</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setupContent">
|
||||
<p><a href="{{ base_url() }}">back to startpage</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<footer></footer>
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="setupWrapper">
|
||||
|
||||
<div class="setupContent">
|
||||
<p><i class="icon-bookmark-empty"></i>Remember to bookmark this page</p>
|
||||
</div>
|
||||
<div class="authformWrapper">
|
||||
<form method="POST" action="{{ path_for("auth.login") }}" autocomplete="off">
|
||||
|
||||
<fieldset class="auth">
|
||||
<div class="formElement{{ errors.username ? ' errors' : '' }}">
|
||||
<label for="username">Username <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="username" value="{{ old.username }}" required>
|
||||
{% if errors.signup_username %}
|
||||
<span class="error">{{ errors.username | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.password ? ' errors' : '' }}">
|
||||
<label for="password">Password <abbr title="required">*</abbr></label>
|
||||
<input type="password" name="password" required autoomplete="off">
|
||||
{% if errors.password %}
|
||||
<span class="error">{{ errors.password | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="loginarea" id="loginarea">
|
||||
<input type="submit" value="Login" id="loginbutton" class="loginbutton" />
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
{% if messages.time %}
|
||||
<div id="counter" class="counter">wait <span id="wait">{{ messages.time }}</span> sec</div>
|
||||
<div class="forgotpw"><a href="https://typemill.net/writers/forgot-password" target="_blank">Forgot password?</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setupContent">
|
||||
<p><a href="{{ base_url() }}">back to startpage</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<footer></footer>
|
||||
{% endblock %}
|
@ -1,41 +1,41 @@
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
{% block title %}Setup{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="setupWrapper">
|
||||
<div class="authformWrapper">
|
||||
<form method="POST" action="{{ path_for('setup.create') }}" autocomplete="off">
|
||||
|
||||
<fieldset class="auth">
|
||||
<div class="formElement{{ errors.username ? ' errors' : '' }}">
|
||||
<label for="username">Username <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="username" value="{{ old.username }}" required>
|
||||
{% if errors.username %}
|
||||
<span class="error">{{ errors.username | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.email ? ' errors' : '' }}">
|
||||
<label for="email">E-Mail <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="email" value="{{ old.email }}" required>
|
||||
{% if errors.email %}
|
||||
<span class="error">{{ errors.email | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.password ? ' errors' : '' }}">
|
||||
<label for="password">Password <abbr title="required">*</abbr></label>
|
||||
<input type="password" name="password" required autocomplete="off">
|
||||
{% if errors.password %}
|
||||
<span class="error">{{ errors.password | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Create User" />
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
{% block title %}Setup{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="setupWrapper">
|
||||
<div class="authformWrapper">
|
||||
<form method="POST" action="{{ path_for('setup.create') }}" autocomplete="off">
|
||||
|
||||
<fieldset class="auth">
|
||||
<div class="formElement{{ errors.username ? ' errors' : '' }}">
|
||||
<label for="username">Username <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="username" value="{{ old.username }}" required>
|
||||
{% if errors.username %}
|
||||
<span class="error">{{ errors.username | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.email ? ' errors' : '' }}">
|
||||
<label for="email">E-Mail <abbr title="required">*</abbr></label>
|
||||
<input type="text" name="email" value="{{ old.email }}" required>
|
||||
{% if errors.email %}
|
||||
<span class="error">{{ errors.email | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="formElement{{ errors.password ? ' errors' : '' }}">
|
||||
<label for="password">Password <abbr title="required">*</abbr></label>
|
||||
<input type="password" name="password" required autocomplete="off">
|
||||
{% if errors.password %}
|
||||
<span class="error">{{ errors.password | first }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Create User" />
|
||||
{{ csrf_field() | raw }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,49 +1,49 @@
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
|
||||
{% block title %}Setup Welcome{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="welcome">
|
||||
|
||||
<div class="medium">
|
||||
<div class="welcomeIntro">
|
||||
<h1>Hurra!</h1>
|
||||
<p>Your account has been created and you are logged in now.</p>
|
||||
<p><strong>Next step:</strong> Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.</p>
|
||||
<p><strong>New:</strong> Write beautiful math formulas with markdown with direct preview in the visual editor. We completely refactored it!!</p>
|
||||
<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="https://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/typemill/typemill"><i class="icon-link-ext"></i> github</a>.</p>
|
||||
</div>
|
||||
<a class="button" href="{{ path_for('settings.show') }}">Configure your website</a>
|
||||
</div>
|
||||
|
||||
<div class="small">
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('settings.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>System</h3>
|
||||
<p>Give your new website a name, add the author and choose a copyright.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('themes.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>Themes</h3>
|
||||
<p>Choose a theme for your website and configure the theme details.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('plugins.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>Plugins</h3>
|
||||
<p>Add new features to your website with plugins and configure them.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
|
||||
{% block title %}Setup Welcome{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="welcome">
|
||||
|
||||
<div class="medium">
|
||||
<div class="welcomeIntro">
|
||||
<h1>Hurra!</h1>
|
||||
<p>Your account has been created and you are logged in now.</p>
|
||||
<p><strong>Next step:</strong> Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.</p>
|
||||
<p><strong>New:</strong> Write beautiful math formulas with markdown with direct preview in the visual editor. We completely refactored it!!</p>
|
||||
<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="https://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/typemill/typemill"><i class="icon-link-ext"></i> github</a>.</p>
|
||||
</div>
|
||||
<a class="button" href="{{ path_for('settings.show') }}">Configure your website</a>
|
||||
</div>
|
||||
|
||||
<div class="small">
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('settings.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>System</h3>
|
||||
<p>Give your new website a name, add the author and choose a copyright.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('themes.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>Themes</h3>
|
||||
<p>Choose a theme for your website and configure the theme details.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="welcomeCard">
|
||||
<a href="{{ path_for('plugins.show') }}">
|
||||
<div class="welcomeInner">
|
||||
<h3>Plugins</h3>
|
||||
<p>Add new features to your website with plugins and configure them.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,202 +1,202 @@
|
||||
{
|
||||
"name": "",
|
||||
"css_prefix_text": "icon-",
|
||||
"css_use_suffix": false,
|
||||
"hinting": true,
|
||||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "381da2c2f7fd51f8de877c044d7f439d",
|
||||
"css": "picture",
|
||||
"code": 59392,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5211af474d3a9848f67f945e2ccaf143",
|
||||
"css": "cancel",
|
||||
"code": 59393,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
"code": 59394,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d7271d490b71df4311e32cdacae8b331",
|
||||
"css": "home",
|
||||
"code": 59395,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
|
||||
"css": "link",
|
||||
"code": 59396,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e99461abfef3923546da8d745372c995",
|
||||
"css": "cog",
|
||||
"code": 59397,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "a8cb1c217f02b073db3670c061cc54d2",
|
||||
"css": "italic",
|
||||
"code": 59398,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "02cca871bb69da75e8ee286b7055832c",
|
||||
"css": "bold",
|
||||
"code": 59399,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
|
||||
"css": "off",
|
||||
"code": 59400,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "872d9516df93eb6b776cc4d94bd97dac",
|
||||
"css": "video",
|
||||
"code": 59401,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "47a1f80457068fbeab69fdb83d7d0817",
|
||||
"css": "youtube-play",
|
||||
"code": 61802,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c5fd68d8253e605e7a78a0c75255b692",
|
||||
"css": "math",
|
||||
"code": 61466,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "6605ee6441bf499ffa3c63d3c7409471",
|
||||
"css": "move",
|
||||
"code": 61511,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e15f0d620a7897e2035c18c80142f6d9",
|
||||
"css": "link-ext",
|
||||
"code": 61582,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "a2a74f5e7b7d9ba054897d8c795a326a",
|
||||
"css": "list-bullet",
|
||||
"code": 61642,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f6766a8b042c2453a4e153af03294383",
|
||||
"css": "list-numbered",
|
||||
"code": 61643,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d4a4a38a40b728f46dad1de4ac950231",
|
||||
"css": "underline",
|
||||
"code": 61645,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8fb55fd696d9a0f58f3b27c1d8633750",
|
||||
"css": "table",
|
||||
"code": 61646,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5408be43f7c42bccee419c6be53fdef5",
|
||||
"css": "doc-text",
|
||||
"code": 61686,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ab95e1351ebaec5850101097cbf7097f",
|
||||
"css": "quote-left",
|
||||
"code": 61709,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "b091a8bd0fdade174951f17d936f51e4",
|
||||
"css": "folder-empty",
|
||||
"code": 61716,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "7034e4d22866af82bef811f52fb1ba46",
|
||||
"css": "code",
|
||||
"code": 61729,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "4e88371fb8857dacc1f66afe6314e426",
|
||||
"css": "superscript",
|
||||
"code": 61739,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "0c708edd8fae2376b3370aa56d40cf9e",
|
||||
"css": "header",
|
||||
"code": 61916,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c5845105a87df2ee1999826d90622f6a",
|
||||
"css": "paragraph",
|
||||
"code": 61917,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "eeec3208c90b7b48e804919d0d2d4a41",
|
||||
"css": "upload",
|
||||
"code": 59402,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "861ab06e455e2de3232ebef67d60d708",
|
||||
"css": "minus",
|
||||
"code": 59403,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "2qh229aneb95ds2afi7dbdsxz9jrbhcl",
|
||||
"css": "colon",
|
||||
"code": 59404,
|
||||
"src": "modernpics"
|
||||
},
|
||||
{
|
||||
"uid": "c819c6225685bae2eed1b8da13e629fa",
|
||||
"css": "list-alt",
|
||||
"code": 59406,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d2d6ab0dd4fb9365b1d5756380484bbb",
|
||||
"css": "pi",
|
||||
"code": 59405,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "d3b3f17bc3eb7cd809a07bbd4d178bee",
|
||||
"css": "resize-vertical",
|
||||
"code": 59407,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "2f5ef6f6b7aaebc56458ab4e865beff5",
|
||||
"css": "bookmark-empty",
|
||||
"code": 61591,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
{
|
||||
"name": "",
|
||||
"css_prefix_text": "icon-",
|
||||
"css_use_suffix": false,
|
||||
"hinting": true,
|
||||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "381da2c2f7fd51f8de877c044d7f439d",
|
||||
"css": "picture",
|
||||
"code": 59392,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5211af474d3a9848f67f945e2ccaf143",
|
||||
"css": "cancel",
|
||||
"code": 59393,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
"code": 59394,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d7271d490b71df4311e32cdacae8b331",
|
||||
"css": "home",
|
||||
"code": 59395,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
|
||||
"css": "link",
|
||||
"code": 59396,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e99461abfef3923546da8d745372c995",
|
||||
"css": "cog",
|
||||
"code": 59397,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "a8cb1c217f02b073db3670c061cc54d2",
|
||||
"css": "italic",
|
||||
"code": 59398,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "02cca871bb69da75e8ee286b7055832c",
|
||||
"css": "bold",
|
||||
"code": 59399,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
|
||||
"css": "off",
|
||||
"code": 59400,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "872d9516df93eb6b776cc4d94bd97dac",
|
||||
"css": "video",
|
||||
"code": 59401,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "47a1f80457068fbeab69fdb83d7d0817",
|
||||
"css": "youtube-play",
|
||||
"code": 61802,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c5fd68d8253e605e7a78a0c75255b692",
|
||||
"css": "math",
|
||||
"code": 61466,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "6605ee6441bf499ffa3c63d3c7409471",
|
||||
"css": "move",
|
||||
"code": 61511,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "e15f0d620a7897e2035c18c80142f6d9",
|
||||
"css": "link-ext",
|
||||
"code": 61582,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "a2a74f5e7b7d9ba054897d8c795a326a",
|
||||
"css": "list-bullet",
|
||||
"code": 61642,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f6766a8b042c2453a4e153af03294383",
|
||||
"css": "list-numbered",
|
||||
"code": 61643,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d4a4a38a40b728f46dad1de4ac950231",
|
||||
"css": "underline",
|
||||
"code": 61645,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "8fb55fd696d9a0f58f3b27c1d8633750",
|
||||
"css": "table",
|
||||
"code": 61646,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5408be43f7c42bccee419c6be53fdef5",
|
||||
"css": "doc-text",
|
||||
"code": 61686,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "ab95e1351ebaec5850101097cbf7097f",
|
||||
"css": "quote-left",
|
||||
"code": 61709,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "b091a8bd0fdade174951f17d936f51e4",
|
||||
"css": "folder-empty",
|
||||
"code": 61716,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "7034e4d22866af82bef811f52fb1ba46",
|
||||
"css": "code",
|
||||
"code": 61729,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "4e88371fb8857dacc1f66afe6314e426",
|
||||
"css": "superscript",
|
||||
"code": 61739,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "0c708edd8fae2376b3370aa56d40cf9e",
|
||||
"css": "header",
|
||||
"code": 61916,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c5845105a87df2ee1999826d90622f6a",
|
||||
"css": "paragraph",
|
||||
"code": 61917,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "eeec3208c90b7b48e804919d0d2d4a41",
|
||||
"css": "upload",
|
||||
"code": 59402,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "861ab06e455e2de3232ebef67d60d708",
|
||||
"css": "minus",
|
||||
"code": 59403,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "2qh229aneb95ds2afi7dbdsxz9jrbhcl",
|
||||
"css": "colon",
|
||||
"code": 59404,
|
||||
"src": "modernpics"
|
||||
},
|
||||
{
|
||||
"uid": "c819c6225685bae2eed1b8da13e629fa",
|
||||
"css": "list-alt",
|
||||
"code": 59406,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "d2d6ab0dd4fb9365b1d5756380484bbb",
|
||||
"css": "pi",
|
||||
"code": 59405,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "d3b3f17bc3eb7cd809a07bbd4d178bee",
|
||||
"css": "resize-vertical",
|
||||
"code": 59407,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "2f5ef6f6b7aaebc56458ab4e865beff5",
|
||||
"css": "bookmark-empty",
|
||||
"code": 61591,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,33 +1,33 @@
|
||||
|
||||
.icon-picture:before { content: '\e800'; } /* '' */
|
||||
.icon-cancel:before { content: '\e801'; } /* '' */
|
||||
.icon-plus:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-link:before { content: '\e804'; } /* '' */
|
||||
.icon-cog:before { content: '\e805'; } /* '' */
|
||||
.icon-italic:before { content: '\e806'; } /* '' */
|
||||
.icon-bold:before { content: '\e807'; } /* '' */
|
||||
.icon-off:before { content: '\e808'; } /* '' */
|
||||
.icon-video:before { content: '\e809'; } /* '' */
|
||||
.icon-upload:before { content: '\e80a'; } /* '' */
|
||||
.icon-minus:before { content: '\e80b'; } /* '' */
|
||||
.icon-colon:before { content: '\e80c'; } /* '' */
|
||||
.icon-pi:before { content: '\e80d'; } /* '' */
|
||||
.icon-list-alt:before { content: '\e80e'; } /* '' */
|
||||
.icon-resize-vertical:before { content: '\e80f'; } /* '' */
|
||||
.icon-math:before { content: '\f01a'; } /* '' */
|
||||
.icon-move:before { content: '\f047'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-bookmark-empty:before { content: '\f097'; } /* '' */
|
||||
.icon-list-bullet:before { content: '\f0ca'; } /* '' */
|
||||
.icon-list-numbered:before { content: '\f0cb'; } /* '' */
|
||||
.icon-underline:before { content: '\f0cd'; } /* '' */
|
||||
.icon-table:before { content: '\f0ce'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-code:before { content: '\f121'; } /* '' */
|
||||
.icon-superscript:before { content: '\f12b'; } /* '' */
|
||||
.icon-youtube-play:before { content: '\f16a'; } /* '' */
|
||||
.icon-header:before { content: '\f1dc'; } /* '' */
|
||||
|
||||
.icon-picture:before { content: '\e800'; } /* '' */
|
||||
.icon-cancel:before { content: '\e801'; } /* '' */
|
||||
.icon-plus:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-link:before { content: '\e804'; } /* '' */
|
||||
.icon-cog:before { content: '\e805'; } /* '' */
|
||||
.icon-italic:before { content: '\e806'; } /* '' */
|
||||
.icon-bold:before { content: '\e807'; } /* '' */
|
||||
.icon-off:before { content: '\e808'; } /* '' */
|
||||
.icon-video:before { content: '\e809'; } /* '' */
|
||||
.icon-upload:before { content: '\e80a'; } /* '' */
|
||||
.icon-minus:before { content: '\e80b'; } /* '' */
|
||||
.icon-colon:before { content: '\e80c'; } /* '' */
|
||||
.icon-pi:before { content: '\e80d'; } /* '' */
|
||||
.icon-list-alt:before { content: '\e80e'; } /* '' */
|
||||
.icon-resize-vertical:before { content: '\e80f'; } /* '' */
|
||||
.icon-math:before { content: '\f01a'; } /* '' */
|
||||
.icon-move:before { content: '\f047'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-bookmark-empty:before { content: '\f097'; } /* '' */
|
||||
.icon-list-bullet:before { content: '\f0ca'; } /* '' */
|
||||
.icon-list-numbered:before { content: '\f0cb'; } /* '' */
|
||||
.icon-underline:before { content: '\f0cd'; } /* '' */
|
||||
.icon-table:before { content: '\f0ce'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-code:before { content: '\f121'; } /* '' */
|
||||
.icon-superscript:before { content: '\f12b'; } /* '' */
|
||||
.icon-youtube-play:before { content: '\f16a'; } /* '' */
|
||||
.icon-header:before { content: '\f1dc'; } /* '' */
|
||||
.icon-paragraph:before { content: '\f1dd'; } /* '' */
|
170
system/author/css/fontello/css/fontello-embedded.css
vendored
170
system/author/css/fontello/css/fontello-embedded.css
vendored
File diff suppressed because one or more lines are too long
@ -1,33 +1,33 @@
|
||||
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
86
system/author/css/fontello/css/fontello-ie7.css
vendored
86
system/author/css/fontello/css/fontello-ie7.css
vendored
@ -1,44 +1,44 @@
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
font-family: 'fontello';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
/* fix buttons height */
|
||||
line-height: 1em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
}
|
||||
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
font-family: 'fontello';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
/* fix buttons height */
|
||||
line-height: 1em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
}
|
||||
|
||||
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
176
system/author/css/fontello/css/fontello.css
vendored
176
system/author/css/fontello/css/fontello.css
vendored
@ -1,89 +1,89 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?35517051');
|
||||
src: url('../font/fontello.eot?35517051#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?35517051') format('woff2'),
|
||||
url('../font/fontello.woff?35517051') format('woff'),
|
||||
url('../font/fontello.ttf?35517051') format('truetype'),
|
||||
url('../font/fontello.svg?35517051#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
/*
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?35517051#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "fontello";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: none;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em;
|
||||
|
||||
/* Animation center compensation - margins should be symmetric */
|
||||
/* remove if not needed */
|
||||
margin-left: .2em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
|
||||
/* Font smoothing. That was taken from TWBS */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Uncomment for 3D effect */
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-picture:before { content: '\e800'; } /* '' */
|
||||
.icon-cancel:before { content: '\e801'; } /* '' */
|
||||
.icon-plus:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-link:before { content: '\e804'; } /* '' */
|
||||
.icon-cog:before { content: '\e805'; } /* '' */
|
||||
.icon-italic:before { content: '\e806'; } /* '' */
|
||||
.icon-bold:before { content: '\e807'; } /* '' */
|
||||
.icon-off:before { content: '\e808'; } /* '' */
|
||||
.icon-video:before { content: '\e809'; } /* '' */
|
||||
.icon-upload:before { content: '\e80a'; } /* '' */
|
||||
.icon-minus:before { content: '\e80b'; } /* '' */
|
||||
.icon-colon:before { content: '\e80c'; } /* '' */
|
||||
.icon-pi:before { content: '\e80d'; } /* '' */
|
||||
.icon-list-alt:before { content: '\e80e'; } /* '' */
|
||||
.icon-resize-vertical:before { content: '\e80f'; } /* '' */
|
||||
.icon-math:before { content: '\f01a'; } /* '' */
|
||||
.icon-move:before { content: '\f047'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-bookmark-empty:before { content: '\f097'; } /* '' */
|
||||
.icon-list-bullet:before { content: '\f0ca'; } /* '' */
|
||||
.icon-list-numbered:before { content: '\f0cb'; } /* '' */
|
||||
.icon-underline:before { content: '\f0cd'; } /* '' */
|
||||
.icon-table:before { content: '\f0ce'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-code:before { content: '\f121'; } /* '' */
|
||||
.icon-superscript:before { content: '\f12b'; } /* '' */
|
||||
.icon-youtube-play:before { content: '\f16a'; } /* '' */
|
||||
.icon-header:before { content: '\f1dc'; } /* '' */
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?35517051');
|
||||
src: url('../font/fontello.eot?35517051#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?35517051') format('woff2'),
|
||||
url('../font/fontello.woff?35517051') format('woff'),
|
||||
url('../font/fontello.ttf?35517051') format('truetype'),
|
||||
url('../font/fontello.svg?35517051#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
/*
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?35517051#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "fontello";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: none;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em;
|
||||
|
||||
/* Animation center compensation - margins should be symmetric */
|
||||
/* remove if not needed */
|
||||
margin-left: .2em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
|
||||
/* Font smoothing. That was taken from TWBS */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Uncomment for 3D effect */
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-picture:before { content: '\e800'; } /* '' */
|
||||
.icon-cancel:before { content: '\e801'; } /* '' */
|
||||
.icon-plus:before { content: '\e802'; } /* '' */
|
||||
.icon-home:before { content: '\e803'; } /* '' */
|
||||
.icon-link:before { content: '\e804'; } /* '' */
|
||||
.icon-cog:before { content: '\e805'; } /* '' */
|
||||
.icon-italic:before { content: '\e806'; } /* '' */
|
||||
.icon-bold:before { content: '\e807'; } /* '' */
|
||||
.icon-off:before { content: '\e808'; } /* '' */
|
||||
.icon-video:before { content: '\e809'; } /* '' */
|
||||
.icon-upload:before { content: '\e80a'; } /* '' */
|
||||
.icon-minus:before { content: '\e80b'; } /* '' */
|
||||
.icon-colon:before { content: '\e80c'; } /* '' */
|
||||
.icon-pi:before { content: '\e80d'; } /* '' */
|
||||
.icon-list-alt:before { content: '\e80e'; } /* '' */
|
||||
.icon-resize-vertical:before { content: '\e80f'; } /* '' */
|
||||
.icon-math:before { content: '\f01a'; } /* '' */
|
||||
.icon-move:before { content: '\f047'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-bookmark-empty:before { content: '\f097'; } /* '' */
|
||||
.icon-list-bullet:before { content: '\f0ca'; } /* '' */
|
||||
.icon-list-numbered:before { content: '\f0cb'; } /* '' */
|
||||
.icon-underline:before { content: '\f0cd'; } /* '' */
|
||||
.icon-table:before { content: '\f0ce'; } /* '' */
|
||||
.icon-doc-text:before { content: '\f0f6'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-folder-empty:before { content: '\f114'; } /* '' */
|
||||
.icon-code:before { content: '\f121'; } /* '' */
|
||||
.icon-superscript:before { content: '\f12b'; } /* '' */
|
||||
.icon-youtube-play:before { content: '\f16a'; } /* '' */
|
||||
.icon-header:before { content: '\f1dc'; } /* '' */
|
||||
.icon-paragraph:before { content: '\f1dd'; } /* '' */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user