added proper f3 tag extension for bulma form
This commit is contained in:
parent
1f10cc7f89
commit
6c936208b7
@ -154,6 +154,7 @@ class KBController {
|
|||||||
$f3->set('article_tag_ids', $article_tag_ids);
|
$f3->set('article_tag_ids', $article_tag_ids);
|
||||||
|
|
||||||
// render
|
// render
|
||||||
|
$f3->set('js', 'kb_edit.js');
|
||||||
$f3->set('content', '../ui/views/kb/edit.html');
|
$f3->set('content', '../ui/views/kb/edit.html');
|
||||||
echo \Template::instance()->render('../ui/templates/layout.html');
|
echo \Template::instance()->render('../ui/templates/layout.html');
|
||||||
$f3->clear('SESSION.error');
|
$f3->clear('SESSION.error');
|
||||||
|
|||||||
12
app/controllers/ParsedownPreview.php
Normal file
12
app/controllers/ParsedownPreview.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ParsedownPreview {
|
||||||
|
|
||||||
|
public function view($f3){
|
||||||
|
|
||||||
|
$preview_text = $f3->get('POST.content');
|
||||||
|
echo Parsedown::instance()->text($preview_text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
app/extensions/BulmaFormHelper.php
Normal file
108
app/extensions/BulmaFormHelper.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class BulmaFormHelper extends \Prefab {
|
||||||
|
|
||||||
|
const H_FIELD_INPUT = 1;
|
||||||
|
const H_FIELD_TEXTAREA = 2;
|
||||||
|
const H_FIELD_SELECT = 3;
|
||||||
|
|
||||||
|
static public function render($args) {
|
||||||
|
|
||||||
|
$type = strtoupper($args['@attrib']['type']);
|
||||||
|
// all *
|
||||||
|
$label = $args['@attrib']['label'];
|
||||||
|
$name = $args['@attrib']['name'];
|
||||||
|
$value = isset($args['@attrib']['value']) ? $args['@attrib']['value'] : '';
|
||||||
|
// select
|
||||||
|
$options = isset($args['@attrib']['options']) ? $args['@attrib']['options'] : '';
|
||||||
|
$selected = isset($args['@attrib']['selected']) ? $args['@attrib']['selected'] : '';
|
||||||
|
//
|
||||||
|
$label = \Template::instance()->build($label);
|
||||||
|
$name = \Template::instance()->build($name);
|
||||||
|
$value = \Template::instance()->build($value);
|
||||||
|
|
||||||
|
if(defined("BulmaFormHelper::$type")){
|
||||||
|
|
||||||
|
$type_const = constant("BulmaFormHelper::$type");
|
||||||
|
|
||||||
|
switch( $type_const ){
|
||||||
|
case BulmaFormHelper::H_FIELD_INPUT:
|
||||||
|
return BulmaFormHelper::build_h_field_input($label, $name, $value);
|
||||||
|
break;
|
||||||
|
case BulmaFormHelper::H_FIELD_TEXTAREA:
|
||||||
|
return BulmaFormHelper::build_h_field_textarea($label, $name, $value);
|
||||||
|
break;
|
||||||
|
case BulmaFormHelper::H_FIELD_SELECT:
|
||||||
|
return BulmaFormHelper::build_h_field_select($label, $name, $options, $selected);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return '<div class="notification is-danger">Error: Bulma CSS Form TYPE ('.$type.') not defined.</div>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return '<div class="notification is-danger">Error: Bulma CSS Form TYPE not defined.</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static function build_h_field_input($label, $name, $value){
|
||||||
|
$string = '
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">'.$label.'</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" id="'.$name.'" name="'.
|
||||||
|
$name.'" value="'.
|
||||||
|
$value.'">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function build_h_field_select($label, $name, $options, $selected){
|
||||||
|
$string =
|
||||||
|
'<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">'.$label.'</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="select">
|
||||||
|
$options
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function build_h_field_textarea($label, $name, $value){
|
||||||
|
$string = '
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">'.$label.'</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" id="'.$name.'" name="'.
|
||||||
|
$name.'">'.$value.'</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
\Template::instance()->extend('bulma', 'BulmaFormHelper::render');
|
||||||
20
app/extensions/ParsedownHelper.php
Normal file
20
app/extensions/ParsedownHelper.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ParsedownHelper extends \Prefab {
|
||||||
|
|
||||||
|
static public function render($args) {
|
||||||
|
$content = $args[0];
|
||||||
|
$content_token = \Template::instance()->token($content);
|
||||||
|
|
||||||
|
return '
|
||||||
|
<parsedown_rendered class="content">
|
||||||
|
<?php echo \ParsedownHelper::instance()->build('.$content_token.'); ?>
|
||||||
|
</parsedown_rendered>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function build($content){
|
||||||
|
return Parsedown::instance()->text($content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\Template::instance()->extend('parsedown', 'ParsedownHelper::render');
|
||||||
@ -8,7 +8,7 @@ class BulmaForm {
|
|||||||
$string = '
|
$string = '
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label>%label%</label>
|
<label class="label">%label%</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@ -31,7 +31,7 @@ class BulmaForm {
|
|||||||
$string = '
|
$string = '
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label>%label%</label>
|
<label class="label">%label%</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@ -54,7 +54,7 @@ class BulmaForm {
|
|||||||
$string = '
|
$string = '
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label>%label%</label>
|
<label class="label">%label%</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|||||||
@ -6,13 +6,16 @@ $f3 = \Base::instance();
|
|||||||
/**
|
/**
|
||||||
* Not required yet
|
* Not required yet
|
||||||
*/
|
*/
|
||||||
// $htmlpurifier = new \HTMLPurifier();
|
$htmlpurifier = \HTMLPurifier::instance();
|
||||||
// $htmlpurifier->purify($input);
|
// $htmlpurifier->purify($input);
|
||||||
$md = \Parsedown::instance();
|
$md = \Parsedown::instance();
|
||||||
|
$md->setSafeMode(true);
|
||||||
|
|
||||||
|
|
||||||
$f3->set('DEBUG', 3); // development debug
|
|
||||||
$f3->config('../app/.env.cfg');
|
$f3->config('../app/.env.cfg');
|
||||||
|
$f3->set('DEBUG', 3); // development debug
|
||||||
|
$f3->set('CACHE', FALSE);
|
||||||
|
|
||||||
|
$f3->set('EXT', [new ParsedownHelper, new BulmaFormHelper]);
|
||||||
|
|
||||||
$f3->set('DB', new \DB\SQL(
|
$f3->set('DB', new \DB\SQL(
|
||||||
'mysql:host=localhost;port=3306;dbname=' . $f3->get('database.db_name'),
|
'mysql:host=localhost;port=3306;dbname=' . $f3->get('database.db_name'),
|
||||||
@ -63,6 +66,8 @@ $f3->route('GET /tags', 'TagController->index');
|
|||||||
$f3->route('GET /tag/create', 'TagController->createForm');
|
$f3->route('GET /tag/create', 'TagController->createForm');
|
||||||
$f3->route('POST /tag/create', 'TagController->create');
|
$f3->route('POST /tag/create', 'TagController->create');
|
||||||
|
|
||||||
|
// parsedown preview
|
||||||
|
$f3->route('POST /parsedown/preview', 'ParsedownPreview->view');
|
||||||
|
|
||||||
// dashboard
|
// dashboard
|
||||||
$f3->route('GET /dashboard', 'DashboardController->index');
|
$f3->route('GET /dashboard', 'DashboardController->index');
|
||||||
|
|||||||
75
public/js/kb_edit.js
Normal file
75
public/js/kb_edit.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// switch to target tab pane
|
||||||
|
function switchTab(targetId){
|
||||||
|
var panes = document.querySelectorAll('.tab-content .tab-pane');
|
||||||
|
for (var i=0; i< panes.length; i++){
|
||||||
|
panes[i].style.display = 'none';
|
||||||
|
}
|
||||||
|
var targetPane = document.getElementById(targetId);
|
||||||
|
if(targetPane){
|
||||||
|
targetPane.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send ajax post request with content to specified url
|
||||||
|
function ajaxPost(content, url, callback){
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', url, true);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr.onreadystatechange = function(){
|
||||||
|
if(xhr.readyState === XMLHttpRequest.DONE){
|
||||||
|
if(xhr.status === 200){
|
||||||
|
callback(xhr.responseText);
|
||||||
|
} else {
|
||||||
|
console.error("AJAX error: " + xhr.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var params = 'content=' + encodeURIComponent(content);
|
||||||
|
xhr.send(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load preview via ajax into preview element
|
||||||
|
function loadPreview(previewElement){
|
||||||
|
var sourceId = previewElement.getAttribute('data-source');
|
||||||
|
var handlerUrl = previewElement.getAttribute('data-handler');
|
||||||
|
var method = previewElement.getAttribute('data-method');
|
||||||
|
var sourceElement = document.getElementById(sourceId);
|
||||||
|
if(sourceElement){
|
||||||
|
var content = sourceElement.value;
|
||||||
|
if(method && method.toLowerCase() == 'post'){
|
||||||
|
ajaxPost(content, handlerUrl, function (response){
|
||||||
|
previewElement.innerHTML = response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialise tab links to handle tab switching
|
||||||
|
function initTabs(){
|
||||||
|
var tabLinks = document.querySelectorAll('.tabs a[data-target]');
|
||||||
|
for(var i=0; i<tabLinks.length; i++){
|
||||||
|
tabLinks[i].addEventListener('click', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var target = this.getAttribute('data-target');
|
||||||
|
switchTab(target);
|
||||||
|
|
||||||
|
// if the new tab has a preview element, load the preview
|
||||||
|
var pane = document.getElementById(target);
|
||||||
|
if(pane){
|
||||||
|
var previewElement = pane.querySelector('.preview');
|
||||||
|
if(previewElement){
|
||||||
|
console.log('pane has preview el')
|
||||||
|
loadPreview(previewElement);
|
||||||
|
} else {
|
||||||
|
console.log('pane doesnt have preview el');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
initTabs();
|
||||||
|
});
|
||||||
@ -14,10 +14,13 @@
|
|||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma-checkradio@2.1/dist/css/bulma-checkradio.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma-checkradio@2.1/dist/css/bulma-checkradio.min.css">
|
||||||
<!-- font awesome -->
|
<!-- font awesome -->
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
|
|
||||||
integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
|
integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<!-- additional JS -->
|
||||||
|
<check if="{{ isset(@js) }}">
|
||||||
|
<script src="/js/{{ @js}}"></script>
|
||||||
|
</check>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -85,7 +88,9 @@
|
|||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="content has-text-centered">
|
<div class="content has-text-centered">
|
||||||
<p>© <?php echo date('Y'); ?> Terry Probert</p>
|
<p>©
|
||||||
|
<?php echo date('Y'); ?> Terry Probert
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@ -4,18 +4,23 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<form action="/kb/create" method="POST">
|
<form action="/kb/create" method="POST">
|
||||||
|
|
||||||
{{ BulmaForm::horizontal_field_input('Title:', 'title') }}
|
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value=""></bulma>
|
||||||
|
|
||||||
<div id="editor" class="block">
|
<div id="editor" class="block">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="is-active"><a>Write</a></li>
|
<li class="is-active"><a>Write</a></li>
|
||||||
<li class=""><a>Preivew</a></li>
|
<li class=""><a>Preview</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
{{ BulmaForm::horizontal_field_textarea('Content:', 'content') }}
|
<div class="tab-content">
|
||||||
|
<bulma type="H_FIELD_TEXTAREA" label="Content:" name="content" value=""></bulma>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -3,18 +3,27 @@
|
|||||||
|
|
||||||
<form action="/kb/{{@article.id}}/update" method="POST">
|
<form action="/kb/{{@article.id}}/update" method="POST">
|
||||||
|
|
||||||
{{ BulmaForm::horizontal_field_input('Title:', 'title', @article.title)}}
|
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value="{{@article.title}}"></bulma>
|
||||||
|
|
||||||
<div id="editor" class="block">
|
<div id="editor" class="block">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="is-active"><a>Write</a></li>
|
<li class="is-active"><a data-target="pane-editor">Write</a></li>
|
||||||
<li class=""><a>Preivew</a></li>
|
<li class=""><a data-target="pane-preview">Preview</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane" id="pane-editor">
|
||||||
<div class="block">
|
<div class="block">
|
||||||
{{ BulmaForm::horizontal_field_textarea('Content:', 'content', @article.content) }}
|
<bulma type="H_FIELD_TEXTAREA" label="Content:" name="content" value="{{@article.content}}"></bulma>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" id="pane-preview">
|
||||||
|
<div class="block content">
|
||||||
|
<div class="preview" data-source="content" data-handler="/parsedown/preview" data-method="post"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,15 @@
|
|||||||
<form method="GET" action="/kb">
|
<form method="GET" action="/kb">
|
||||||
<div class="level">
|
<div class="level">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
|
<check if="{{ isset(@GET.search)}}">
|
||||||
|
<true>
|
||||||
|
<input class="input" type="text" name="search" placeholder="Search by title..."
|
||||||
|
value="{{ HTMLPurifier::instance()->purify( @GET.search) }}">
|
||||||
|
</true>
|
||||||
|
<false>
|
||||||
<input class="input" type="text" name="search" placeholder="Search by title...">
|
<input class="input" type="text" name="search" placeholder="Search by title...">
|
||||||
|
</false>
|
||||||
|
</check>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<h1 class="title">{{@article.title}}</h1>
|
<h1 class="title">{{@article.title}}</h1>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{ Parsedown::instance()->text(@article.content) }}
|
<parsedown>{{ @article.content | raw }}</parsedown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user