1
0
mirror of https://github.com/typemill/typemill.git synced 2025-01-17 05:18:19 +01:00

Version 1.2.9: Add videos and frontend forms

This commit is contained in:
Sebastian 2019-01-03 21:15:07 +01:00
parent 28b6f93a9c
commit 08ece40117
52 changed files with 1003 additions and 452 deletions

3
.gitignore vendored
View File

@ -5,9 +5,12 @@ plugins/demo
plugins/disqus
plugins/download
plugins/finalwords
plugins/joblistings
plugins/mail
plugins/textadds
plugins/version
settings/settings.yaml
settings/formdata.yaml
settings/users
system/vendor
tests

2
cache/lastCache.txt vendored
View File

@ -1 +1 @@
1544479161
1546546314

View File

@ -1 +0,0 @@
["# Add Title","Add Content"]

View File

@ -1,16 +0,0 @@
# Stellenangebot veröffentlichen
CMSstash erreicht pro Monat mehrere tausend CMS-Experten in ganz Deutschland. Detaillierte Angaben finden sie in den [Mediadaten](/mediadaten). Unternehmen können diese Reichweite nutzen und ihre Stellenanzeigen mit CMS-Bezug auf CMSstash veröffentlichen. Wir bieten folgende Konditionen an:
| Kondition | Anzeige Standard | Anzeige Pro |
|-----------|-----------|----------|
| Laufzeit | 8 Wochen | 8 Wochen |
| Exklusiver Teaser auf allen Seiten | Nein | 4 Wochen |
| Tweets | 1 Tweet | 3 Tweets |
| __Preis__ | **59,- Euro** | **159,- Euro** |
| _(keine Ausweisung der Mwst. nach § 19 UStG)_ | | |
## Stellenanzeige aufgeben
Bei Interesse schreiben Sie uns gerne über [jobs@cmsstash.de](mailto:jobs@cmsstash.de) an oder nutzen Sie das Kontaktformular. Aufgrund der inhaltlichen Ausrichtung können wir nur Stellenangebote mit einem klaren Bezug zum Thema Content Management Systeme veröffentlichen.

View File

@ -1,4 +0,0 @@
# Add Title
Add Content

View File

@ -6,70 +6,168 @@ use \Typemill\Plugin;
class ContactForm extends Plugin
{
protected $item;
protected $originalHtml;
protected $settings;
protected $pluginSettings;
protected $originalHtml;
public static function getSubscribedEvents()
{
return array(
'onSessionSegmentsLoaded' => 'onSessionSegmentsLoaded',
'onOriginalLoaded' => 'onOriginalLoaded',
'onHtmlLoaded' => 'onHtmlLoaded',
);
}
# add the path for session and csrf-protection
# add the path stored in user-settings to initiate session and csrf-protection
public function onSessionSegmentsLoaded($segments)
{
$this->pluginSettings = $this->getPluginSettings('contactform');
if($this->getPath() == $this->pluginSettings['page'])
if($this->getPath() == $this->pluginSettings['page_value'])
{
$data = $segments->getData();
$data[] = $this->pluginSettings['page'];
$data = $segments->getData();
$data[] = $this->pluginSettings['page_value'];
$segments->setData($data);
}
}
# get the original html without manipulations
public function onOriginalLoaded($original)
# create the output
public function onHtmlLoaded($html)
{
if(substr($this->getPath(), 0, strlen($this->pluginSettings['area'])) === $this->pluginSettings['area'])
if($this->getPath() == $this->pluginSettings['page_value'])
{
$content = $html->getData();
# add css
$this->addCSS('/contactform/css/contactform.css');
# check if form data have been stored
$formdata = $this->getFormdata('contactform');
if($formdata)
{
# process the form-data
$sendmail = $this->sendMail($formdata);
if($sendmail)
{
$mailresult = '<div class="mailresult">' . $this->markdownToHtml($this->pluginSettings['message_success']) . '</div>';
}
else
{
$mailresult = '<div class="mailresult">' . $this->markdownToHtml($this->pluginSettings['message_error']) . '</div>';
}
# add thank you to the content
$content = $content . $mailresult;
}
else
{
# get the public forms for the plugin
$contactform = $this->generateForm('contactform');
# add forms to the content
$content = $content . $contactform;
}
$html->setData($content);
}
}
private function sendMail($formdata)
{
$mailto = $this->pluginSettings['mailto'];
$subject = $formdata['subject'];
$message = wordwrap($formdata['message'],70);
$header = 'From: ' . $formdata['email'] . "\r\n" .
'Reply-To: ' . $formdata['email'] . "\r\n" .
'X-Mailer: PHP/' . phpversion();
return @mail($mailto, $subject, $message, $header);
}
# check if the mail-plugin is active
/*
public function onTwigLoaded()
{
$this->settings = $this->getSettings();
if(isset($this->settings['plugins']['mail']) OR !$this->settings['plugins']['mail']['active'])
{
$this->container->flash->addMessage('error', 'You have to activate the mail-plugin to use the contactform.');
}
}
*/
# get the original html without manipulations from other plugins
/*
public function onOriginalLoaded($original)
{
if(substr($this->getPath(), 0, strlen($this->pluginSettings['area_value'])) === $this->pluginSettings['area_value'])
{
$this->originalHtml = $original->getHTML($urlrel = false);
}
}
*/
# create the output
/*
public function onHtmlLoaded($html)
{
if(substr($this->getPath(), 0, strlen($this->pluginSettings['area'])) === $this->pluginSettings['area'])
{
if(substr($this->getPath(), 0, strlen($this->pluginSettings['area_value'])) === $this->pluginSettings['area_value'])
{
$content = $this->originalHtml;
if($this->getPath() == $this->pluginSettings['page'])
{
$this->generateForm('contactform');
if($this->getPath() == $this->pluginSettings['page_value'])
{
# add css
# $this->addCSS('/textadds/css/textadds.css');
# 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 and render it with twig
$contactform = $twig->fetch('/contactform.twig', $this->pluginSettings);
$this->addCSS('/contactform/css/contactform.css');
# check if form data have been stored
$formdata = $this->getFormdata('contactform');
$content = $this->originalHtml . $contactform;
if($formdata)
{
# process the form-data
$sendmail = $this->sendMail($formdata);
if($sendmail)
{
$mailresult = '<div class="mailresult"><h2>Thank you!</h2><p>Your Message has been send.</p></div>';
}
else
{
$mailresult = '<div class="mailresult"><h2>Error</h2><p>We could not send your message. Please send the mail manually.</p></div>';
}
/*
print_r($formdata);
die();
$mail = $this->container->mail->send('mail/welcome.twig', ['user' => $user, 'url' => $host], function($message) use ($user){
$message->to($user['email']);
$message->subject('Dein Account bei der Regierungsmannschaft');
});
if(!$mail)
{
$this->container->flash->addMessage('error', 'Die Bestätigungsmail konnte nicht verschickt werden.');
}
# create thank you page
# add thank you to the content
$content = $this->originalHtml . $mailresult;
}
else
{
# get the public forms for the plugin
$contactform = $this->generateForm('contactform');
# add forms to the content
$content = $this->originalHtml . $contactform;
}
}
$html->setData($content);
$html->stopPropagation();
$html->stopPropagation();
}
}
*/
}

