Compare commits
No commits in common. "1f10cc7f89c165fb3ee4c4ae620a59b20bda3b50" and "b2ff261e74a9998f905d9b3d3d614fe38e96d277" have entirely different histories.
1f10cc7f89
...
b2ff261e74
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,3 @@
|
||||
/downloads/
|
||||
/app/.env.cfg
|
||||
/public/tmp/
|
||||
composer.lock
|
||||
@ -30,11 +30,11 @@ class KBController {
|
||||
$args[] = $tag_param;
|
||||
|
||||
if($search_term){
|
||||
$sql .= ' AND LOWER(a.title) LIKE LOWER(?)';
|
||||
$sql .= ' AND a.title LIKE ?';
|
||||
$args[] = '%' . $search_term . '%';
|
||||
}
|
||||
} else if ($search_term){
|
||||
$sql .= ' WHERE LOWER(a.title) LIKE LOWER(?)';
|
||||
$sql .= ' WHERE a.title LIKE ?';
|
||||
$args[] = '%' . $search_term . '%';
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ class KBController {
|
||||
$db->exec(
|
||||
'INSERT INTO kb (title, content, created_by, updated_by, created_at, updated_at)
|
||||
VALUES (?,?,?,?, NOW(), NOW())',
|
||||
[$title, $content, $created_by, $created_by]
|
||||
[$title, $content, $created_by]
|
||||
);
|
||||
|
||||
$article_id = $db->lastInsertId();
|
||||
@ -92,26 +92,24 @@ class KBController {
|
||||
$f3->reroute('/kb');
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
protected function check_kb_exists($article_id, $db, $f3){
|
||||
$articles = $db->exec(
|
||||
'SELECT * FROM kb WHERE id = ? LIMIT 1', [$article_id]
|
||||
);
|
||||
if(!$articles){
|
||||
$f3->set('SESSION.error', 'Article not found');
|
||||
$f3->reroute('/kb');
|
||||
}
|
||||
return $articles;
|
||||
}
|
||||
|
||||
// view a single
|
||||
public function view($f3){
|
||||
$this->check_access($f3);
|
||||
$article_id = $f3->get('PARAMS.id');
|
||||
$db = $f3->get('DB');
|
||||
|
||||
$articles = $this->check_kb_exists($article_id, $db, $f3);
|
||||
$articles = $db->exec(
|
||||
'SELECT a.*, u.username AS created_by_name
|
||||
FROM kb AS a
|
||||
LEFT JOIN users AS u ON a.created_by = u.id
|
||||
WHERE a.id = ? LIMIT 1',
|
||||
[$article_id]
|
||||
);
|
||||
|
||||
if(!$articles){
|
||||
$f3->set('SESSION.error', 'Article not found');
|
||||
$f3->reroute('/kb');
|
||||
}
|
||||
|
||||
$article = $articles[0];
|
||||
$f3->set('article', $article);
|
||||
@ -120,7 +118,7 @@ class KBController {
|
||||
$tags = $db->exec(
|
||||
'SELECT t.* FROM tags AS t
|
||||
JOIN kb_tags AS at ON t.id = at.tag_id
|
||||
WHERE at.kb_id = ?',
|
||||
WHERE at.article_id = ?',
|
||||
[$article_id]
|
||||
);
|
||||
|
||||
@ -130,76 +128,4 @@ class KBController {
|
||||
$f3->clear('SESSION.error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to edit existing kb article
|
||||
*/
|
||||
public function editForm($f3){
|
||||
|
||||
$this->check_access($f3);
|
||||
|
||||
$article_id = $f3->get('PARAMS.id');
|
||||
$db = $f3->get('DB');
|
||||
|
||||
$articles = $this->check_kb_exists($article_id, $db, $f3);
|
||||
|
||||
$article = $articles[0];
|
||||
$f3->set('article', $article);
|
||||
|
||||
// fetch current tags
|
||||
$current_tag_ids = $db->exec(
|
||||
'SELECT tag_id FROM kb_tags WHERE kb_id = ?', [$article_id]
|
||||
);
|
||||
|
||||
$article_tag_ids = array_column($current_tag_ids, 'tag_id');
|
||||
$f3->set('article_tag_ids', $article_tag_ids);
|
||||
|
||||
// render
|
||||
$f3->set('content', '../ui/views/kb/edit.html');
|
||||
echo \Template::instance()->render('../ui/templates/layout.html');
|
||||
$f3->clear('SESSION.error');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle POST to edit existing article
|
||||
*/
|
||||
public function update($f3){
|
||||
$this->check_access($f3);
|
||||
$article_id = $f3->get('PARAMS.id');
|
||||
$db = $f3->get('DB');
|
||||
|
||||
$articles = $this->check_kb_exists($article_id, $db, $f3);
|
||||
$article = $articles[0];
|
||||
|
||||
$title = $f3->get('POST.title');
|
||||
$content = $f3->get('POST.content');
|
||||
$updated_by = $f3->get('SESSION.user.id');
|
||||
|
||||
$db->exec(
|
||||
'UPDATE kb
|
||||
SET title=?, content=?, updated_by =?, updated_at = NOW()
|
||||
WHERE id = ?',
|
||||
[$title, $content, $updated_by, $article_id]
|
||||
);
|
||||
|
||||
// update tags - first delete
|
||||
$db->exec('DELETE FROM kb_tags WHERE kb_id = ?', [$article_id]);
|
||||
|
||||
$tags_id = $f3->get('POST.tags');
|
||||
if(!empty($tags_id) && is_array($tags_id)){
|
||||
foreach($tags_id as $tag_id){
|
||||
$db->exec(
|
||||
'INSERT IGNORE INTO kb_tags (article_id, tag_id) VALUES (?,?)',
|
||||
[$article_id, $tag_id]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$f3->reroute('/kb/'.$article_id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
class TagController {
|
||||
|
||||
protected function check_access($f3){
|
||||
if(!$f3->exists('SESSION.user')){
|
||||
$f3->reroute('/login');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all tags
|
||||
*/
|
||||
public function index($f3){
|
||||
$this->check_access($f3);
|
||||
|
||||
$db = $f3->get('DB');
|
||||
$tags = $db->exec('SELECT * FROM tags ORDER BY name ASC');
|
||||
$f3->set('tags', $tags);
|
||||
|
||||
$f3->set('content', '../ui/views/tag/index.html');
|
||||
echo \Template::instance()->render('../ui/templates/layout.html');
|
||||
}
|
||||
|
||||
public function createForm($f3){
|
||||
$this->check_access($f3);
|
||||
|
||||
$f3->set('content', '../ui/views/tag/create.html');
|
||||
echo \Template::instance()->render('../ui/templates/layout.html');
|
||||
}
|
||||
|
||||
public function create($f3){
|
||||
$this->check_access($f3);
|
||||
|
||||
$name = $f3->get('POST.name');
|
||||
$color = $f3->get('POST.color');
|
||||
$db = $f3->get('DB');
|
||||
|
||||
// insert new tag
|
||||
$db->exec('INSERT IGNORE INTO tags (name, color) VALUES (?, ?)', [$name, $color]);
|
||||
$f3->reroute('/tags');
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,7 +6,6 @@
|
||||
},
|
||||
"require": {
|
||||
"bcosca/fatfree-core": "^3.9",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"ezyang/htmlpurifier": "^4.18"
|
||||
"erusev/parsedown": "^1.7"
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,8 @@
|
||||
<?php
|
||||
|
||||
require '../lib/autoload.php';
|
||||
require '../lib/vendor/autoload.php';
|
||||
|
||||
$f3 = \Base::instance();
|
||||
/**
|
||||
* Not required yet
|
||||
*/
|
||||
// $htmlpurifier = new \HTMLPurifier();
|
||||
// $htmlpurifier->purify($input);
|
||||
$md = \Parsedown::instance();
|
||||
|
||||
|
||||
$f3->set('DEBUG', 3); // development debug
|
||||
$f3->config('../app/.env.cfg');
|
||||
|
||||
@ -52,16 +44,16 @@ $f3->route('POST /ticket/@id/update', 'TicketController->update'); //
|
||||
|
||||
// knowledgebase
|
||||
$f3->route('GET /kb', 'KBController->index');
|
||||
$f3->route('GET /kb/@id', 'KBController->view');
|
||||
$f3->route('GET /kb/create', 'KBController->createForm');
|
||||
$f3->route('POST /kb/create', 'KBController->create');
|
||||
$f3->route('GET /kb/@id', 'KBController->view');
|
||||
$f3->route('GET /kb/@id/edit', 'KBController->editForm');
|
||||
$f3->route('POST /kb/@id/update', 'KBController->update'); // should this be update - "crud"?
|
||||
$f3->route('POST /kb/@id/edit', 'KBControllerKB->edit'); // should this be update - "crud"?
|
||||
|
||||
// tags
|
||||
$f3->route('GET /tags', 'TagController->index');
|
||||
$f3->route('GET /tag/create', 'TagController->createForm');
|
||||
$f3->route('POST /tag/create', 'TagController->create');
|
||||
$f3->route('GET /tag/create', 'Tag->createForm');
|
||||
$f3->route('POST /tag/create', 'Tag->create');
|
||||
|
||||
|
||||
// dashboard
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma-helpers/0.4.3/css/bulma-helpers.min.css"
|
||||
integrity="sha512-U6ELnUi7oqVEjkLmFw5r5UR5LEtvpImS/jUykBKneVhD0lxZxfJZ3k3pe003ktrtNZYungd9u3Urp2X09wKwXg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<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">
|
||||
<!-- font awesome -->
|
||||
<link rel="stylesheet"
|
||||
@ -40,8 +40,8 @@
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/dashboard">Dashboard</a>
|
||||
<a class="navbar-item" href="/tickets">Tickets</a>
|
||||
<a class="navbar-item" href="/projects">Projects</a>
|
||||
<a class="navbar-item" href="/kb">Knowledge Base</a>
|
||||
<a class="navbar-item" href="/tags">Tags</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
@ -60,20 +60,6 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- breadcrumbs TODO: NOT YET
|
||||
<div class="container">
|
||||
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li><a href="#">Bulma</a></li>
|
||||
<li><a href="#">Documentation</a></li>
|
||||
<li><a href="#">Components</a></li>
|
||||
<li class="is-active"><a href="#" aria-current="page">Breadcrumb</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="section" id="page">
|
||||
<div class="container">
|
||||
|
||||
@ -1,34 +1,16 @@
|
||||
<h1 class="title">Create Knowledge Base Article</h1>
|
||||
|
||||
|
||||
<div class="content">
|
||||
<form action="/kb/create" method="POST">
|
||||
<form action="/kb/create" method="POST">
|
||||
|
||||
{{ BulmaForm::horizontal_field_input('Title:', 'title') }}
|
||||
|
||||
<div id="editor" class="block">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li class="is-active"><a>Write</a></li>
|
||||
<li class=""><a>Preivew</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{{ BulmaForm::horizontal_field_textarea('Description:', 'description') }}
|
||||
|
||||
<div class="block">
|
||||
{{ BulmaForm::horizontal_field_textarea('Content:', 'content') }}
|
||||
</div>
|
||||
</div>
|
||||
{{ BulmaForm::horizontal_field_select('Priority:', 'priority', ['Low', 'Medium', 'High'])}}
|
||||
|
||||
<!-- TODO: tags -->
|
||||
{{ BulmaForm::horizontal_field_select('Status:', 'status', ['New', 'In Progress', 'On Hold', 'Completed'])}}
|
||||
|
||||
<!-- buttons -->
|
||||
<div class="field is-grouped is-grouped-right">
|
||||
<div class="control">
|
||||
<a class="button is-secondary" href="/kb">Cancel</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Create KB Article</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<button class="button is-primary" type="submit">Create Ticket</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,25 +0,0 @@
|
||||
<h1 class="title">Edit Knowledge Base Article</h1>
|
||||
|
||||
|
||||
<form action="/kb/{{@article.id}}/update" method="POST">
|
||||
|
||||
{{ BulmaForm::horizontal_field_input('Title:', 'title', @article.title)}}
|
||||
|
||||
<div id="editor" class="block">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li class="is-active"><a>Write</a></li>
|
||||
<li class=""><a>Preivew</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
{{ BulmaForm::horizontal_field_textarea('Content:', 'content', @article.content) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: tags -->
|
||||
|
||||
<button class="button is-primary" type="submit">Update Article</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -6,56 +6,32 @@
|
||||
</div>
|
||||
</check>
|
||||
|
||||
<p><a href="/kb/create">create new kb article</a></p>
|
||||
<p><a href="/kb/create">create kb article</a></p>
|
||||
<hr>
|
||||
|
||||
<div class="block">
|
||||
<form method="GET" action="/kb">
|
||||
<div class="level">
|
||||
<div class="level-item">
|
||||
<input class="input" type="text" name="search" placeholder="Search by title...">
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="select">
|
||||
<select name="tag">
|
||||
<option value="">--Filter by Tag</option>
|
||||
<!-- TODO: load list of all tags-->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<button class="button is-primary" type="submit">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<check if="{{@articles}}">
|
||||
<table class="table is-fullwidth is-bordered">
|
||||
<table class="table is-fullwidth is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>id</th><th>title</th><th>created_at</th>
|
||||
<th>id</th><th>title</th><th>description</th>
|
||||
<th>status</th><th>priority</th><th>created_at</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<repeat group="{{@articles}}" value="{{@article}}">
|
||||
<repeat group="{{@tickets}}" value="{{@ticket}}">
|
||||
<tr>
|
||||
<td><a href="/kb/{{@article.id}}">{{@article.id}}</a></td>
|
||||
<td>{{@article.title}}</td>
|
||||
<td>{{@article.created_at}}</td>
|
||||
<td>{{@ticket.id}}</td>
|
||||
<td>{{@ticket.title}}</td>
|
||||
<td>{{@ticket.description}}</td>
|
||||
<td>{{@ticket.status}}</td>
|
||||
<td>{{@ticket.priority}}</td>
|
||||
<td>{{@ticket.created_at}}</td>
|
||||
<td>
|
||||
<a href="/kb/{{@article.id}}/edit"><i class="fa fa-edit"></i></a>
|
||||
<a href="/ticket/{{@ticket.id}}"><i class="fa fa-eye"></i></a>
|
||||
<a href="/ticket/{{@ticket.id}}/edit"><i class="fa fa-edit"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</repeat>
|
||||
</tbody>
|
||||
</table>
|
||||
</check>
|
||||
|
||||
<check if="!@articles">
|
||||
<div class="notification is-info is-light">
|
||||
<p>No articles found.</p>
|
||||
</div>
|
||||
</check>
|
||||
</table>
|
||||
@ -1,13 +1,9 @@
|
||||
<h1 class="title">{{@article.title}}</h1>
|
||||
<h1 class="title">Knowledge Article</h1>
|
||||
|
||||
<div class="content">
|
||||
{{ Parsedown::instance()->text(@article.content) }}
|
||||
</div>
|
||||
<div class="box">
|
||||
|
||||
<h2 class="title">{{@article.title}}</h2>
|
||||
|
||||
|
||||
<check if="{{ isset(@tags)}}">
|
||||
<div class="box">
|
||||
<table class="table is-bordered is-fullwidth">
|
||||
<thead>
|
||||
<tr><th class="has-width-200">Property</th><th>Value</th></tr>
|
||||
@ -18,5 +14,5 @@
|
||||
</repeat>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</check>
|
||||
|
||||
</div>
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
<h1 class="title">Create Tag</h1>
|
||||
|
||||
<div class="content">
|
||||
<form action="/tag/create" method="POST">
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Name</label>
|
||||
<div class="control">
|
||||
<input name="name" class="input" type="text" placeholder="Tag Name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<div class="control">
|
||||
<input name="color" class="input" type="text" placeholder="Tag Color">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped is-grouped-right">
|
||||
<div class="control">
|
||||
<a class="button is-secondary" href="/tags">Cancel</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Create Tag</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,45 +0,0 @@
|
||||
<h1 class="title">Tags</h1>
|
||||
|
||||
<check if="{{isset(@SESSION.error)}}">
|
||||
<div class="notification is-warning">
|
||||
{{ @SESSION.error }}
|
||||
</div>
|
||||
</check>
|
||||
|
||||
<p><a href="/tag/create">create new tag</a></p>
|
||||
<hr>
|
||||
|
||||
<check if="{{ count(@tags) > 1 }}">
|
||||
<true>
|
||||
<div class="tags are-medium">
|
||||
<repeat group="{{ @tags }}" value="{{ @tag }}">
|
||||
<a class="tag {{ (@tag.color ? 'is-' . @tag.color : '') }}">{{ @tag.name }}</a>
|
||||
</repeat>
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<div class="notification is-size-5">
|
||||
<span><i class="fa fa-2xl fa-face-sad-tear"></i> </span>No tags found
|
||||
</div>
|
||||
</false>
|
||||
</check>
|
||||
|
||||
|
||||
<div class="box has-background-dark">
|
||||
<div class="block">
|
||||
<h3 class="subtitle has-text-white"><i class="fa-solid fa-lightbulb has-text-warning"></i> Color Examples</h3>
|
||||
<p class="has-text-white">The following color names can be used for tags</p>
|
||||
</div>
|
||||
<div class="block">
|
||||
<span class="tag is-black">Black</span>
|
||||
<span class="tag is-dark">Dark</span>
|
||||
<span class="tag is-light">Light</span>
|
||||
<span class="tag is-white">White</span>
|
||||
<span class="tag is-primary">Primary</span>
|
||||
<span class="tag is-link">Link</span>
|
||||
<span class="tag is-info">Info</span>
|
||||
<span class="tag is-success">Success</span>
|
||||
<span class="tag is-warning">Warning</span>
|
||||
<span class="tag is-danger">Danger</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -11,13 +11,6 @@
|
||||
|
||||
{{ BulmaForm::horizontal_field_select('Status:', 'status', ['New', 'In Progress', 'On Hold', 'Completed'])}}
|
||||
|
||||
<div class="field is-grouped is-grouped-right">
|
||||
<div class="control">
|
||||
<a class="button is-secondary" href="/tickets">Cancel</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Create Ticket</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -12,7 +12,7 @@
|
||||
<table class="table is-fullwidth is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>id</th><th>title</th>
|
||||
<th>id</th><th>title</th><th>description</th>
|
||||
<th>status</th><th>priority</th><th>created_at</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@ -21,12 +21,13 @@
|
||||
<tbody>
|
||||
<repeat group="{{@tickets}}" value="{{@ticket}}">
|
||||
<tr>
|
||||
<td><a href="/ticket/{{@ticket.id}}">{{@ticket.id}}</a></td>
|
||||
<td>{{@ticket.id}}</td>
|
||||
<td>{{@ticket.title}}</td>
|
||||
<td>{{@ticket.status}}</td>
|
||||
<td>{{@ticket.priority}}</td>
|
||||
<td>{{@ticket.created_at}}</td>
|
||||
<td>
|
||||
<a href="/ticket/{{@ticket.id}}"><i class="fa fa-eye"></i></a>
|
||||
<a href="/ticket/{{@ticket.id}}/edit"><i class="fa fa-edit"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user