This commit is contained in:
amazuecos
2025-02-22 20:56:06 +01:00
parent 339c979ded
commit f270b6dee8
27 changed files with 981 additions and 92 deletions

View File

@ -5,4 +5,13 @@ use CodeIgniter\Router\RouteCollection;
$routes->group('wiki', ['namespace' => 'App\Controllers\Wiki'], function ($routes) {
$routes->get('','WikiController::index',["as" => "wikiIndex"]);
$routes->get('view/(:segment)','WikiController::show_page/$1',["as" => "showWikiPage"]);
$routes->get('section/(:num)','WikiController::get_section/$1',["as" => "getWikiSection"]);
$routes->post('section','WikiController::store_section/$1',["as" => "storeWikiSection"]);
$routes->post('save/(:num)','WikiController::store_save_page/$1',["as" => "storeWikiSavePage"]);
$routes->post('publish/(:num)','WikiController::store_publish_page/$1',["as" => "storeWikiPublishPage"]);
$routes->post('file/upload/(:num)','WikiController::wiki_file_upload/$1',["as" => "storeWikiFileUpload"]);
$routes->get('file/(:num)','WikiController::get_wiki_file/$1',["as" => "getWikiFile"]);
});

View File

@ -3,12 +3,111 @@
namespace App\Controllers\Wiki;
use App\Controllers\BaseController;
use App\Models\Wiki\WikiContentModel;
use App\Models\Wiki\WikiFileModel;
use App\Models\Wiki\WikiPageModel;
use App\Models\Wiki\WikiSectionModel;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
class WikiController extends BaseController
{
protected WikiSectionModel $wikiSectionModel;
protected WikiContentModel $wikiContentModel;
protected WikiPageModel $wikiPageModel;
protected WikiFileModel $wikiFileModel;
protected string $locale;
protected array $viewData;
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
$this->wikiSectionModel = model(WikiSectionModel::class);
$this->wikiPageModel = model(WikiPageModel::class);
$this->wikiContentModel = model(WikiContentModel::class);
$this->wikiFileModel = model(WikiFileModel::class);
$sections = $this->wikiSectionModel->sections();
$this->locale = session()->get('lang');
$this->viewData['wiki_sections'] = $sections;
parent::initController($request, $response, $logger);
}
public function index()
{
return view('themes/vuexy/wiki/pages/home');
return view('themes/vuexy/wiki/pages/render', $this->viewData);
}
public function show_page(string $slug)
{
$section = $this->wikiSectionModel->where('slug', $slug)->first();
$this->viewData['slug'] = $slug;
$this->viewData['section'] = $section->withAll($this->locale);
return view('themes/vuexy/wiki/pages/render', $this->viewData);
}
public function get_section(int $section_id)
{
$section = $this->wikiSectionModel->find($section_id)->withAll($this->locale);
return $this->response->setJSON(["data" => $section, "message" => lang("App.global_alert_fetch_success")]);
}
public function store_save_page(int $section_id)
{
$bodyData = $this->request->getPost();
$wikiSectionPage = $this->wikiSectionModel->find($section_id)->page();
if ($wikiSectionPage) {
$wikiPageId = $wikiSectionPage->id;
} else {
$wikiPageId = $this->wikiPageModel->insert(['section_id' => $section_id]);
}
$content = $this->wikiContentModel->where('locale', $this->locale)->where('page_id', $wikiPageId)->countAllResults();
if ($content > 0) {
$this->wikiContentModel->where('locale', $this->locale)->where('page_id', $wikiPageId)->delete();
}
$this->wikiContentModel->insert([
"locale" => $this->locale,
"page_id" => $wikiPageId,
"editor_data" => json_encode($bodyData)
]);
return $this->response->setJSON(["data" => [], "message" => lang("App.global_alert_save_success")]);
}
public function wiki_file_upload(int $section_id)
{
try {
$file = $this->request->getFile('image');
$section = $this->wikiSectionModel->find($section_id);
$content = $section->content();
$r = null;
$fullpath = null;
if ($file->isValid() && !$file->hasMoved()) {
$fullpath = $file->store('wiki_images/' . $section->slug);
$r = $this->wikiFileModel->insert(["content_id" => $content->id, "path" => $fullpath]);
return $this->response->setJSON(["success" => 1, "file" => [
"url" => '/wiki/file/'.$r
]]);
}else{
return $this->response->setJSON(["success" => 0, "file" => [
"url" => null
]]);
}
} catch (\Throwable $th) {
return $this->response->setJSON(["success" => 0, "error" => $th->getMessage()])->setStatusCode($th->getCode());
}
}
public function get_wiki_file(int $wiki_file_id)
{
$wikiFile = $this->wikiFileModel->find($wiki_file_id);
if ($wikiFile->path) {
$filePath = WRITEPATH . 'uploads/' . $wikiFile->path;
$mimeType = mime_content_type($filePath);
return $this->response
->setHeader('Content-Type', $mimeType)
->setHeader('Content-Length', filesize($filePath))
->setBody(file_get_contents($filePath));
} else {
return $this->response->setJSON(["message" => "Portada error", "error" => "No hay portada"])->setStatusCode(400);
}
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class WikiSectionsMigration extends Migration
{
protected array $COLUMNS = [
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'slug' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'role' => [
'type' => 'VARCHAR',
'constraint' => 255,
'default' => 'admin'
],
'parent_section_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql('CURRENT_TIMESTAMP');
$this->forge->addField([
'created_at' => [
'type' => 'TIMESTAMP',
'default' => $currenttime,
],
'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
'deleted_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('parent_section_id','wiki_sections','id');
$this->forge->createTable("wiki_sections");
}
public function down()
{
$this->forge->dropTable("wiki_sections");
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class WikiPagesMigration extends Migration
{
protected array $COLUMNS = [
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'section_id' => [
'type' => 'INT',
'unsigned' => true,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql('CURRENT_TIMESTAMP');
$this->forge->addField([
'created_at' => [
'type' => 'TIMESTAMP',
'default' => $currenttime,
],
'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
'deleted_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('section_id','wiki_sections','id');
$this->forge->createTable("wiki_pages");
}
public function down()
{
$this->forge->dropTable("wiki_pages");
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class WikiContentsMigration extends Migration
{
protected array $COLUMNS = [
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'locale' => [
'type' => 'VARCHAR',
'constraint' => 255,
'default' => 'es',
],
'page_id' => [
'type' => 'INT',
'unsigned' => true,
],
'editor_data' => [
'type' => 'LONGTEXT',
'null' => true,
],
'published_data' => [
'type' => 'LONGTEXT',
'null' => true,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql('CURRENT_TIMESTAMP');
$this->forge->addField([
'created_at' => [
'type' => 'TIMESTAMP',
'default' => $currenttime,
],
'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
'deleted_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('page_id','wiki_pages','id');
$this->forge->createTable("wiki_contents");
}
public function down()
{
$this->forge->dropTable("wiki_contents");
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
use CodeIgniter\Database\RawSql;
class WikiFilesMigration extends Migration
{
protected array $COLUMNS = [
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'content_id' => [
'type' => 'INT',
'unsigned' => true,
],
'path' => [
'type' => 'LONGTEXT',
'null' => true,
],
];
public function up()
{
$this->forge->addField($this->COLUMNS);
$currenttime = new RawSql('CURRENT_TIMESTAMP');
$this->forge->addField([
'created_at' => [
'type' => 'TIMESTAMP',
'default' => $currenttime,
],
'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
'deleted_at' => [
'type' => 'TIMESTAMP',
'null' => true,
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('content_id','wiki_contents','id');
$this->forge->createTable("wiki_files");
}
public function down()
{
$this->forge->dropTable("wiki_files");
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Entities\Wiki;
use CodeIgniter\Entity\Entity;
class WikiContentEntity extends Entity
{
protected $attributes = [
"locale" => null,
"page_id" => null,
"editor_data" => null,
"published_data" => null,
];
protected $casts = [
"locale" => "string",
"page_id" => "int",
"editor_data" => "string",
"published_data" => "string",
];
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Entities\Wiki;
use CodeIgniter\Entity\Entity;
class WikiFileEntity extends Entity
{
protected $attributes = [
"content_id" => null,
"path" => null,
];
protected $casts = [
"content_id" => "int",
"path" => "string",
];
}

View File

@ -0,0 +1,15 @@
<?php
namespace App\Entities\Wiki;
use CodeIgniter\Entity\Entity;
class WikiPageEntity extends Entity
{
protected $attributes = [
"section_id" => null,
];
protected $casts = [
"section_id" => "int",
];
}

View File

@ -0,0 +1,59 @@
<?php
namespace App\Entities\Wiki;
use App\Models\Wiki\WikiContentModel;
use App\Models\Wiki\WikiPageModel;
use CodeIgniter\Entity\Entity;
class WikiSectionEntity extends Entity
{
protected $attributes = [
"name" => null,
"slug" => null,
"role" => null,
"parent_id" => null
];
protected $casts = [
"name" => "string",
"slug" => "string",
"role" => "string",
"parent_id" => "int"
];
public function withPage(): self
{
$this->attributes['pages'] = $this->page();
return $this;
}
public function withContents(string $locale = "es"): self
{
$m = model(WikiContentModel::class);
$this->attributes['contents'] = $this->content($locale);
return $this;
}
public function withAll(string $locale = "es") : self
{
$this->withPage();
$this->withContents($locale);
return $this;
}
public function page(): ?WikiPageEntity
{
$m = model(WikiPageModel::class);
return $m->where('section_id',$this->attributes['id'])->first();
}
public function content(string $locale = "es"): ?WikiContentEntity
{
$page = $this->page();
$content = null;
$m = model(WikiContentModel::class);
if($page){
$content = $m->where('page_id',$page->id)
->where('locale',$locale)
->first();
}
return $content;
}
}

View File

@ -19,6 +19,7 @@ return [
"global_come_back" => "Volver",
"global_save" => "Guardar",
"global_alert_save_success" => "¡Guardado exitosamente!",
"global_alert_fetch_success" => "Obtenido exitosamente!",
"global_alert_save_error" => "¡Error al guardar!",
"global_activate" => "Activar",
"global_disable" => "Desactivar",

View File

@ -0,0 +1,53 @@
<?php
namespace App\Models\Wiki;
use App\Entities\Wiki\WikiContentEntity;
use CodeIgniter\Model;
class WikiContentModel extends Model
{
protected $table = 'wiki_contents';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = WikiContentEntity::class;
protected $useSoftDeletes = true;
protected $protectFields = true;
protected $allowedFields = [
"locale",
"page_id",
"editor_data",
"published_data"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Models\Wiki;
use App\Entities\Wiki\WikiFileEntity;
use CodeIgniter\Model;
class WikiFileModel extends Model
{
protected $table = 'wiki_files';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = WikiFileEntity::class;
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
"content_id",
"path"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Models\Wiki;
use App\Entities\Wiki\WikiPageEntity;
use CodeIgniter\Model;
class WikiPageModel extends Model
{
protected $table = 'wiki_pages';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = WikiPageEntity::class;
protected $useSoftDeletes = true;
protected $protectFields = true;
protected $allowedFields = [
"section_id",
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Models\Wiki;
use App\Entities\Wiki\WikiSectionEntity;
use CodeIgniter\Model;
class WikiSectionModel extends Model
{
protected $table = 'wiki_sections';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = WikiSectionEntity::class;
protected $useSoftDeletes = true;
protected $protectFields = true;
protected $allowedFields = [
"name",
"slug",
"role",
"parent_id"
];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
/**
* Get wiki sections
*
* @return array<WikiSectionEntity>
*/
public function sections() : array
{
return $this->where('role','admin')->findAll();
}
}

View File

@ -90,7 +90,38 @@ $picture = "/assets/img/default-user.png";
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<?php include "sidebar.php" ?>
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand">
<a href="<?= site_url('home') ?>" class="app-brand-link">
<span class="app-brand-logo">
<img src="<?= site_url('themes/vuexy/img/safekat/logos/sk-logo.png') ?>" width="150px">
</span>
</a>
<a href="javascript:void(0);" class="layout-menu-toggle menu-link text-large ms-auto">
<i class="ti menu-toggle-icon d-none d-xl-block ti-sm align-middle"></i>
<i class="ti ti-x d-block d-xl-none ti-sm align-middle"></i>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<!-- Iterate throught sections -->
<?php foreach ($wiki_sections as $key => $value) : ?>
<!-- Check if user can view the section link -->
<?php if (auth()->user()->inGroup($value->role)): ?>
<a href="<?= site_url("wiki/render/".$value->slug) ?>" class="menu-link">
<i class="menu-icon tf-icons ti ti-book"></i>
<?= $value->name ?>
</a>
<?php endif; ?>
<?php endforeach; ?>
</li>
</ul>
</aside>
<!-- Layout container -->
<div class="layout-page">
@ -236,7 +267,7 @@ $picture = "/assets/img/default-user.png";
<script src="<?= site_url('themes/vuexy/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/libs/hammer/hammer.js') ?>"></script>
<script src="<?= site_url('themes/vuexy/vendor/js/menu.js') ?>"></script>
<script type="module" src="<?= site_url('assets/js/safekat/pages/layout.js') ?>"></script>
<!-- Helpers -->
<script src="<?= site_url('themes/vuexy/vendor/js/helpers.js') ?>"></script>

View File

@ -1,6 +1,7 @@
<?= $this->include('themes/_commonPartialsBs/select2bs5') ?>
<?= $this->include('themes/_commonPartialsBs/datatables') ?>
<?= $this->include('themes/_commonPartialsBs/_confirm2delete') ?>
<?= $this->include("themes/_commonPartialsBs/sweetalert") ?>
<?= $this->extend('themes/vuexy/wiki/layout') ?>
<?= $this->section('content'); ?>
@ -13,7 +14,12 @@
</div><!--//.card-header -->
<div class="card-body">
<div class="row">
<form action="POST" id="form-wiki">
<input type="hidden" name="slug" id="section-slug">
<input type="hidden" name="wiki_page_id" id="wiki-section-id" value="<?=$section->id?>">
<input type="hidden" name="wiki_page_id" id="wiki-page-id">
<input type="hidden" name="wiki_page_id" id="wiki-content-id">
</form>
<?php if (auth()->user()->inGroup('admin')): ?>
<div class="col-md-10">
<div id="editorjs"></div>
@ -27,6 +33,7 @@
</div>
</div>
<?php else : ?>
<div class="col-md-12">
<div id="editorjs"></div>
</div>
@ -40,7 +47,15 @@
</div><!--//.card -->
</div><!--//.col -->
</div><!--//.row -->
<?= $this->section("additionalExternalJs") ?>
<script type="module" src="<?= site_url('assets/js/safekat/pages/wiki/home.js') ?>"></script>
<?= $this->endSection() ?>
<?= $this->section('css') ?>
<link rel="stylesheet" href="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.css') ?>" />
<?= $this->endSection() ?>
<?= $this->section("additionalExternalJs") ?>
<script src="<?= site_url('themes/vuexy/vendor/libs/sweetalert2/sweetalert2.js') ?>"></script>
<script type="module" src="<?= site_url('assets/js/safekat/pages/wiki/home.js') ?>"></script>
<?= $this->endSection() ?>

View File

@ -17,17 +17,12 @@
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<?php foreach ($wiki_sections as $key => $value) :?>
<a href="<?= site_url("wiki/presupuesto")?>" class="menu-link menu-toggle">
<i class="menu-icon tf-icons ti ti-book"></i>
<?= lang("App.menu_presupuestos") ?>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="<?= site_url("wiki/presupuesto") ?>" class="menu-link">
<?= lang("App.menu_presupuestos") ?>
</a>
</li>
</ul>
<?php endforeach;?>
</li>
</ul>