View File

@ -6,91 +6,89 @@ homepage: https://typemill.net
licence: MIT
settings:
page: ''
name: 'Name: '
email: 'E-Mail: '
subject: 'Subject: '
message: 'Message: '
button: 'Send Message'
message_success: "## Thank you!\r\n\r\nWe got your message and will answer as soon as possible."
message_error: "## Error\r\n\r\nWe could not send your message. Please send your e-mail manually."
forms:
fields:
page:
mailto:
type: email
label: Your e-mail-address
required: true
page_value:
type: text
label: Path to the page where to show the form
label: Path to page where to include the form
placeholder: 'path/to/page'
required: true
area:
type: text
label: Path to the area with original content
placeholder: 'path/to/rootpage'
required: true
name:
name_label:
type: text
label: Label for Name Input Field
placeholder: 'Name: '
required: true
email:
email_label:
type: text
label: Label for E-Mail-Field
placeholder: 'E-Mail: '
required: true
subject:
subject_label:
type: text
label: Label for Subject-Field
placeholder: 'Subject: '
required: true
message:
message_label:
type: text
label: Label for Message
placeholder: 'Message: '
required: true
button:
button_label:
type: text
label: Label for Button
placeholder: 'Send Message'
required: true
hint:
legalnotice:
type: textarea
rows: 5;
label: Text below button (use markdown)
placeholder: 'Add your legal text or other hints here'
frontend:
message_success:
type: textarea
rows: 5;
label: Message if mail is send (use markdown)
placeholder: 'Add your legal text or other hints here'
message_error:
type: textarea
rows: 5;
label: Message if mail failed (use markdown)
placeholder: 'Add your legal text or other hints here'
public:
fields:
name:
type: text
label: Label for Name Input Field
placeholder: 'Name: '
label: name_label
required: true
email:
type: text
label: Label for E-Mail-Field
placeholder: 'E-Mail: '
type: email
label: email_label
required: true
subject:
type: text
label: Label for Subject-Field
placeholder: 'Subject: '
label: subject_label
required: true
message:
type: text
label: Label for Message
placeholder: 'Message: '
type: textarea
label: message_label
required: true
hint:
type: textarea
label: Text below button (use markdown)
placeholder: 'Add your legal text or other hints here'
legalnotice:
type: paragraph

View File

@ -1,26 +0,0 @@
.contentadd{
display: block;
position: relative;
width: 100%;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.contentadd span{
position: absolute;
right: 10px;
top: 20px;
text-transform: uppercase;
color: #ccc;
font-size: 0.6em;
}
.contentadd h3{
margin-top: 20px;
margin-bottom: 0px;
}
.contentadd small{
margin: 0px;
}
.contentadd p{
margin-top: 0px;
margin-bottom: 20px;
}

View File

@ -1,40 +0,0 @@
<form method="POST" action="{{ path_for('settings.save') }}">
<fieldset>
<div class="medium{{ errors.contact.name ? ' error' : '' }}">
<label for="contact[name]">{{ name }} *</label>
<input type="text" name="contact[name]" id="name" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.contact.title ? old.contact.name : contact.name }}" />
{% if errors.contact.title %}
<span class="error">{{ errors.contact.name | first }}</span>
{% endif %}
</div>
<div class="medium{{ errors.contact.email ? ' error' : '' }}">
<label for="contact[email]">{{ email }} *</label>
<input type="text" name="contact[email]" id="email" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.contact.email ? old.contact.email : contact.email }}" />
{% if errors.contact.title %}
<span class="error">{{ errors.contact.email | first }}</span>
{% endif %}
</div>
<div class="medium{{ errors.contact.subject ? ' error' : '' }}">
<label for="contact[email]">{{ subject }} *</label>
<input type="text" name="contact[subject]" id="subject" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.contact.subject ? old.contact.subject : contact.subject }}" />
{% if errors.contact.title %}
<span class="error">{{ errors.contact.email | first }}</span>
{% endif %}
</div>
<div class="medium{{ errors.contact.message ? ' error' : '' }}">
<label for="contact[email]">{{ message }} *</label>
<input type="textfield" name="contact[message]" id="message" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.contact.message ? old.contact.message : contact.message }}" />
{% if errors.contact.title %}
<span class="error">{{ errors.contact.message | first }}</span>
{% endif %}
</div>
<small>{{ hint }}</small>
</fieldset>
<input type="submit" value="{{ button }}" />
{{ csrf_field() | raw }}
</form>

View File

@ -856,7 +856,7 @@ class ContentApiController extends ContentController
$imageProcessor = new ProcessImage();
if($imageProcessor->createImage($this->params['image'], $this->settings['images'], $name = false))
if($imageProcessor->createImage($this->params['image'], $this->settings['images']))
{
return $response->withJson(array('errors' => false));
}
@ -867,7 +867,7 @@ class ContentApiController extends ContentController
public function publishImage(Request $request, Response $response, $args)
{
$params = $request->getParsedBody();
$imageProcessor = new ProcessImage();
$imageUrl = $imageProcessor->publishImage($this->settings['images'], $name = false);
@ -882,4 +882,72 @@ class ContentApiController extends ContentController
return $response->withJson(array('errors' => 'could not store image to temporary folder'));
}
public function saveVideoImage(Request $request, Response $response, $args)
{
/* get params from call */
$this->params = $request->getParams();
$this->uri = $request->getUri();
$class = false;
$imageUrl = $this->params['markdown'];
if(strpos($imageUrl, 'https://www.youtube.com/watch?v=') !== false)
{
$videoID = str_replace('https://www.youtube.com/watch?v=', '', $imageUrl);
$videoID = strpos($videoID, '&') ? substr($videoID, 0, strpos($videoID, '&')) : $videoID;
$class = 'youtube';
}
if(strpos($imageUrl, 'https://youtu.be/') !== false)
{
$videoID = str_replace('https://youtu.be/', '', $imageUrl);
$videoID = strpos($videoID, '?') ? substr($videoID, 0, strpos($videoID, '?')) : $videoID;
$class = 'youtube';
}
if($class == 'youtube')
{
$videoURLmaxres = 'https://i1.ytimg.com/vi/' . $videoID . '/maxresdefault.jpg';
$videoURL0 = 'https://i1.ytimg.com/vi/' . $videoID . '/0.jpg';
}
$ctx = stream_context_create(array(
'https' => array(
'timeout' => 1
)
)
);
$imageData = @file_get_contents($videoURLmaxres, 0, $ctx);
if($imageData === false)
{
$imageData = @file_get_contents($videoURL0, 0, $ctx);
if($imageData === false)
{
return $response->withJson(array('errors' => 'could not get the video image'));
}
}
$imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData);
$desiredSizes = ['live' => ['width' => 560, 'height' => 315]];
$imageProcessor = new ProcessImage();
$tmpImage = $imageProcessor->createImage($imageData64, $desiredSizes);
if(!$tmpImage)
{
return $response->withJson(array('errors' => 'could not create temporary image'));
}
$imageUrl = $imageProcessor->publishImage($desiredSizes, $videoID);
if($imageUrl)
{
$this->params['markdown'] = '![' . $class . '-video](' . $imageUrl . ' "click to load video"){#' . $videoID. ' .' . $class . '}';
$request = $request->withParsedBody($this->params);
return $this->updateBlock($request, $response, $args);
}
return $response->withJson(array('errors' => 'could not store the preview image'));
}
}

