mirror of
https://github.com/typemill/typemill.git
synced 2025-01-16 21:08:20 +01:00
Version 1.5.1: Shortcodes, public form generator, improved textarea for code
This commit is contained in:
parent
959e043aa3
commit
d1d7d61b4e
@ -1076,7 +1076,7 @@ class ControllerAuthorArticleApi extends ControllerAuthor
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl());
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
|
||||
|
||||
# fix footnotes in parsedown, might break with complicated footnotes
|
||||
$parsedown->setVisualMode();
|
||||
|
@ -67,7 +67,7 @@ class ControllerAuthorBlockApi extends ControllerAuthor
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl());
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($pageMarkdown))
|
||||
@ -265,7 +265,7 @@ class ControllerAuthorBlockApi extends ControllerAuthor
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl());
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
|
||||
$parsedown->setVisualMode();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
|
@ -220,7 +220,7 @@ class ControllerFrontendWebsite extends ControllerShared
|
||||
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension($this->base_url, $this->settings);
|
||||
$parsedown = new ParsedownExtension($this->base_url, $this->settings, $this->c->dispatcher);
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
14
system/Events/OnShortcodeFound.php
Normal file
14
system/Events/OnShortcodeFound.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for breadcrumb.
|
||||
*/
|
||||
|
||||
class OnShortcodeFound extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@ -3,15 +3,19 @@
|
||||
namespace Typemill\Extensions;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Events\OnShortcodeFound;
|
||||
|
||||
|
||||
class ParsedownExtension extends \ParsedownExtra
|
||||
{
|
||||
function __construct($baseUrl = '', $settings = NULL)
|
||||
function __construct($baseUrl = '', $settings = NULL, $dispatcher = NULL)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->settings = $settings;
|
||||
|
||||
$this->dispatcher = $dispatcher;
|
||||
|
||||
# show anchor next to headline?
|
||||
$this->showAnchor = isset($settings['headlineanchors']) ? $settings['headlineanchors'] : false;
|
||||
|
||||
@ -34,6 +38,7 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
|
||||
$this->InlineTypes['\\'][] = 'Math';
|
||||
$this->InlineTypes['$'][] = 'Math';
|
||||
$this->InlineTypes['['][] = 'Shortcode';
|
||||
$this->inlineMarkerList .= '\\';
|
||||
$this->inlineMarkerList .= '$';
|
||||
|
||||
@ -42,7 +47,10 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
|
||||
$this->visualMode = false;
|
||||
|
||||
# identify Table Of contents after footnotes and links
|
||||
# identify Shortcodes after footnotes and links
|
||||
array_unshift($this->BlockTypes['['], 'Shortcode');
|
||||
|
||||
# identify Table Of contents after footnotes and links and shortcodes
|
||||
array_unshift($this->BlockTypes['['], 'TableOfContents');
|
||||
}
|
||||
|
||||
@ -715,6 +723,176 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
{
|
||||
return $Block;
|
||||
}
|
||||
|
||||
protected function blockShortcode($Line)
|
||||
{
|
||||
if ($this->dispatcher && preg_match('/^\[:.*:\]/', $Line['text'], $matches))
|
||||
{
|
||||
return $this->createShortcodeArray($matches);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineShortcode($Excerpt)
|
||||
{
|
||||
$remainder = $Excerpt['text'];
|
||||
|
||||
if ($this->dispatcher && preg_match('/\[:.*:\]/', $remainder, $matches))
|
||||
{
|
||||
return $this->createShortcodeArray($matches);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected function createShortcodeArray($matches)
|
||||
{
|
||||
$shortcodeString = substr($matches[0], 2, -2);
|
||||
$shortcodeArray = explode(' ', $shortcodeString, 2);
|
||||
$shortcode = [];
|
||||
|
||||
$shortcode['name'] = $shortcodeArray[0];
|
||||
$shortcode['params'] = false;
|
||||
|
||||
# are there params?
|
||||
if(isset($shortcodeArray[1]))
|
||||
{
|
||||
$shortcode['params'] = [];
|
||||
|
||||
# see: https://www.thetopsites.net/article/58136180.shtml
|
||||
$pattern = '/(\\w+)\s*=\\s*("[^"]*"|\'[^\']*\'|[^"\'\\s>]*)/';
|
||||
preg_match_all($pattern, $shortcodeArray[1], $attributes, PREG_SET_ORDER);
|
||||
|
||||
foreach($attributes as $attribute)
|
||||
{
|
||||
if(isset($attribute[1]) && isset($attribute[2]))
|
||||
{
|
||||
$shortcode['params'][$attribute[1]] = trim($attribute[2], " \"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$html = $this->dispatcher->dispatch('onShortcodeFound', new OnShortcodeFound($shortcode))->getData();
|
||||
|
||||
# if no shortcode has been processed, add the original string
|
||||
if(is_array($html) OR is_object($html))
|
||||
{
|
||||
$html = '<span class="shortcode-alert">No shortcode found.</span>';
|
||||
}
|
||||
|
||||
return array(
|
||||
'element' => array(
|
||||
'rawHtml' => $html,
|
||||
'allowRawHtmlInSafeMode' => true,
|
||||
),
|
||||
'extent' => strlen($matches[0]),
|
||||
);
|
||||
}
|
||||
|
||||
protected function inlineLink($Excerpt)
|
||||
{
|
||||
$Element = array(
|
||||
'name' => 'a',
|
||||
'handler' => array(
|
||||
'function' => 'lineElements',
|
||||
'argument' => null,
|
||||
'destination' => 'elements',
|
||||
),
|
||||
'nonNestables' => array('Url', 'Link'),
|
||||
'attributes' => array(
|
||||
'href' => null,
|
||||
'title' => null,
|
||||
),
|
||||
);
|
||||
|
||||
$extent = 0;
|
||||
|
||||
$remainder = $Excerpt['text'];
|
||||
|
||||
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
|
||||
{
|
||||
$Element['handler']['argument'] = $matches[1];
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
|
||||
$remainder = substr($remainder, $extent);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
|
||||
{
|
||||
# start typemill: if relative link or media-link
|
||||
$href = $matches[1];
|
||||
if($href[0] == '/')
|
||||
{
|
||||
$href = $this->baseUrl . $href;
|
||||
}
|
||||
elseif(substr( $href, 0, 6 ) === "media/")
|
||||
{
|
||||
$href = $this->baseUrl . '/' . $href;
|
||||
}
|
||||
# end typemill
|
||||
|
||||
$Element['attributes']['href'] = $href;
|
||||
|
||||
if (isset($matches[2]))
|
||||
{
|
||||
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
||||
}
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
||||
{
|
||||
$definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
|
||||
$definition = strtolower($definition);
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$definition = strtolower($Element['handler']['argument']);
|
||||
}
|
||||
|
||||
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$Definition = $this->DefinitionData['Reference'][$definition];
|
||||
|
||||
$Element['attributes']['href'] = $Definition['url'];
|
||||
$Element['attributes']['title'] = $Definition['title'];
|
||||
}
|
||||
|
||||
$Link = array(
|
||||
'extent' => $extent,
|
||||
'element' => $Element,
|
||||
);
|
||||
|
||||
# Parsedown Extra
|
||||
$remainder = $Link !== null ? substr($Excerpt['text'], $Link['extent']) : '';
|
||||
|
||||
if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
|
||||
{
|
||||
$Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
|
||||
|
||||
$Link['extent'] += strlen($matches[0]);
|
||||
}
|
||||
|
||||
return $Link;
|
||||
|
||||
}
|
||||
|
||||
# advanced attribute data, check parsedown extra plugin: https://github.com/tovic/parsedown-extra-plugin
|
||||
protected function parseAttributeData($text) {
|
||||
@ -771,8 +949,6 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
|
||||
protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)';
|
||||
|
||||
|
||||
|
||||
# ++
|
||||
# blocks that belong to a "magneticType" would "merge" if they are next to each other
|
||||
protected $magneticTypes = array('DefinitionList', 'Footnote');
|
||||
@ -1050,105 +1226,4 @@ class ParsedownExtension extends \ParsedownExtra
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected function inlineLink($Excerpt)
|
||||
{
|
||||
$Element = array(
|
||||
'name' => 'a',
|
||||
'handler' => array(
|
||||
'function' => 'lineElements',
|
||||
'argument' => null,
|
||||
'destination' => 'elements',
|
||||
),
|
||||
'nonNestables' => array('Url', 'Link'),
|
||||
'attributes' => array(
|
||||
'href' => null,
|
||||
'title' => null,
|
||||
),
|
||||
);
|
||||
|
||||
$extent = 0;
|
||||
|
||||
$remainder = $Excerpt['text'];
|
||||
|
||||
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
|
||||
{
|
||||
$Element['handler']['argument'] = $matches[1];
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
|
||||
$remainder = substr($remainder, $extent);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
|
||||
{
|
||||
# start typemill: if relative link or media-link
|
||||
$href = $matches[1];
|
||||
if($href[0] == '/')
|
||||
{
|
||||
$href = $this->baseUrl . $href;
|
||||
}
|
||||
elseif(substr( $href, 0, 6 ) === "media/")
|
||||
{
|
||||
$href = $this->baseUrl . '/' . $href;
|
||||
}
|
||||
# end typemill
|
||||
|
||||
$Element['attributes']['href'] = $href;
|
||||
|
||||
if (isset($matches[2]))
|
||||
{
|
||||
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
||||
}
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
||||
{
|
||||
$definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
|
||||
$definition = strtolower($definition);
|
||||
|
||||
$extent += strlen($matches[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$definition = strtolower($Element['handler']['argument']);
|
||||
}
|
||||
|
||||
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$Definition = $this->DefinitionData['Reference'][$definition];
|
||||
|
||||
$Element['attributes']['href'] = $Definition['url'];
|
||||
$Element['attributes']['title'] = $Definition['title'];
|
||||
}
|
||||
|
||||
$Link = array(
|
||||
'extent' => $extent,
|
||||
'element' => $Element,
|
||||
);
|
||||
|
||||
# Parsedown Extra
|
||||
$remainder = $Link !== null ? substr($Excerpt['text'], $Link['extent']) : '';
|
||||
|
||||
if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
|
||||
{
|
||||
$Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
|
||||
|
||||
$Link['extent'] += strlen($matches[0]);
|
||||
}
|
||||
|
||||
return $Link;
|
||||
|
||||
}
|
||||
}
|
@ -89,13 +89,24 @@ class Fields
|
||||
}
|
||||
|
||||
# Now prepopulate the field object with the value */
|
||||
if($field->getType() == "textarea" || $field->getType() == "paragraph")
|
||||
if($field->getType() == "textarea")
|
||||
{
|
||||
if($userValue)
|
||||
{
|
||||
$field->setContent($userValue);
|
||||
}
|
||||
}
|
||||
elseif($field->getType() == 'paragraph')
|
||||
{
|
||||
if(isset($fieldConfigurations['value']))
|
||||
{
|
||||
$field->setContent($fieldConfigurations['value']);
|
||||
}
|
||||
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
|
||||
|
@ -202,9 +202,10 @@ abstract class Plugin implements EventSubscriberInterface
|
||||
$fieldsModel = new Fields();
|
||||
|
||||
$settings = $this->getSettings();
|
||||
$form = false;
|
||||
|
||||
$pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName);
|
||||
if(isset($settings['plugins'][$pluginName]['publicformdefinitions']))
|
||||
if(isset($settings['plugins'][$pluginName]['publicformdefinitions']) && $settings['plugins'][$pluginName]['publicformdefinitions'] != '')
|
||||
{
|
||||
$arrayFromYaml = \Symfony\Component\Yaml\Yaml::parse($settings['plugins'][$pluginName]['publicformdefinitions']);
|
||||
$pluginDefinitions['public']['fields'] = $arrayFromYaml;
|
||||
@ -214,6 +215,12 @@ abstract class Plugin implements EventSubscriberInterface
|
||||
$captchaoptions = isset($settings['plugins'][$pluginName]['captchaoptions']) ? $settings['plugins'][$pluginName]['captchaoptions'] : false;
|
||||
$recaptcha = isset($settings['plugins'][$pluginName]['recaptcha']) ? $settings['plugins'][$pluginName]['recaptcha_webkey'] : false;
|
||||
|
||||
if($captchaoptions == 'disabled')
|
||||
{
|
||||
# in case a captcha has failed on another page like login, the captcha-session must be deleted, otherwise it will not pass the security middleware
|
||||
unset($_SESSION['captcha']);
|
||||
}
|
||||
|
||||
$fieldsModel = new Fields();
|
||||
|
||||
if(isset($pluginDefinitions['public']['fields']))
|
||||
|
@ -1,3 +1,10 @@
|
||||
@font-face {
|
||||
font-family: 'visiblecontrol';
|
||||
src: url('visiblecontrol.woff') format('woff'),
|
||||
url('visiblecontrol.woff2') format('woff2');
|
||||
unicode-range: U+0020;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* TRANSITION *
|
||||
**********************/
|
||||
@ -1349,7 +1356,7 @@ ul.cardInfo{
|
||||
background-color: #FFF;
|
||||
}
|
||||
textarea.codearea, .cardField textarea.codearea{
|
||||
font-family: monospace;
|
||||
font-family: visiblecontrol,monospace;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
BIN
system/author/css/visiblecontrol.woff
Normal file
BIN
system/author/css/visiblecontrol.woff
Normal file
Binary file not shown.
BIN
system/author/css/visiblecontrol.woff2
Normal file
BIN
system/author/css/visiblecontrol.woff2
Normal file
Binary file not shown.
@ -37,7 +37,16 @@
|
||||
|
||||
{% if field.type == 'textarea' %}
|
||||
|
||||
<textarea id="{{ itemName }}[{{ field.name }}]" {{ (field.name == 'publicformdefinitions') ? 'class="publicformdefinitions"' : '' }} name="{{ itemName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
|
||||
{% if field.name == 'publicformdefinitions' %}
|
||||
|
||||
<textarea id="{{ itemName }}[{{ field.name }}]" class="codearea yaml" name="{{ itemName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
|
||||
<!-- <button class="formpreview formpreview bg-tm-green white pa2 right bn dim">preview form</button> -->
|
||||
|
||||
{% else %}
|
||||
|
||||
<textarea id="{{ itemName }}[{{ field.name }}]" name="{{ itemName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% elseif field.type == 'paragraph' %}
|
||||
|
||||
|
@ -85,6 +85,8 @@
|
||||
{% endfor %}
|
||||
|
||||
</section>
|
||||
|
||||
<script>window.addEventListener('load', function () { autosize(document.querySelectorAll('textarea'));})</script>
|
||||
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user