Compare commits
No commits in common. "164692a728aad4b0ebdc098080f3f3c090b15ebc" and "2a3465fff803fcf105405fcb0fbf9770b7072995" have entirely different histories.
164692a728
...
2a3465fff8
@ -28,7 +28,6 @@ GET /ticket/@id/attachments=AttachmentController->index
|
|||||||
POST /ticket/@id/attachments/upload=AttachmentController->upload
|
POST /ticket/@id/attachments/upload=AttachmentController->upload
|
||||||
GET /attachment/@id/download=AttachmentController->download
|
GET /attachment/@id/download=AttachmentController->download
|
||||||
GET /attachment/@id/delete=AttachmentController->delete
|
GET /attachment/@id/delete=AttachmentController->delete
|
||||||
GET /attachment/@id/view=AttachmentController->view
|
|
||||||
|
|
||||||
; knowledgebase
|
; knowledgebase
|
||||||
GET /kb=KBController->index
|
GET /kb=KBController->index
|
||||||
|
|||||||
@ -145,41 +145,4 @@ class AttachmentController {
|
|||||||
// remove DB row
|
// remove DB row
|
||||||
$db->exec('DELETE FROM attachments WHERE id =?', [$attachment_id]);
|
$db->exec('DELETE FROM attachments WHERE id =?', [$attachment_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// view attachment
|
|
||||||
public function view($f3){
|
|
||||||
$this->check_access($f3);
|
|
||||||
|
|
||||||
$attachment_id = (int) $f3->get('PARAMS.id');
|
|
||||||
$db = $f3->get('DB');
|
|
||||||
|
|
||||||
$rows = $db->exec('SELECt * FROM attachments WHERE id = ?', [$attachment_id]);
|
|
||||||
|
|
||||||
if(!$rows){
|
|
||||||
$f3->error(404, "File not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$attachment = $rows[0];
|
|
||||||
$file_path = $attachment['path'];
|
|
||||||
$file_name = $attachment['file_name'];
|
|
||||||
|
|
||||||
if(!file_exists($file_path)){
|
|
||||||
$f3->error(404, "File not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect mime type
|
|
||||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
||||||
$mime_type = finfo_file($finfo, $file_path);
|
|
||||||
finfo_close($finfo);
|
|
||||||
|
|
||||||
header('Content-Type: ' . $mime_type);
|
|
||||||
header('Content-Disposition: inline; filename="' . basename($file_name) . '"');
|
|
||||||
header('Content-Length: ' . filesize($file_path));
|
|
||||||
|
|
||||||
flush();
|
|
||||||
readfile($file_path);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -58,7 +58,6 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'title' => $this->f3->get('POST.title'),
|
'title' => $this->f3->get('POST.title'),
|
||||||
'created_at' => $this->f3->get('POST.created_at'),
|
|
||||||
'description' => $this->f3->get('POST.description'),
|
'description' => $this->f3->get('POST.description'),
|
||||||
'priority' => $this->f3->get('POST.priority'),
|
'priority' => $this->f3->get('POST.priority'),
|
||||||
'status' => $this->f3->get('POST.status'),
|
'status' => $this->f3->get('POST.status'),
|
||||||
@ -116,7 +115,6 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'title' => $this->f3->get('POST.title'),
|
'title' => $this->f3->get('POST.title'),
|
||||||
'created_at' => $this->f3->get('POST.created_at'),
|
|
||||||
'description' => $this->f3->get('POST.description'),
|
'description' => $this->f3->get('POST.description'),
|
||||||
'priority' => $this->f3->get('POST.priority'),
|
'priority' => $this->f3->get('POST.priority'),
|
||||||
'status' => $this->f3->get('POST.status'),
|
'status' => $this->f3->get('POST.status'),
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class ParsedownHelper extends \Prefab {
|
|||||||
|
|
||||||
$return = \Parsedown::instance()->text($args[0]);
|
$return = \Parsedown::instance()->text($args[0]);
|
||||||
|
|
||||||
return '<!-- tableextension -->
|
return '
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<parsedown_rendered>'.$return.'</parsedown_rendered>
|
<parsedown_rendered>'.$return.'</parsedown_rendered>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +28,7 @@ class ParsedownHelper extends \Prefab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function build($content){
|
function build($content){
|
||||||
return \ParsedownTableExtension::instance()->text($content);
|
return Parsedown::instance()->text($content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class ParsedownTableExtension extends Parsedown
|
|
||||||
{
|
|
||||||
protected function blockTable($Line, ?array $Block = null)
|
|
||||||
{
|
|
||||||
$Block = parent::blockTable($Line, $Block);
|
|
||||||
if(!isset($Block)){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add classes to the table element
|
|
||||||
$Block['element']['attributes'] = [
|
|
||||||
'class' => 'table is-bordered',
|
|
||||||
];
|
|
||||||
|
|
||||||
// wrap the table in a bulma div
|
|
||||||
$Block['element'] = [
|
|
||||||
'name' => 'div',
|
|
||||||
'attributes' => [
|
|
||||||
'class' => 'table-container'
|
|
||||||
],
|
|
||||||
'handler' => 'element',
|
|
||||||
'text' => $Block['element'],
|
|
||||||
];
|
|
||||||
return $Block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -33,7 +33,7 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$this->priority = $data['priority'] ?? 'Low';
|
$this->priority = $data['priority'] ?? 'Low';
|
||||||
$this->status = $data['status'] ?? 'New';
|
$this->status = $data['status'] ?? 'New';
|
||||||
$this->created_by = $data['created_by'] ?? null;
|
$this->created_by = $data['created_by'] ?? null;
|
||||||
$this->created_at = $data['created_at'] ?? date('Y-m-d H:i:s');
|
$this->created_at = date('Y-m-d H:i:s');
|
||||||
$this->updated_at = date('Y-m-d H:i:s');
|
$this->updated_at = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
$this->save();
|
$this->save();
|
||||||
@ -43,11 +43,10 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
public function updateTicket(array $data): void
|
public function updateTicket(array $data): void
|
||||||
{
|
{
|
||||||
if(isset($data['title'])){ $this->title = $data['title']; }
|
if(isset($data['title'])){ $this->title = $data['title']; }
|
||||||
if(isset($data['description'])) { $this->description = $data['description']; }
|
if(isset($data['description'])) { $this->descrtipion = $data['description']; }
|
||||||
if(isset($data['priority'])) { $this->priority = $data['priority']; }
|
if(isset($data['priority'])) { $this->priority = $data['priority']; }
|
||||||
if(isset($data['status'])) { $this->status = $data['status']; }
|
if(isset($data['status'])) { $this->status = $data['status']; }
|
||||||
if(isset($data['updated_by'])) { $this->updated_by = $data['updated_by']; }
|
if(isset($data['updated_by'])) { $this->status = $data['updated_by']; }
|
||||||
$this->created_at = ($data['created_at'] == '' ? date('Y-m-d H:i:s') : $data['created_at']) ?? date('Y-m-d H:i:s');
|
|
||||||
$this->updated_at = date('Y-m-d H:i:s');
|
$this->updated_at = date('Y-m-d H:i:s');
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,18 @@
|
|||||||
require '../lib/autoload.php';
|
require '../lib/autoload.php';
|
||||||
|
|
||||||
$f3 = \Base::instance();
|
$f3 = \Base::instance();
|
||||||
$f3->config('../app/config/.env.cfg');
|
|
||||||
$f3->set('DEBUG', 3); // development debug
|
|
||||||
$f3->set('CACHE', FALSE);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not required yet
|
* Not required yet
|
||||||
*/
|
*/
|
||||||
$htmlpurifier = \HTMLPurifier::instance();
|
$htmlpurifier = \HTMLPurifier::instance();
|
||||||
// $htmlpurifier->purify($input);
|
// $htmlpurifier->purify($input);
|
||||||
$md = \ParsedownTableExtension::instance();
|
$md = \Parsedown::instance();
|
||||||
$md->setSafeMode(true);
|
$md->setSafeMode(true);
|
||||||
|
|
||||||
|
$f3->config('../app/config/.env.cfg');
|
||||||
|
$f3->set('DEBUG', 3); // development debug
|
||||||
|
$f3->set('CACHE', FALSE);
|
||||||
|
|
||||||
$f3->set('EXT', [new ParsedownHelper, new BulmaFormHelper]);
|
$f3->set('EXT', [new ParsedownHelper, new BulmaFormHelper]);
|
||||||
|
|
||||||
$f3->set('DB', new \DB\SQL(
|
$f3->set('DB', new \DB\SQL(
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
<div class="block">
|
|
||||||
<style>
|
|
||||||
#upload-area {
|
|
||||||
height: 250px;
|
|
||||||
border: 2px dashed #aaa;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
color: #555;
|
|
||||||
font-family: sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-area.hover {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
#preview {
|
|
||||||
max-width:100%;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div id="upload-area" contenteditable="true">
|
|
||||||
Paste or drag an image here
|
|
||||||
</div>
|
|
||||||
<img id="preview" alt="image preview" hidden>
|
|
||||||
<p id="status"></p>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const area = document.getElementById('upload-area');
|
|
||||||
const preview = document.getElementById('preview');
|
|
||||||
const status = document.getElementById('status');
|
|
||||||
|
|
||||||
async function uploadImage(file){
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => {
|
|
||||||
preview.src = reader.result;
|
|
||||||
preview.hidden = false;
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('attachment', file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('/ticket/{{@ticket->id}}/attachments/upload', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
const text = await res.text();
|
|
||||||
status.textContent = text;
|
|
||||||
} catch (e) {
|
|
||||||
status.textContent = 'upload failed.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// paste
|
|
||||||
area.addEventListener('paste', (e) => {
|
|
||||||
for(let item of e.clipboardData.items){
|
|
||||||
if(item.type.startsWith('image/')){
|
|
||||||
uploadImage(item.getAsFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// drag and drop
|
|
||||||
area.addEventListener('dragover', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
area.classList.add('hover');
|
|
||||||
});
|
|
||||||
|
|
||||||
area.addEventListener('dragLeave', ()=> {
|
|
||||||
area.classList.remove('hover');
|
|
||||||
});
|
|
||||||
|
|
||||||
area.addEventListener('drop', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
area.classList.remove('hover');
|
|
||||||
const files = e.dataTransfer.files;
|
|
||||||
for(let file of files){
|
|
||||||
if(file.type.startsWith('image/')){
|
|
||||||
uploadImage(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
@ -28,12 +28,6 @@
|
|||||||
</repeat>
|
</repeat>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr>
|
|
||||||
<div>
|
|
||||||
<repeat group="{{ @attachments }}" value="{{ @attach }}">
|
|
||||||
<img src="/attachment/{{@attach.id}}/view">
|
|
||||||
</repeat>
|
|
||||||
</div>
|
|
||||||
</check>
|
</check>
|
||||||
</check>
|
</check>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
|
|||||||
@ -4,10 +4,9 @@
|
|||||||
<form action="/ticket/create" method="POST">
|
<form action="/ticket/create" method="POST">
|
||||||
|
|
||||||
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value=""></bulma>
|
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value=""></bulma>
|
||||||
<bulma type="H_FIELD_INPUT" label="Created At:" name="created_at" value=""></bulma>
|
|
||||||
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value=""></bulma>
|
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value=""></bulma>
|
||||||
|
|
||||||
<!-- TODO implement priority and status -->
|
|
||||||
<bulma type="H_FIELD_SELECT" label="Priority:" name="priority" options="['Low', 'Medium', 'High']" selected="Medium"></bulma>
|
<bulma type="H_FIELD_SELECT" label="Priority:" name="priority" options="['Low', 'Medium', 'High']" selected="Medium"></bulma>
|
||||||
<bulma type="H_FIELD_SELECT" label="Status:" name="status" options="['New', 'In Progress', 'On Hold', 'Completed']" selected="New"></bulma>
|
<bulma type="H_FIELD_SELECT" label="Status:" name="status" options="['New', 'In Progress', 'On Hold', 'Completed']" selected="New"></bulma>
|
||||||
|
|
||||||
|
|||||||
@ -3,14 +3,12 @@
|
|||||||
<form action="/ticket/{{ @PARAMS.id }}/update" method="POST">
|
<form action="/ticket/{{ @PARAMS.id }}/update" method="POST">
|
||||||
|
|
||||||
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value="{{@ticket.title}}"></bulma>
|
<bulma type="H_FIELD_INPUT" label="Title:" name="title" value="{{@ticket.title}}"></bulma>
|
||||||
<bulma type="H_FIELD_INPUT" label="Created At:" name="created_at" value="{{@ticket.created_at}}"></bulma>
|
|
||||||
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value="{{@ticket.description}}"></bulma>
|
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value="{{@ticket.description}}"></bulma>
|
||||||
|
|
||||||
|
|
||||||
<bulma type="H_FIELD_SELECT" label="Priority:" name="priority" options="['Low', 'Medium', 'High']" selected="{{@ticket.priority}}"></bulma>
|
<bulma type="H_FIELD_SELECT" label="Priority:" name="priority" options="['Low', 'Medium', 'High']" selected="{{@ticket.priority}}"></bulma>
|
||||||
<bulma type="H_FIELD_SELECT" label="Status:" name="status" options="['New', 'In Progress', 'On Hold', 'Completed']" selected="{{@ticket.status}}"></bulma>
|
<bulma type="H_FIELD_SELECT" label="Status:" name="status" options="['New', 'In Progress', 'On Hold', 'Completed']" selected="{{@ticket.status}}"></bulma>
|
||||||
|
|
||||||
<include href="/ui/parts/clipboard.html"></include>
|
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<h3 class="title is-5">Custom Fields</h3>
|
<h3 class="title is-5">Custom Fields</h3>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user