View File

@ -0,0 +1,106 @@
<?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']);
}
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!');
}
}
}
}
}

View File

@ -137,8 +137,10 @@ class PageController extends Controller
/* 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, $item->urlRel);
$contentHTML = $parsedown->markup($contentArray, $itemUrl);
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
/* extract the h1 headline*/

View File

@ -121,7 +121,7 @@ class SettingsController extends Controller
$user = new User();
$users = $user->getUsers();
$route = $request->getAttribute('route');
$this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
}
@ -241,7 +241,7 @@ class SettingsController extends Controller
$pluginSettings = array();
$userInput = $request->getParams();
$validate = new Validation();
/* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */
foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings)
{
@ -323,7 +323,7 @@ class SettingsController extends Controller
if(!$fieldDefinition && $fieldName != 'active')
{
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
}
}
}
}
}

View File

@ -19,6 +19,6 @@ class TwigMarkdownExtension extends \Twig_Extension
$markdownArray = $parsedown->text($markdown);
return $parsedown->markup($markdownArray);
return $parsedown->markup($markdownArray, false);
}
}

View File

@ -44,7 +44,8 @@ class Field
'url',
'week',
'textarea',
'select'
'select',
'paragraph'
);
/* defines all boolean attributes, that are allowed for fields */
@ -80,20 +81,17 @@ class Field
{
$this->setName($fieldName);
if(isset($fieldConfigs['type']))
{
$this->setType($fieldConfigs['type']);
}
$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);
if(isset($fieldConfigs['label']))
{
$this->setLabel($fieldConfigs['label']);
}
if(isset($fieldConfigs['options']))
{
$this->setOptions($fieldConfigs['options']);
}
$options = isset($fieldConfigs['options']) ? $fieldConfigs['options'] : array();
$this->setOptions($options);
$this->setAttributes($fieldConfigs);
@ -125,7 +123,7 @@ class Field
return $this->type;
}
private function setLabel($label)
public function setLabel($label)
{
$this->label = $label;
}
@ -134,6 +132,16 @@ class Field
{
return $this->label;
}
public function setCheckboxLabel($label)
{
$this->checkboxLabel = $label;
}
public function getCheckboxLabel()
{
return $this->checkboxLabel;
}
public function setContent($content)
{
@ -177,7 +185,7 @@ class Field
public function getAttributes()
{
$string = false;
foreach($this->attributes as $key => $attribute)
{
$string .= ' ' . $key;

View File

@ -7,13 +7,13 @@ use Typemill\Models\Field;
class Fields
{
public function getFields($userSettings, $objectType, $objectName, $objectSettings, $formType = false)
{
{
# hold all fields in array
$fields = array();
# formtype are backendforms or frontendforms, only relevant for plugins for now
# 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)
{
@ -56,7 +56,7 @@ class Fields
}
# Now prepopulate the field object with the value */
if($field->getType() == "textarea")
if($field->getType() == "textarea" || $field->getType() == "paragraph")
{
if($userValue)
{
@ -66,25 +66,26 @@ class Fields
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]))
if(!isset($userSettings[$objectType][$objectName][$fieldName]))
{
$field->setAttribute('checked', 'checked');
}
else
{
$field->unsetAttribute('checked');
$field->unsetAttribute('checked');
}
}
else
{
$field->setAttributeValue('value', $userValue);
}
if(isset($fieldConfigurations['label']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['label']]))
{
$field->setLabel($userSettings[$objectType][$objectName][$fieldConfigurations['label']]);
}
# add the field to the field-List
$fields[] = $field;
}
}
}
return $fields;
}
}

View File

@ -3,7 +3,7 @@ namespace Typemill\Models;
class ProcessImage
{
public function createImage(string $image, array $desiredSizes, $name)
public function createImage(string $image, array $desiredSizes)
{
# fix error from jpeg-library
ini_set ('gd.jpeg_ignore_warning', 1);
@ -60,7 +60,7 @@ class ProcessImage
return false;
}
public function publishImage(array $desiredSizes)
public function publishImage(array $desiredSizes, $name = false)
{
/* get images from tmp folder */
$basePath = getcwd() . DIRECTORY_SEPARATOR . 'media';
@ -71,7 +71,7 @@ class ProcessImage
if(!file_exists($originalFolder)){ mkdir($originalFolder, 0774, true); }
if(!file_exists($liveFolder)){ mkdir($liveFolder, 0774, true); }
$name = uniqid();
$name = $name ? $name : uniqid();
$files = scandir($tmpFolder);
$success = true;

View File

@ -312,7 +312,6 @@ class Validation
{
$v = new Validator(array($fieldName => $fieldValue));
if(isset($fieldDefinitions['required']))
{
$v->rule('required', $fieldName);
@ -364,11 +363,18 @@ class Validation
$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);
}

View File

@ -4,6 +4,8 @@ namespace Typemill;
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Typemill\Models\Fields;
use Typemill\Models\WriteYaml;
use Typemill\Extensions\ParsedownExtension;
abstract class Plugin implements EventSubscriberInterface
{
@ -65,7 +67,7 @@ abstract class Plugin implements EventSubscriberInterface
$function = new \Twig_SimpleFunction($name, $function);
$this->container->view->getEnvironment()->addFunction($function);
}
protected function addJS($JS)
{
$this->container->assets->addJS($JS);
@ -86,19 +88,63 @@ abstract class Plugin implements EventSubscriberInterface
$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];
}
}
return false;
}
protected function generateForm($pluginName)
{
$fieldsModel = new Fields();
$pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
if(isset($pluginDefinitions['frontend']['fields']))
$pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
$settings = $this->getSettings();
$buttonlabel = isset($settings['plugins'][$pluginName]['button_label']) ? $settings['plugins'][$pluginName]['button_label'] : false;
if(isset($pluginDefinitions['public']['fields']))
{
# add simple honeypot spam protection
$pluginDefinitions['public']['fields']['personal-mail'] = ['type' => 'text', 'class' => 'personal-mail'];
/*
# add spam protection questions
$spamanswers = ['Albert', 'kalt', 'Gelb'];
shuffle($spamanswers);
$pluginDefinitions['public']['fields']['spamquestion'] = ['type' => 'checkboxlist', 'label' => 'Der Vorname von Einstein lautet', 'required' => true, 'options' => $spamanswers];
*/
# get all the fields and prefill them with the dafault-data, the user-data or old input data
$fields = $fieldsModel->getFields($userSettings = false, 'plugins', $pluginName, $pluginDefinitions, 'frontend');
$fields = $fieldsModel->getFields($settings, 'plugins', $pluginName, $pluginDefinitions, 'public');
# use the field-objects to generate the html-fields
# 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', 'buttonlabel' => $buttonlabel]);
}
return $form;
}
}

View File

@ -21,4 +21,6 @@ $app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->set
$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->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']));

View File

@ -1,6 +1,7 @@
<?php
use Typemill\Controllers\PageController;
use Typemill\Controllers\FormController;
use Typemill\Controllers\SetupController;
use Typemill\Controllers\AuthController;
use Typemill\Controllers\SettingsController;
@ -27,6 +28,8 @@ 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']));

View File

@ -88,7 +88,6 @@ class Settings
if($userSettings)
{
$yaml = new Models\WriteYaml();
$settings = array_merge($userSettings, $settings);

View File

@ -1,15 +1,6 @@
Font license info
## Entypo
Copyright (C) 2012 by Daniel Bruce
Author: Daniel Bruce
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://www.entypo.com
## Font Awesome
Copyright (C) 2016 by Dave Gandy
@ -19,3 +10,12 @@ Font license info
Homepage: http://fortawesome.github.com/Font-Awesome/
## Entypo
Copyright (C) 2012 by Daniel Bruce
Author: Daniel Bruce
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://www.entypo.com

View File

@ -7,10 +7,10 @@
"ascent": 850,
"glyphs": [
{
"uid": "c709da589c923ba3c2ad48d9fc563e93",
"css": "cancel",
"uid": "0f99ab40ab0b4d64a74f2d0deeb03e42",
"css": "videocam",
"code": 59392,
"src": "entypo"
"src": "fontawesome"
},
{
"uid": "381da2c2f7fd51f8de877c044d7f439d",
@ -18,34 +18,46 @@
"code": 59393,
"src": "fontawesome"
},
{
"uid": "c709da589c923ba3c2ad48d9fc563e93",
"css": "cancel",
"code": 59394,
"src": "entypo"
},
{
"uid": "f9cbf7508cd04145ade2800169959eef",
"css": "font",
"code": 59394,
"code": 59395,
"src": "fontawesome"
},
{
"uid": "e99461abfef3923546da8d745372c995",
"css": "cog",
"code": 59395,
"code": 59396,
"src": "fontawesome"
},
{
"uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
"css": "off",
"code": 59396,
"code": 59397,
"src": "fontawesome"
},
{
"uid": "d7271d490b71df4311e32cdacae8b331",
"css": "home",
"code": 59397,
"code": 59398,
"src": "fontawesome"
},
{
"uid": "44e04715aecbca7f266a17d5a7863c68",
"css": "plus",
"code": 59398,
"code": 59399,
"src": "fontawesome"
},
{
"uid": "e15f0d620a7897e2035c18c80142f6d9",
"css": "link-ext",
"code": 61582,
"src": "fontawesome"
},
{
@ -55,9 +67,9 @@
"src": "fontawesome"
},
{
"uid": "e15f0d620a7897e2035c18c80142f6d9",
"css": "link-ext",
"code": 61582,
"uid": "6605ee6441bf499ffa3c63d3c7409471",
"css": "move",
"code": 61511,
"src": "fontawesome"
},
{
@ -71,6 +83,12 @@
"css": "folder-empty",
"code": 61716,
"src": "fontawesome"
},
{
"uid": "872d9516df93eb6b776cc4d94bd97dac",
"css": "video",
"code": 59400,
"src": "fontawesome"
}
]
}

View File

@ -1,11 +1,14 @@
.icon-cancel:before { content: '\e800'; } /* '' */
.icon-videocam:before { content: '\e800'; } /* '' */
.icon-picture:before { content: '\e801'; } /* '' */
.icon-font:before { content: '\e802'; } /* '' */
.icon-cog:before { content: '\e803'; } /* '' */
.icon-off:before { content: '\e804'; } /* '' */
.icon-home:before { content: '\e805'; } /* '' */
.icon-plus:before { content: '\e806'; } /* '' */
.icon-cancel:before { content: '\e802'; } /* '' */
.icon-font:before { content: '\e803'; } /* '' */
.icon-cog:before { content: '\e804'; } /* '' */
.icon-off:before { content: '\e805'; } /* '' */
.icon-home:before { content: '\e806'; } /* '' */
.icon-plus:before { content: '\e807'; } /* '' */
.icon-video:before { content: '\e808'; } /* '' */
.icon-move:before { content: '\f047'; } /* '' */
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
.icon-doc-text:before { content: '\f0f6'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,14 @@
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-videocam { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

View File

@ -10,13 +10,16 @@
/* font-size: 120%; */
}
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-videocam { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

View File

@ -1,11 +1,11 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?52515879');
src: url('../font/fontello.eot?52515879#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?52515879') format('woff2'),
url('../font/fontello.woff?52515879') format('woff'),
url('../font/fontello.ttf?52515879') format('truetype'),
url('../font/fontello.svg?52515879#fontello') format('svg');
src: url('../font/fontello.eot?80940798');
src: url('../font/fontello.eot?80940798#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?80940798') format('woff2'),
url('../font/fontello.woff?80940798') format('woff'),
url('../font/fontello.ttf?80940798') format('truetype'),
url('../font/fontello.svg?80940798#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -15,7 +15,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?52515879#fontello') format('svg');
src: url('../font/fontello.svg?80940798#fontello') format('svg');
}
}
*/
@ -55,13 +55,16 @@
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-cancel:before { content: '\e800'; } /* '' */
.icon-videocam:before { content: '\e800'; } /* '' */
.icon-picture:before { content: '\e801'; } /* '' */
.icon-font:before { content: '\e802'; } /* '' */
.icon-cog:before { content: '\e803'; } /* '' */
.icon-off:before { content: '\e804'; } /* '' */
.icon-home:before { content: '\e805'; } /* '' */
.icon-plus:before { content: '\e806'; } /* '' */
.icon-cancel:before { content: '\e802'; } /* '' */
.icon-font:before { content: '\e803'; } /* '' */
.icon-cog:before { content: '\e804'; } /* '' */
.icon-off:before { content: '\e805'; } /* '' */
.icon-home:before { content: '\e806'; } /* '' */
.icon-plus:before { content: '\e807'; } /* '' */
.icon-video:before { content: '\e808'; } /* '' */
.icon-move:before { content: '\f047'; } /* '' */
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
.icon-doc-text:before { content: '\f0f6'; } /* '' */

View File

@ -229,11 +229,11 @@ body {
}
@font-face {
font-family: 'fontello';
src: url('./font/fontello.eot?74591350');
src: url('./font/fontello.eot?74591350#iefix') format('embedded-opentype'),
url('./font/fontello.woff?74591350') format('woff'),
url('./font/fontello.ttf?74591350') format('truetype'),
url('./font/fontello.svg?74591350#fontello') format('svg');
src: url('./font/fontello.eot?44201991');
src: url('./font/fontello.eot?44201991#iefix') format('embedded-opentype'),
url('./font/fontello.woff?44201991') format('woff'),
url('./font/fontello.ttf?44201991') format('truetype'),
url('./font/fontello.svg?44201991#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -298,19 +298,24 @@ body {
</div>
<div class="container" id="icons">
<div class="row">
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-cancel">&#xe800;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe800</span></div>
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-videocam">&#xe800;</i> <span class="i-name">icon-videocam</span><span class="i-code">0xe800</span></div>
<div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-picture">&#xe801;</i> <span class="i-name">icon-picture</span><span class="i-code">0xe801</span></div>
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-font">&#xe802;</i> <span class="i-name">icon-font</span><span class="i-code">0xe802</span></div>
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-cog">&#xe803;</i> <span class="i-name">icon-cog</span><span class="i-code">0xe803</span></div>
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-cancel">&#xe802;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe802</span></div>
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-font">&#xe803;</i> <span class="i-name">icon-font</span><span class="i-code">0xe803</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-off">&#xe804;</i> <span class="i-name">icon-off</span><span class="i-code">0xe804</span></div>
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-home">&#xe805;</i> <span class="i-name">icon-home</span><span class="i-code">0xe805</span></div>
<div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-plus">&#xe806;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe806</span></div>
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-cog">&#xe804;</i> <span class="i-name">icon-cog</span><span class="i-code">0xe804</span></div>
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-off">&#xe805;</i> <span class="i-name">icon-off</span><span class="i-code">0xe805</span></div>
<div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-home">&#xe806;</i> <span class="i-name">icon-home</span><span class="i-code">0xe806</span></div>
<div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-plus">&#xe807;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe807</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-video">&#xe808;</i> <span class="i-name">icon-video</span><span class="i-code">0xe808</span></div>
<div class="the-icons span3" title="Code: 0xf047"><i class="demo-icon icon-move">&#xf047;</i> <span class="i-name">icon-move</span><span class="i-code">0xf047</span></div>
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
<div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt">&#xf0b2;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt">&#xf0b2;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
<div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text">&#xf0f6;</i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>
<div class="the-icons span3" title="Code: 0xf114"><i class="demo-icon icon-folder-empty">&#xf114;</i> <span class="i-name">icon-folder-empty</span><span class="i-code">0xf114</span></div>
</div>

View File

@ -1,24 +1,30 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="cancel" unicode="&#xe800;" d="M452 194q18-18 18-43t-18-43q-18-16-43-16t-43 16l-132 152-132-152q-18-16-43-16t-43 16q-16 18-16 43t16 43l138 156-138 158q-16 18-16 43t16 43q18 16 43 16t43-16l132-152 132 152q18 16 43 16t43-16q18-18 18-43t-18-43l-138-158z" horiz-adv-x="470" />
<glyph glyph-name="videocam" unicode="&#xe800;" d="M1000 654v-608q0-23-22-32-7-3-14-3-15 0-25 10l-225 225v-92q0-67-47-114t-113-47h-393q-67 0-114 47t-47 114v392q0 67 47 114t114 47h393q66 0 113-47t47-114v-92l225 225q10 10 25 10 7 0 14-2 22-10 22-33z" horiz-adv-x="1000" />
<glyph glyph-name="picture" unicode="&#xe801;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="font" unicode="&#xe802;" d="M405 538l-95-251q18 0 76-1t89-1q11 0 32 1-48 141-102 252z m-405-617l1 44q13 4 31 7t32 6 28 8 25 17 17 28l132 344 156 404h72q4-8 6-12l114-268q19-43 60-144t63-153q9-19 33-80t40-94q11-26 19-32 11-9 49-17t47-11q4-22 4-32 0-3-1-8t0-7q-35 0-106 5t-107 4q-42 0-120-4t-99-4q0 24 2 43l73 16q1 0 7 1t9 2 8 3 9 4 6 4 5 6 1 8q0 9-17 54t-40 99-24 56l-251 1q-14-32-43-109t-28-91q0-12 8-21t24-14 27-7 32-5 23-2q1-11 1-32 0-5-1-16-33 0-98 6t-97 6q-5 0-15-3t-12-2q-45-8-105-8z" horiz-adv-x="928.6" />
<glyph glyph-name="cancel" unicode="&#xe802;" d="M452 194q18-18 18-43t-18-43q-18-16-43-16t-43 16l-132 152-132-152q-18-16-43-16t-43 16q-16 18-16 43t16 43l138 156-138 158q-16 18-16 43t16 43q18 16 43 16t43-16l132-152 132 152q18 16 43 16t43-16q18-18 18-43t-18-43l-138-158z" horiz-adv-x="470" />
<glyph glyph-name="cog" unicode="&#xe803;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
<glyph glyph-name="font" unicode="&#xe803;" d="M405 538l-95-251q18 0 76-1t89-1q11 0 32 1-48 141-102 252z m-405-617l1 44q13 4 31 7t32 6 28 8 25 17 17 28l132 344 156 404h72q4-8 6-12l114-268q19-43 60-144t63-153q9-19 33-80t40-94q11-26 19-32 11-9 49-17t47-11q4-22 4-32 0-3-1-8t0-7q-35 0-106 5t-107 4q-42 0-120-4t-99-4q0 24 2 43l73 16q1 0 7 1t9 2 8 3 9 4 6 4 5 6 1 8q0 9-17 54t-40 99-24 56l-251 1q-14-32-43-109t-28-91q0-12 8-21t24-14 27-7 32-5 23-2q1-11 1-32 0-5-1-16-33 0-98 6t-97 6q-5 0-15-3t-12-2q-45-8-105-8z" horiz-adv-x="928.6" />
<glyph glyph-name="off" unicode="&#xe804;" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
<glyph glyph-name="cog" unicode="&#xe804;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
<glyph glyph-name="home" unicode="&#xe805;" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
<glyph glyph-name="off" unicode="&#xe805;" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
<glyph glyph-name="plus" unicode="&#xe806;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
<glyph glyph-name="home" unicode="&#xe806;" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
<glyph glyph-name="plus" unicode="&#xe807;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
<glyph glyph-name="video" unicode="&#xe808;" d="M214-43v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="move" unicode="&#xf047;" d="M1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-25l-143-143q-10-11-25-11t-25 11l-143 143q-11 10-11 25t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-25 11l-143 143q-11 11-11 25t11 25l143 143q10 11 25 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -227,7 +227,7 @@ li.menu-item{
background: #e0474c;
color: #fff;
}
.navi-item i.icon-resize-full-alt{
.navi-item i.icon-resize-full-alt,.navi-item i.icon-move {
position: absolute;
right: 5px;
top: 7px;
@ -1618,12 +1618,15 @@ button.format-item:hover{
box-sizing:border-box;
padding: 0;
}
.dropbox input{
background: #fff;
.dropbox input, .dropbox select{
background-color: #fff;
width: 80%;
margin: 2px 0;
display: inline-block;
}
.dropbox select{
background-image: linear-gradient(45deg, transparent 50%, #444 50%), linear-gradient(135deg, #444 50%, transparent 50%), linear-gradient(to right, #fff, #fff);
}
.dropbox label{
width: 20%;
display: inline-block;
@ -1643,6 +1646,41 @@ button.format-item:hover{
margin: auto;
max-width: 100%;
}
.blox img.youtube{
position: relative;
}
.blox .video-container{
position: relative;
text-align: center;
}
.blox button.play-video {
position: absolute;
top: 50%;
margin-top: -50px;
margin-left: -50px;
height: 100px;
width: 100px;
background: #e0474c;
color: #FFFFFF;
border-radius: 50%;
border: 0px;
padding: 0;
text-align: center;
}
.blox button.play-video:hover {
background: #cc4146;
}
.blox button.play-video::after {
position: absolute;
top: 50%;
margin: -20px 0 0 -15px;
height: 0;
width: 0;
border-style: solid;
border-width: 20px 0 20px 40px;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.75);
content: ' ';
}
sup{}
cite{}
abbr{}

View File

@ -29,6 +29,7 @@
<content-block :body="false">
<button class="format-item" @click.prevent="setData( $event, 'markdown-component' )" data-id="99999" id="blox-99999"><i class="icon-font"></i></button>
<button class="format-item" @click.prevent="setData( $event, 'image-component' )" data-id="99999" id="blox-99999"><i class="icon-picture"></i></button>
<button class="format-item" @click.prevent="setData( $event, 'video-component' )" data-id="99999" id="blox-99999"><i class="icon-video"></i></button>
</content-block>
</div>

View File

@ -0,0 +1,34 @@
( function() {
var youtube = document.querySelectorAll( ".youtube" );
for (var i = 0; i < youtube.length; i++)
{
var thisyoutube = youtube[i];
thisyoutube.parentNode.classList.add("video-container");
var playbutton = document.createElement("button");
playbutton.classList.add("play-video");
playbutton.value = "Play";
thisyoutube.parentNode.appendChild(playbutton);
playbutton.addEventListener( "click", function(event)
{
event.preventDefault();
event.stopPropagation();
var iframe = document.createElement( "iframe" );
iframe.setAttribute( "frameborder", "0" );
iframe.setAttribute( "allowfullscreen", "" );
iframe.setAttribute( "width", "560" );
iframe.setAttribute( "height", "315" );
iframe.setAttribute( "src", "https://www.youtube.com/embed/" + thisyoutube.id + "?rel=0&showinfo=0&autoplay=1" );
var videocontainer = thisyoutube.parentNode
videocontainer.innerHTML = "";
videocontainer.appendChild( iframe );
})(thisyoutube);
};
} )();

View File

@ -22,7 +22,7 @@ const contentComponent = Vue.component('content-block', {
this.compmarkdown = $event;
this.$nextTick(function () {
this.$refs.preview.style.minHeight = this.$refs.component.offsetHeight + 'px';
});
});
},
switchToEditMode: function()
{
@ -35,9 +35,22 @@ const contentComponent = Vue.component('content-block', {
this.edit = true; /* show the edit-mode */
this.compmarkdown = self.$root.$data.blockMarkdown; /* get markdown data */
this.componentType = self.$root.$data.blockType; /* get block-type of element */
this.$nextTick(function () {
this.$refs.preview.style.minHeight = this.$refs.component.offsetHeight + 'px';
});
if(this.componentType == 'image-component')
{
setTimeout(function(){
self.$nextTick(function ()
{
self.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px';
});
}, 200);
}
else
{
this.$nextTick(function ()
{
this.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px';
});
}
},
closeComponents: function()
{
@ -80,7 +93,7 @@ const contentComponent = Vue.component('content-block', {
if(self.$root.$data.blockType != '')
{
this.switchToEditMode();
}
}
},
submitBlock: function(){
var emptyline = /^\s*$(?:\r\n?|\n)/gm;
@ -99,8 +112,6 @@ const contentComponent = Vue.component('content-block', {
},
saveBlock: function()
{
console.log(this.compmarkdown);
if(this.compmarkdown == undefined || this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,"") == '')
{
this.switchToPreviewMode();
@ -116,6 +127,11 @@ const contentComponent = Vue.component('content-block', {
var url = self.$root.$data.root + '/api/v1/image';
var method = 'PUT';
}
else if(this.componentType == 'video-component')
{
var url = self.$root.$data.root + '/api/v1/video';
var method = 'POST';
}
else
{
var url = self.$root.$data.root + '/api/v1/block';
@ -268,7 +284,20 @@ const titleComponent = Vue.component('title-component', {
const imageComponent = Vue.component('image-component', {
props: ['compmarkdown', 'disabled'],
template: '<div class="dropbox"><input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"><input type="file" name="image" accept="image/*" class="input-file" @change="onFileChange( $event )"><p>drag a picture or click to select</p><img class="uploadPreview" :src="imgpreview" /><div v-if="load" class="loadwrapper"><span class="load"></span></div><div class="imgmeta" v-if="imgmeta"><label for="imgalt">Alt-Text: </label><input name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100"/><label for="imgtitle">Title: </label><input name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" /><label for="imgcaption">Caption: </label><input title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" /><label for="imgurl">Link: </label><input title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" /></div></div>',
template: '<div class="dropbox">' +
'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +
'<input type="file" name="image" accept="image/*" class="input-file" @change="onFileChange( $event )" /> ' +
'<p>drag a picture or click to select</p>' +
'<img class="uploadPreview" :src="imgpreview" />' +
'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
'<div class="imgmeta" v-if="imgmeta">' +
'<label for="imgalt">Alt-Text: </label><input name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />' +
'<label for="imgtitle">Title: </label><input name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />' +
'<label for="imgcaption">Caption: </label><input title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />' +
'<label for="imgurl">Link: </label><input title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />' +
'<label for="imgclass">Class: </label><select title="imgclass" v-model="imgclass" @change="createmarkdown"><option value="center">Center</option><option value="left">Left</option><option value="right">Right</option><option value="youtube">Youtube</option><option value="vimeo">Vimeo</option></select>' +
'<input title="imgid" type="hidden" placeholder="id" v-model="imgid" @input="createmarkdown" max="140" />' +
'</div></div>',
data: function(){
return {
maxsize: 5, // megabyte
@ -279,19 +308,21 @@ const imageComponent = Vue.component('image-component', {
imgtitle: '',
imgcaption: '',
imglink: '',
imgclass: 'center',
imgid: '',
imgfile: 'imgplchldr',
}
},
mounted: function(){
// autosize(document.querySelector('textarea'));
this.$refs.markdown.focus();
this.$refs.markdown.focus();
if(this.compmarkdown)
{
{
this.imgmeta = true;
var imgmarkdown = this.compmarkdown;
var imgcaption = imgmarkdown.match(/\*.*?\*/);
if(imgcaption){
this.imgcaption = imgcaption[0].slice(1,-1);
@ -322,6 +353,25 @@ const imageComponent = Vue.component('image-component', {
{
this.imgalt = imgalt[0].slice(1,-1);
}
var imgattr = imgmarkdown.match(/\{.*?\}/);
if(imgattr)
{
imgattr = imgattr[0].slice(1,-1);
imgattr = imgattr.split(' ');
for (var i = 0; i < imgattr.length; i++)
{
if(imgattr[i].charAt(0) == '.')
{
this.imgclass = imgattr[i].slice(1);
}
else if(imgattr[i].charAt(0) == '#')
{
this.imgid = imgattr[i].slice(1);
}
}
}
var imgpreview = imgmarkdown.match(/\(.*?\)/);
if(imgpreview)
{
@ -331,13 +381,20 @@ const imageComponent = Vue.component('image-component', {
}
},
methods: {
isChecked: function(classname)
{
if(this.imgclass == classname)
{
return ' checked';
}
},
updatemarkdown: function(event)
{
this.$emit('updatedMarkdown', event.target.value);
},
createmarkdown: function()
{
errors = false;
var errors = false;
if(this.imgalt.length < 101)
{
@ -365,6 +422,34 @@ const imageComponent = Vue.component('image-component', {
imgmarkdown = imgmarkdown + '(' + this.imgfile + ')';
}
var imgattr = '';
if(this.imgid != '')
{
if(this.imgid.length < 100)
{
imgattr = imgattr + '#' + this.imgid + ' ';
}
else
{
errors = 'Maximum size of image id is 100 characters';
}
}
if(this.imgclass != '')
{
if(this.imgclass.length < 100)
{
imgattr = imgattr + '.' + this.imgclass;
}
else
{
errors = 'Maximum size of image class is 100 characters';
}
}
if(this.imgid != '' || this.imgclass != '')
{
imgmarkdown = imgmarkdown + '{' + imgattr + '}';
}
if(this.imglink != '')
{
if(this.imglink.length < 101)
@ -376,7 +461,7 @@ const imageComponent = Vue.component('image-component', {
errors = 'Maximum size of image link is 100 characters';
}
}
if(this.imgcaption != '')
{
if(this.imgcaption.length < 140)
@ -388,7 +473,7 @@ const imageComponent = Vue.component('image-component', {
errors = 'Maximum size of image caption is 140 characters';
}
}
if(errors)
{
this.$parent.freezePage();
@ -472,6 +557,20 @@ const imageComponent = Vue.component('image-component', {
}
})
const videoComponent = Vue.component('video-component', {
props: ['compmarkdown', 'disabled', 'load'],
template: '<div class="video dropbox">' +
'<label for="video">Link to video: </label><input type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown">' +
'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
'</div>',
methods: {
updatemarkdown: function(event)
{
this.$emit('updatedMarkdown', event.target.value);
},
},
})
const tableComponent = Vue.component('table', {
template: '<div>table component</div>',
})

View File

@ -19,7 +19,7 @@
<link rel="stylesheet" href="{{ base_url }}/system/author/css/fontello/css/fontello.css" />
<link rel="stylesheet" href="{{ base_url }}/system/author/css/normalize.css" />
<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css?20181206" />
<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css?20190103" />
<link rel="stylesheet" href="{{ base_url }}/system/author/css/color-picker.min.css" />
</head>
<body>
@ -36,13 +36,14 @@
</article>
<footer></footer>
</div>
<script src="{{ base_url }}/system/author/js/vue.min.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/autosize.min.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/sortable.min.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/vuedraggable.min.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/author.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/vue-publishcontroller.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/vue-blox.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/vue-navi.js?20181206"></script>
<script src="{{ base_url }}/system/author/js/vue.min.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/autosize.min.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/sortable.min.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/vuedraggable.min.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/author.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/vue-publishcontroller.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/vue-blox.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/vue-navi.js?20190103"></script>
<script src="{{ base_url }}/system/author/js/lazy-video.js?20190103"></script>
</body>
</html>

View File

@ -42,7 +42,7 @@
{% verbatim %}
<template id="navigation-template">
<li class="navi-item" :class="elementtype"><a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><i :class="getIcon(elementtype, filetype)"></i><span :class="getLevel(level)">{{ name }}</span><i class="icon-resize-full-alt"></i></a>
<li class="navi-item" :class="elementtype"><a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><i :class="getIcon(elementtype, filetype)"></i><span :class="getLevel(level)">{{ name }}</span><i class="icon-move"></i></a>
<draggable v-if="folder" :element="'ul'" class="navi-list" :list="folder" :move="checkMove" @start="onStart" @end="onEnd" :options="{group:{ name:'file'}, animation: 150, 'disabled': freeze }">
<navigation ref="draggit" v-for="item in folder" :freeze="freeze" :name="item.name" :active="item.active" :parent="item.activeParent" :level="item.keyPath" :url="item.urlRelWoF" :root="root" v-bind:id="item.keyPath" :key="item.keyPath" :filetype="item.fileType" :elementtype="item.elementType" :folder="item.folderContent"></navigation>
</draggable>

View File

@ -0,0 +1,73 @@
<div class="cardField{{ errors[itemName][field.name] ? ' error' : '' }}">
<label for="{{ itemName }}[{{ field.name }}]">{{ field.getLabel() }}
{% if field.getAttribute('required') %}<strong><abbr title="required">*</abbr></strong>{% endif %}
{% if field.help %}<div class="help">?<span class="tooltip">{{field.help|slice(0,100)}}</span></div>{% endif %}
</label>
{% if field.type == 'textarea' %}
<textarea name="{{ itemName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
{% elseif field.type == 'paragraph' %}
{{ markdown(field.getContent()) }}
{% elseif field.type == 'checkbox' %}
{{ field.getAttributes }}
<label class="control-group">{{ field.getCheckboxLabel() }}
<input type="checkbox" name="{{ itemName}}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
<span class="checkmark"></span>
</label>
{% elseif field.type == 'checkboxlist' %}
{% set options = field.getOptions() %}
{% for value,label in options %}
<label class="control-group">{{ label }}
<input type="checkbox" name="{{ itemName }}[{{ field.name }}][{{value}}]" {{ settings[object][itemName][field.name][value] ? ' checked' : '' }}>
<span class="checkmark"></span>
</label>
{% endfor %}
{% elseif field.type == 'select' %}
{% set options = field.getOptions() %}
<select name="{{ itemName }}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
{% for value,label in options %}
<option value="{{ value }}" {{ (value == field.getAttributeValue('value')) ? ' selected' : '' }}>{{ label }}</option>
{% endfor %}
</select>
{% elseif field.type == 'radio' %}
{% set options = field.getOptions() %}
{% for value,label in options %}
<label class="control-group">{{ label }}
<input type="radio" name="{{ itemName }}[{{ field.name }}]" value="{{ value }}" {{ (value == settings[object][itemName][field.name]) ? ' checked' : '' }}>
<span class="radiomark"></span>
</label>
{% endfor %}
{% else %}
<input name="{{itemName}}[{{ field.name }}]" type="{{ field.type }}"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
{% endif %}
{% if field.description %}<div class="description">{{field.description}}</div>{% endif %}
{% if errors[itemName][field.name] %}
<span class="error">{{ errors[itemName][field.name] | first }}</span>
{% endif %}
</div>

View File

@ -0,0 +1,32 @@
<form method="POST" action="{{ path_for('form.save') }}">
<fieldset class="card{{ errors[itemName] ? ' errors' : '' }}">
{% for field in fields %}
{% if field.type == 'fieldset' %}
<fieldset class="subfield">
<legend>{{ field.legend }}</legend>
{% for field in field.fields %}
{% include '/partials/fields.twig' with {'itemName' : itemName, 'object' : object } %}
{% endfor %}
</fieldset>
{% else %}
{% include '/partials/fields.twig' with {'itemName' : itemName, 'object' : object } %}
{% endif %}
{% endfor %}
{{ csrf_field() | raw }}
<input type="submit" value="{{ buttonlabel ? buttonlabel : 'send' }}" />
<style>.personal-mail{display:none}</style>
</fieldset>
</form>

View File

@ -1,66 +0,0 @@
<div class="cardField{{ errors[itemName][field.name] ? ' error' : '' }}">
<label for="{{ itemName }}[{{ field.name }}]">{{ field.getLabel() }}
{% if field.getAttribute('required') %}<strong><abbr title="required">*</abbr></strong>{% endif %}
{% if field.help %}<div class="help">?<span class="tooltip">{{field.help|slice(0,100)}}</span></div>{% endif %}
</label>
{% if field.type == 'textarea' %}
<textarea name="{{ itemName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
{% elseif field.type == 'checkbox' %}
<label class="control-group">{{ field.description }}
<input type="checkbox" name="{{ itemName}}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
<span class="checkmark"></span>
</label>
{% elseif field.type == 'checkboxlist' %}
{% set options = field.getOptions() %}
{% for value,label in options %}
<label class="control-group">{{ label }}
<input type="checkbox" name="{{ itemName }}[{{ field.name }}][{{value}}]" {{ settings[object][itemName][field.name][value] ? ' checked' : '' }}>
<span class="checkmark"></span>
</label>
{% endfor %}
{% elseif field.type == 'select' %}
{% set options = field.getOptions() %}
<select name="{{ itemName }}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
{% for value,label in options %}
<option value="{{ value }}" {{ (value == field.getAttributeValue('value')) ? ' selected' : '' }}>{{ label }}</option>
{% endfor %}
</select>
{% elseif field.type == 'radio' %}
{% set options = field.getOptions() %}
{% for value,label in options %}
<label class="control-group">{{ label }}
<input type="radio" name="{{ itemName }}[{{ field.name }}]" value="{{ value }}" {{ (value == settings[object][itemName][field.name]) ? ' checked' : '' }}>
<span class="radiomark"></span>
</label>
{% endfor %}
{% else %}
<input name="{{itemName}}[{{ field.name }}]" type="{{ field.type }}"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
{% endif %}
{% if errors[itemName][field.name] %}
<span class="error">{{ errors[itemName][field.name] | first }}</span>
{% endif %}
</div>

View File

@ -43,7 +43,7 @@
{% for field in plugin.forms.fields %}
{% include '/partials/forms.twig' with {'itemName' : pluginName, 'object' : 'plugins' } %}
{% include '/partials/fields.twig' with {'itemName' : pluginName, 'object' : 'plugins' } %}
{% endfor %}

View File

@ -50,13 +50,13 @@
<fieldset class="subfield">
<legend>{{ field.legend }}</legend>
{% for field in field.fields %}
{% include '/partials/forms.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% include '/partials/fields.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% endfor %}
</fieldset>
{% else %}
{% include '/partials/forms.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% include '/partials/fields.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% endif %}
@ -70,7 +70,7 @@
<button type="button" class="theme-button fc-settings{{ (settings.theme == themeName) ? ' active' : '' }}{{ theme.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ theme.forms.fields|length > 0 ? 'Settings <span class="button-arrow"></span>' : 'No Settings'}}</button>
</div>
<div class="medium">
<input type="submit" value="Save Theme" />
<input type="submit" value="Save Theme" />
</div>
</div>
</div>
@ -85,8 +85,6 @@
</section>
</form>
</div>
{% endblock %}

View File

@ -1,7 +1,11 @@
<div class="chapter">
<div class="chapterNumber">{{ settings.themes.typemill.chapter ? settings.themes.typemill.chapter : 'Chapter'}} {{ item.chapter }}</div>
{% if settings.themes.typemill.chapter %}
<div class="chapterNumber">{{ settings.themes.typemill.chapter }} {{ item.chapter }}</div>
{% endif %}
{% if content is empty %}
<h1>{{ item.name }}</h1>

View File

@ -339,7 +339,44 @@ article img.middle{
max-width: 100%;
margin: auto;
}
article img.youtube{
position: relative;
max-width: 560px;
}
article .video-container{
position: relative;
text-align: center;
}
article button.play-video {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
height: 100px;
width: 100px;
background: #e0474c;
color: #FFFFFF;
border-radius: 50%;
border: 0px;
padding: 0;
text-align: center;
}
article button.play-video:hover {
background: #cc4146;
cursor: pointer;
}
article button.play-video::after {
position: absolute;
top: 50%;
margin: -20px 0 0 -15px;
height: 0;
width: 0;
border-style: solid;
border-width: 20px 0 20px 40px;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.75);
content: ' ';
}
/************************
* PAGING / BREADCRUMB *
@ -730,8 +767,8 @@ img.myClass{
header p{
margin: 20px 0;
}
.chapterNumber{
margin: 40px 0px 0px;
.chapter{
margin: 60px 0px 0px;
}
.close{
display: block;

View File

@ -0,0 +1,55 @@
var menu = document.getElementById("menu"),
navi = document.getElementById("navigation");
if(menu)
{
menu.addEventListener("click", function()
{
if(navi.className == "close")
{
navi.className = "open";
menu.className = "active";
}
else
{
navi.className = "close";
menu.className = "";
}
});
}
var shareButton = document.getElementById("share-button");
var shareIcons = document.getElementById("share-icons");
if(shareButton)
{
shareButton.addEventListener("click", function()
{
if(shareIcons.className == "share-icons show")
{
shareIcons.className = "share-icons hide";
}
else
{
shareIcons.className = "share-icons show";
}
});
}
var shareButtonBottom = document.getElementById("share-button-bottom");
var shareIconsBottom = document.getElementById("share-icons-bottom");
if(shareButtonBottom)
{
shareButtonBottom.addEventListener("click", function()
{
if(shareIconsBottom.className == "share-icons show")
{
shareIconsBottom.className = "share-icons hide";
}
else
{
shareIconsBottom.className = "share-icons show";
}
});
}

View File

@ -55,62 +55,11 @@
{% include 'partials/footer.twig' %}
</footer>
</div>
{% block javascripts %}
<script>
var menu = document.getElementById("menu"),
navi = document.getElementById("navigation");
if(menu)
{
menu.addEventListener("click", function(){
if(navi.className == "close")
{
navi.className = "open";
menu.className = "active";
}
else
{
navi.className = "close";
menu.className = "";
}
});
}
var shareButton = document.getElementById("share-button");
var shareIcons = document.getElementById("share-icons");
if(shareButton)
{
shareButton.addEventListener("click", function(){
if(shareIcons.className == "share-icons show")
{
shareIcons.className = "share-icons hide";
}
else
{
shareIcons.className = "share-icons show";
}
});
}
var shareButtonBottom = document.getElementById("share-button-bottom");
var shareIconsBottom = document.getElementById("share-icons-bottom");
{% block javascripts %}
<script src="{{ base_url }}/themes/typemill/js/script.js"></script>
<script src="{{ base_url }}/system/author/js/lazy-video.js"></script>
if(shareButtonBottom)
{
shareButtonBottom.addEventListener("click", function(){
if(shareIconsBottom.className == "share-icons show")
{
shareIconsBottom.className = "share-icons hide";
}
else
{
shareIconsBottom.className = "share-icons show";
}
});
}
</script>
{{ assets.renderJS() }}
{% endblock %}

View File

@ -1,5 +1,5 @@
name: Typemill Theme
version: 1.1.1
version: 1.1.2
description: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used.
author: Sebastian Schürmanns
homepage: https://typemill.net
@ -23,7 +23,6 @@ forms:
type: text
label: Text For Chapter
placeholder: Add Name for Chapter
required: true
start:
type: text

Binary file not shown.