summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/classes/Render.php10
-rw-r--r--public/app/controllers/Auth.php7
-rw-r--r--public/app/controllers/admin/Course_admin.php282
-rw-r--r--public/app/controllers/cms/Course.php36
-rw-r--r--public/app/models/cms/Course_model.php11
-rw-r--r--public/app/routes/backend.php77
-rw-r--r--public/app/routes/frontend.php8
-rw-r--r--public/app/views/admin/admin-menu.php6
-rw-r--r--public/app/views/admin/edit_lesson.php2
-rw-r--r--public/app/views/admin/files.php96
-rw-r--r--public/app/views/admin/panel.php4
-rw-r--r--public/app/views/components/header_includes.php5
-rw-r--r--public/assets/css/class.css1
-rw-r--r--public/assets/css/overides.css20
-rw-r--r--public/assets/js/admin/edit_lesson.js198
-rw-r--r--public/assets/js/admin/files.js264
-rw-r--r--public/assets/js/admin/panel.js1
17 files changed, 873 insertions, 155 deletions
diff --git a/core/classes/Render.php b/core/classes/Render.php
index 75f46d5..3b68c26 100644
--- a/core/classes/Render.php
+++ b/core/classes/Render.php
@@ -344,5 +344,15 @@
}
+ /** render file
+ *
+ */
+ public static function file($path, $madia_type)
+ {
+ $content = file_get_contents($path);
+ header("Content-Type: {$media_type}");
+ echo $content;
+ }
+
} \ No newline at end of file
diff --git a/public/app/controllers/Auth.php b/public/app/controllers/Auth.php
index d7b0c3b..b00843c 100644
--- a/public/app/controllers/Auth.php
+++ b/public/app/controllers/Auth.php
@@ -322,6 +322,13 @@ class Auth {
}
+ public static function is_admin()
+ {
+ $manager = new Classroom_manager();
+ return ($manager->isGranted([1,2,3]));
+ }
+
+
}
diff --git a/public/app/controllers/admin/Course_admin.php b/public/app/controllers/admin/Course_admin.php
index ef9ef0e..d1048b7 100644
--- a/public/app/controllers/admin/Course_admin.php
+++ b/public/app/controllers/admin/Course_admin.php
@@ -98,6 +98,167 @@ class Course_admin {
}
+ ## -------------------------------------------------------------------------
+ ##
+ ## LESSON METHODS
+ ##
+ ## -------------------------------------------------------------------------
+
+
+ /** get_lesson
+ *
+ * @param $id (int) : lesson's id
+ */
+ public static function get_lesson($id)
+ {
+ // get (first) lesson with id = $id; render as json
+ Render::json(Registry::use('database')->query(
+ "SELECT lesson.*,
+ ( SELECT
+ CONCAT('[', GROUP_CONCAT(JSON_OBJECT(
+ 'id', media.id,
+ 'label', media.label,
+ 'type', media.type,
+ 'path', media.path )),
+ ']')
+ FROM media
+ LEFT JOIN lesson_media ON lesson_media.media_id = media.id
+ WHERE lesson_media.lesson_id = :id
+ ) AS medias_json,
+ lesson_privilege.privilege_id
+ FROM lesson
+ LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id
+ WHERE lesson.id = :id",
+ [ ':id' => $id]
+ )->getFirst());
+ }
+
+
+ /** add_lesson
+ * insert a new lesson
+ *
+ * all parametres are passed via $_POST array
+ *
+ * POST @param title
+ * POST @param course_id
+ * POST @param intro
+ * POST @param body
+ * POST @param published
+ */
+ public static function add_lesson()
+ {
+ $post = Registry::get('REQUEST')->POST;
+
+ // insert lesson content into daabase
+ $id = Registry::use('database')->query(
+ "INSERT INTO lesson (title, course_id, intro, `body`, `status`)
+ VALUES (:title, :courseid, :intro, :body, :status)",
+ [
+ ':title' => $post['title'],
+ ':courseid' => $post['course_id'],
+ ':intro' => $post['intro'],
+ ':body' => $post['body'],
+ 'status' => $post['status']
+ ]
+ )->lastInsertID();
+
+ // set lesson privileges to database
+ Registry::use('database')->runQuery(
+ "INSERT INTO lesson_privilege (lesson_id, privilege_id)
+ VALUES (:lesson_id, :privilege_id)",
+ [
+ ':lesson_id' => $id,
+ ':privilege_id' => $post['privilege_id']
+ ]
+ );
+
+ // update media's privileges
+ self::update_medias_privileges($post['media'], $post['privilege_id']);
+
+ // set lesson's media files
+ self::create_medias_for_lesson($post['media'], $id, $post['privilege_id']);
+
+ return true;
+ }
+
+
+ public static function update_lesson()
+ {
+ $post = Registry::get('REQUEST')->POST;
+
+ // print_r($post); die();
+
+ // update lesson's content
+ Registry::use('database')->query(
+ "UPDATE lesson
+ SET title = :title,
+ course_id = :courseid,
+ intro = :intro, `body` = :body,
+ `status` = :status
+ WHERE id = :id",
+ [
+ ':id' => $post['id'],
+ ':title' => $post['title'],
+ ':courseid' => $post['course_id'],
+ ':intro' => $post['intro'],
+ ':body' => $post['body'],
+ ':status' => $post['status']
+ ]
+ );
+
+ // update lesson's privileges
+ Registry::use('database')->runQuery(
+ "UPDATE lesson_privilege SET privilege_id = :privilege_id
+ WHERE lesson_id = :lesson_id",
+ [
+ ':lesson_id' => $post['id'],
+ ':privilege_id' => $post['privilege_id']
+ ]
+ );
+
+ // update media's privileges
+ self::update_medias_privileges($post['media'], $post['privilege_id']);
+
+ // remove all old lesson's links to media files
+ Registry::use('database')->runQuery(
+ "DELETE from lesson_media WHERE lesson_id = :lesson",
+ [ ':lesson' => $post['id'] ]
+ );
+
+ // update lesson's media files
+ self::create_medias_for_lesson($post['media'], $post['id'], $post['privilege_id']);
+
+ return true;
+ }
+
+ /** update medias privileges
+ *
+ * @param $medias (array) : array of media id(s)
+ * @param $privilege (int) : privilege id
+ * @return true (always)
+ */
+ private static function update_medias_privileges($medias, $privilege)
+ {
+ // create WHERE IN (LIST) holders and params for prepare statement
+ $params = [ ':privilege' => $privilege];
+ $holders = [];
+ foreach($medias as $key => $media) {
+ $params[':id'.$key] = $media;
+ $holders[] = ':id'.$key;
+ }
+ $list = '('. implode(',', $holders) .')';
+
+ // print_r(['params' => $params, 'list' => $list]); die();
+
+ Registry::use('database')->runQuery(
+ "UPDATE media SET privilege_id = :privilege
+ WHERE id IN {$list}",
+ $params
+ );
+
+ return true;
+ }
+
## -------------------------------------------------------------------------
@@ -107,18 +268,66 @@ class Course_admin {
## -------------------------------------------------------------------------
+ /** files
+ * echo all files
+ * @return (array)
+ */
+ public static function files()
+ {
+ return proxy( // cache-get files
+ [\app\models\cms\Course_model::class, 'files'],
+ [], CACHE_ROOT_TTL
+ );
+ }
+
+
/** upload_file
* upload the file to the file system
*
* the method reads the POST and FILES array
* to retrieve all needed parametres
*
- * + $_FILES[file]
- * + $_POST[folder] : lesson's ID
- * + $_POST[reference] : reference type
+ * FILES @param file
+ * POST @param folder : lesson's ID or somthing random
+ * POST @param reference : reference type
+ * POST @param
*/
public static function upload_file()
{
+ $request = Registry::get('REQUEST');
+
+ $uploaded = self::upload_to_fs(); // upload file to file-system
+
+ if ($uploaded['success']) {
+
+ $media_id = self::define_media([ // define media in database; get id
+ 'title' => $request->POST['title'],
+ 'type' => $uploaded['type'],
+ 'path' => $uploaded['path']
+ ]);
+
+ Render::json([ // render results as json
+ 'success' => true,
+ 'id' => $media_id,
+ 'title' => $request->POST['title'],
+ 'path' => $uploaded['path'],
+ 'type' => $uploaded['type']
+ ]);
+
+ } else {
+ Render::json(['success' => false ]);
+ }
+ }
+
+
+ /** upload to fs
+ * upload file to File-System
+ *
+ * POST @param folder
+ * FILES @param file
+ */
+ private static function upload_to_fs()
+ {
$post = Registry::get('REQUEST')->POST;
$files = Registry::get('REQUEST')->FILES;
@@ -181,7 +390,7 @@ class Course_admin {
if (move_uploaded_file($files["file"]["tmp_name"], $store_filename)) {
return [
'success' => true,
- 'path' => $store_filename,
+ 'path' => $relative_filename,
'type' => $mime_type
];
@@ -207,22 +416,61 @@ class Course_admin {
*
* create a record in media table
*
- * @param $data
+ * @param $data (array): [title => , path => , type => mime-type]
* @return id (int): id of created media record
*/
- public static function define_media($data)
+ private static function define_media($data)
{
- $sql = "INSERT INTO wiki_media (`title`, `type`, `path`)
- VALUES (:title, :mimetype, :filepath)";
- $stmt = $this->pdo->prepare($sql);
- $stmt->execute([
- 'title' => $data['title'],
- 'mimetype' => $data['type'],
- 'filepath' => $data['path']
- ]);
- $inserted_id = $this->pdo->lastInsertId();
-
- return $inserted_id;
+ $request = Registry::get('REQUEST');
+
+ $media_id = Registry::use('database')->query(
+ "INSERT INTO media (label, `type`, `path`)
+ VALUES (:label, :mimetype, :filepath)",
+ [
+ 'label' => $data['title'],
+ 'mimetype' => $data['type'],
+ 'filepath' => $data['path']
+ ]
+ )->lastInsertID();
+ return $media_id;
+ }
+
+
+ /** create media for post
+ *
+ * links post to each media-file of the media `id`s array
+ * @param $media (array): a list of media-file `id`s
+ * @param $post_id (int)
+ */
+ private static function create_medias_for_lesson($medias, $lesson_id)
+ {
+ foreach($medias as $key => $medi) {
+ self::link_media_to_lesson($medi, $lesson_id); // link to lesson
+ }
+ return true;
+ }
+
+
+ /** link one media-file to a specific post
+ *
+ * NOTE:
+ * the method does not check if media is linked already
+ * so be sure that the pair of (media_id,post_id) not exist
+ *
+ * @param $media_id (int)
+ * @param $lesson_id (int)
+ */
+ private static function link_media_to_lesson( $media_id, $lesson_id )
+ {
+ Registry::use('database')->runQuery(
+ "INSERT INTO lesson_media (lesson_id, media_id)
+ VALUES (:lesson, :media)",
+ [
+ 'lesson' => $lesson_id,
+ 'media' => $media_id,
+ ]
+ );
+ return true;
}
diff --git a/public/app/controllers/cms/Course.php b/public/app/controllers/cms/Course.php
new file mode 100644
index 0000000..b671a5f
--- /dev/null
+++ b/public/app/controllers/cms/Course.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace app\controllers\cms;
+
+use Registry;
+use Render;
+use app\controllers\Auth;
+use app\models\cms\Course_model;
+
+class Course {
+
+ /** serve file by file_path
+ * (request is valid only for admin users)
+ *
+ * @param $file_path (string): relative file path
+ * GET @param type (string) : media-type of file
+ */
+ public static function serve_file($file_path)
+ {
+ // check privileges
+ if (Auth::is_admin()) { // TODO: -OR- has prvivilege
+
+ $media_type = Registry::get('REQUEST')->GET['type']; // get media-type
+ $real_path = MEDIA_STORAGE_ROOT . $file_path; // construct real path
+
+ if (!file_exists($real_path)) {
+ Render::view('error/404');
+
+ } else {
+ Render::file($real_path, $media_type);
+ }
+
+ }
+ }
+
+} \ No newline at end of file
diff --git a/public/app/models/cms/Course_model.php b/public/app/models/cms/Course_model.php
index 4740a4f..3d0ac1c 100644
--- a/public/app/models/cms/Course_model.php
+++ b/public/app/models/cms/Course_model.php
@@ -216,5 +216,16 @@ class Course_model {
}
+ /** files
+ * return all files
+ * @param void
+ * @return array
+ */
+ public static function files()
+ {
+ return Registry::use('database')->runQuery("SELECT * from media", []);
+ }
+
+
}
diff --git a/public/app/routes/backend.php b/public/app/routes/backend.php
index 704d161..7f4717d 100644
--- a/public/app/routes/backend.php
+++ b/public/app/routes/backend.php
@@ -4,6 +4,25 @@ use app\controllers\Auth;
use app\controllers\admin\Course_admin;
use app\models\admin\Privilege_model;
+
+// admin panel
+// ---
+Route::add('/admin', function () {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Render::view('admin/skeleton', [
+ 'title' => 'Διαχείριση Classroom',
+ 'action' => 'panel',
+ ]);
+});
+Route::add('/admin/panel', function () {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Render::view('admin/skeleton', [
+ 'title' => 'Διαχείριση Classroom',
+ 'action' => 'panel',
+ ]);
+});
+
+
// admin lessons
// ---
Route::add('/admin/lessons', function () {
@@ -15,7 +34,7 @@ Route::add('/admin/lessons', function () {
});
-// admin categories (=courses)
+// admin categories (=courses) /////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// manage categories page
@@ -54,7 +73,7 @@ Route::add('/admin/api/categories/update', function () {
);
-// admin privileges
+// admin privileges ////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// manage privileges page
@@ -84,8 +103,12 @@ Route::add('/admin/api/privileges', function () {
-// new lesson
+// admin lesson ////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
+
+
+// new lesson
+// ---
Route::add('/admin/edit_lesson', function () {
Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
Render::view('admin/skeleton', [
@@ -94,18 +117,55 @@ Route::add('/admin/edit_lesson', function () {
]);
});
-// new lesson
-// -----------------------------------------------------------------------------
+// edit lesson (give lesson-id)
+// ---
Route::add('/admin/edit_lesson/([0-9]*)', function ($id) {
Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
Render::view('admin/skeleton', [
- 'title' => 'Νέο Μάθημα',
+ 'title' => 'Επεξεργασία Μαθήματος',
'action' => 'edit_lesson',
'id' => intval($id)
]);
});
+// ajax: get lesson
+// ---
+Route::add('/admin/api/lesson/([0-9]*)', function ($id) {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Course_admin::get_lesson($id);
+});
+
+
+Route::add('/admin/api/lesson/add', function () {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Course_admin::add_lesson();
+}, 'post');
+
+
+Route::add('/admin/api/lesson/update', function () {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Course_admin::update_lesson();
+}, 'post');
+
+
+
+// admin files /////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+Route::add('/admin/files', function () {
+ Auth::allowRoles([1, 2, 3]);
+ Render::view('admin/skeleton', [
+ 'title' => 'Διαχείριση Μέσων (media)',
+ 'action' => 'files',
+ ]);
+});
+// ajax: get lesson
+// ---
+Route::add('/admin/api/files', function () {
+ Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
+ Render::json(Course_admin::files());
+});
// ajax: file-upload
// ---
@@ -113,3 +173,8 @@ Route::add('/admin/api/file_upload', function () {
Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles
Render::json(Course_admin::upload_file());
}, 'post');
+
+
+
+
+
diff --git a/public/app/routes/frontend.php b/public/app/routes/frontend.php
index dde0fcd..898b23a 100644
--- a/public/app/routes/frontend.php
+++ b/public/app/routes/frontend.php
@@ -2,6 +2,7 @@
use app\controllers\Auth;
use app\controllers\Classroom_user;
+use app\controllers\cms\Course;
use app\models\cms\Course_model;
// Home/Welcome
@@ -65,6 +66,13 @@ Route::add('/about/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)',
);
+// serve file
+// ---
+Route::add('/serve/file/([0-9a-zA-Z-_\.\/]*)', function ($path) {
+ Course::serve_file($path);
+});
+
+
/** URL-format Proposal:
* -----------------------------------------------------------------------------
*
diff --git a/public/app/views/admin/admin-menu.php b/public/app/views/admin/admin-menu.php
index 92c2f54..b783b39 100644
--- a/public/app/views/admin/admin-menu.php
+++ b/public/app/views/admin/admin-menu.php
@@ -17,10 +17,12 @@
*/
$admin_options = [
+ 'panel' => 'Διαχείριση',
'edit_lesson' => 'Νέο Μάθημα',
'lessons' => 'Μαθήματα',
'categories' => 'Κεφάλαια',
- 'pages' => 'Σελίδες',
+ // 'pages' => 'Σελίδες',
+ 'files' => 'Αρχεία',
'privileges' => 'Πρόσβαση',
'users' => 'Χρήστες'
];
@@ -28,8 +30,6 @@ $admin_options = [
<div class="top-bar">
- <span class="btn btn-warning">Admin</span>
-
<?php foreach($admin_options as $opt => $label) : ?>
<?php if (($opt == $action) & (!$id)) : ?>
diff --git a/public/app/views/admin/edit_lesson.php b/public/app/views/admin/edit_lesson.php
index 7d74c38..ef875b4 100644
--- a/public/app/views/admin/edit_lesson.php
+++ b/public/app/views/admin/edit_lesson.php
@@ -16,7 +16,7 @@
<div><h5><?=$action_title?></h5></div>
<div>
<button type="button" class="btn pull-right"
- data-toggle="modal" data-target="#editorModal" data-action="preview">
+ data-bs-toggle="modal" data-bs-target="#editorModal" data-action="preview">
<i class="fas fa-search"></i> &nbsp; Προεπισκόπηση
</button>
</div>
diff --git a/public/app/views/admin/files.php b/public/app/views/admin/files.php
new file mode 100644
index 0000000..c6d2771
--- /dev/null
+++ b/public/app/views/admin/files.php
@@ -0,0 +1,96 @@
+ <!-- title bar -->
+ <div class="title-bar">
+ <div><h5>Διαχείριση Επιπέδων Πρόσβασης</h5></div>
+ <div>
+ <!-- Button trigger modal
+ <button type="button" class="btn pull-right"
+ data-bs-toggle="modal" data-bs-target="#managePrivileges" data-id="0">
+ <i class="fas fa-plus"></i> &nbsp; Νέο Επίπεδο
+ </button>
+ -->
+ </div>
+</div>
+
+<div class="manage">
+ <div class="row">
+ <div class="col-md-12">
+
+ <div id="files">
+
+ <table id="dt-files" class="table table-responsive dt-table" style="width:100%">
+ <thead>
+ <th class="dt-id">α/α</th>
+ <th class="dt-label">Περιγραφή</th>
+ <th class="dt-path">Διαδρομή</th>
+ <th class="dt-type">Media-Type</th>
+ <th class="dt-privilege">Πρόσβαση</th>
+ <th class="dt-actions"></th>
+ </thead>
+ </table>
+
+ </div>
+
+
+ </div>
+ </div>
+</div><!-- /manage -->
+
+
+
+<!-- MODAL for new/edit category -->
+<!-- NOTE: tabindex="-1" removed, ref:https://github.com/select2/select2-bootstrap-theme/issues/41 -->
+<div class="modal fade" id="manageFiles" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-dialog modal-lg" role="document">
+ <div class="modal-content">
+
+ <form method="post" action="">
+
+ <div class="modal-header">
+ <h4 class="modal-title" id="myModalLabel">Πρόσβαση αρχείου</h4>
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+ </div><!-- /modal-header -->
+
+ <!-- modal-body -->
+ <div class="modal-body">
+
+ <!-- form fields -->
+
+ <div class="row form-group">
+ <label class="control-label col-sm-12" for="label">Κατηγορία Επιπέδου Πρόσβασης</label>
+ <div class="col-sm-12">
+ <input class="form-control form-control-sm" type="text" name="label" value="" required="">
+ </div>
+ </div>
+
+ <div class="row form-group">
+ <label class="control-label col-sm-12" for="included_id">Κατηγορία Επιπέδου Πρόσβασης</label>
+ <div class="col-sm-12">
+ <select class="form-control form-control-sm form-select form-select-sm" id="privilege_id" name="privilege_id" required></select>
+ </div>
+ </div>
+
+ <div class="row form-group">
+ <input type="hidden" name="id" value="0"><!-- action = (0) ? 'new' : 'edit' -->
+ </div>
+
+
+ </div><!-- /modal body -->
+
+ <div class="modal-footer">
+ <div class="row form-actions">
+
+ <div class="col">
+ <button type="button" class="btn btn-default js-modal-close col-sm-12" data-bs-dismiss="modal">Κλείσιμο</button>
+ </div>
+ <div class="col">
+ <button class="btn btn-success col-sm-12" type="submit">Καταχώριση</button>
+ </div>
+
+ </div>
+ </div><!-- /modal-footer -->
+
+ </form><!-- /form -->
+
+ </div>
+ </div>
+</div>
diff --git a/public/app/views/admin/panel.php b/public/app/views/admin/panel.php
new file mode 100644
index 0000000..59b8cba
--- /dev/null
+++ b/public/app/views/admin/panel.php
@@ -0,0 +1,4 @@
+<br/>
+<br/>
+<br/>
+<h4>Admin panel</h4> \ No newline at end of file
diff --git a/public/app/views/components/header_includes.php b/public/app/views/components/header_includes.php
index e5e51cd..62dc616 100644
--- a/public/app/views/components/header_includes.php
+++ b/public/app/views/components/header_includes.php
@@ -49,7 +49,10 @@
<script src="https://cdn.datatables.net/v/bs5/jszip-2.5.0/dt-1.13.4/b-2.3.6/b-colvis-2.3.6/b-html5-2.3.6/b-print-2.3.6/cr-1.6.2/date-1.4.0/fc-4.2.2/fh-3.3.2/kt-2.8.2/r-2.4.1/rr-1.3.3/sc-2.1.1/sl-1.6.2/sr-1.2.2/datatables.min.js"></script>
<!-- select2 js -->
- <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+
+ <!-- marked -->
+ <script src="/assets/js/marked/marked.min.js"></script>
<?php endif ?>
diff --git a/public/assets/css/class.css b/public/assets/css/class.css
index 598f441..e87861a 100644
--- a/public/assets/css/class.css
+++ b/public/assets/css/class.css
@@ -345,6 +345,7 @@
color: var(--class-btn-blue);
border-bottom: 3px solid var(--class-blue);
padding: 0 8px;
+ text-align: revert-layer;
}
.classroom .article-body table tbody td {
padding: 6px 8px;
diff --git a/public/assets/css/overides.css b/public/assets/css/overides.css
index 568164b..e3d5e1b 100644
--- a/public/assets/css/overides.css
+++ b/public/assets/css/overides.css
@@ -109,3 +109,23 @@ ul.select2-results__options li { font-size: 14px; padding: 4px 8px; }
.admin-container .edit-post textarea[name=intro] { height: 94px ;} /* almost 3 (half-)lines */
+.admin-container .edit-post .post-media button.over-bar { margin: -26px 0 0 0; float: right; }
+.admin-container .edit-post .post-media ul {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ padding: 6px;
+}
+
+.admin-container .edit-post .post-media ul li {
+ border-bottom: 1px solid #ddd;
+ list-style-type: none;
+ padding: 3px 6px;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.admin-container .edit-post .post-media ul li:last-child { border-bottom: none ;}
+.admin-container .edit-post .post-media ul li:hover { background: #eee; }
+
+.edit-post .post-media li .btn-xs { font-size: 14px; width: 24px; height: 24px; padding:0; } \ No newline at end of file
diff --git a/public/assets/js/admin/edit_lesson.js b/public/assets/js/admin/edit_lesson.js
index ba31470..c5b0501 100644
--- a/public/assets/js/admin/edit_lesson.js
+++ b/public/assets/js/admin/edit_lesson.js
@@ -25,7 +25,6 @@ var files = []; // array of { id:.., title:.., type:.., path:.. } records
const files_root = '/media/'; // files root firectory
-var pageUrl = new URL(window.location.href);
// an alternative--id when a lesson_id not exists (needed for file-upload)
// ... consist of a 6-digit date string + random number up to 999
@@ -89,22 +88,9 @@ const upload_file_form = () => {
'<input type="text" class="form-control " value="" name="title" id="title" placeholder="Τίτλος για το Αρχείο" required="">',
'</div>',
- '<div class="row">',
-
- '<div class="col-7"><div class="form-group">',
- '<label class="control-label" for="file">Εισαγωγή Αρχείου</label>',
- '<input class="form-control" type="file" id="file" name="file" required="">',
- '</div></div>',
-
- '<div class="col-5"><div class="form-group">',
- '<label class="control-label" for="category_id">Είδος</label>',
- '<select class="form-control form-select" id="reference" name="reference" required="">',
- '<option"></option>',
- '<option value="1">Παραπομπή (εμφανίζεται)</option>',
- '<option value="0">Βοηθητικό αρχείο (κρυφό)</option>',
- '</select>',
- '</div></div>',
-
+ '<div class="form-group">',
+ '<label class="control-label" for="file">Εισαγωγή Αρχείου</label>',
+ '<input class="form-control" type="file" id="file" name="file" required="">',
'</div>',
'</div>',
@@ -172,6 +158,13 @@ $(document).ready(function() {
width: '100%'
});
+ // inti category (select2)
+ // ...
+ $("#privilege_id").select2({
+ placeholder: 'Επίπεδο Πρόσβασης',
+ width: '100%'
+ });
+
// // init tags (select2)
// // ...
@@ -200,33 +193,25 @@ $(document).ready(function() {
*/
if (id) { // if an `id` is given, load post .. then init_form
- $.getJSON( "/admin/api/", { action: "get_post", id: id } )
+ $.getJSON( "/admin/api/lesson/" + id )
.done( function (data) {
- post = data; // keep post
+ lesson = data; // keep post
// update fields' values
- $('input[name=id]').val(post.id); // id
- $('input[name=title]').val(post.title); // title
- $('textarea[name=intro]').val(post.intro); // intro
- $('textarea[name=body]').val(post.body); // body
+ $('input[name=id]').val(lesson.id); // id
+ $('input[name=title]').val(lesson.title); // title
+ $('textarea[name=intro]').val(lesson.intro); // intro
+ $('textarea[name=body]').val(lesson.body); // body
$('input[name=status]').prop( // status
'checked',
- ((post.status==1)? true : false))
+ ((lesson.status==1)? true : false))
.trigger('change');
- $('#post-info').html('Δημιουργία: '+post.creation_date+'<br>Τελευταία Ενημέρωση: '+ post.update_date);
-
- $('#delete').prop( "disabled", false ); // enable delete button
-
- // if intro exist, show intro (optinal) field
- if ((post.intro != null) && (post.intro != '')) {
- $('.optional label[for=intro]').addClass('show'); // unhide section
- }
+ // $('#post-info').html('Δημιουργία: '+post.creation_date+'<br>Τελευταία Ενημέρωση: '+ post.update_date);
// parse media files; then draw
- if (post.medias_json != null) {
- files = JSON.parse(post.medias_json);
- $('.optional label[for=media]').addClass('show'); // unhide section
+ if (lesson.medias_json != null) {
+ files = JSON.parse(lesson.medias_json);
}
draw_files_list(); // then draw the files list
@@ -234,7 +219,6 @@ $(document).ready(function() {
});
} else { // else ... just init_form
- $('#delete').prop( "disabled", true ); // disable delete button for new post attempt
init_form_values();
}
@@ -265,15 +249,15 @@ $(document).ready(function() {
});
categories.sort( compare_label ); // sort categories by breadcrumb
- // // construct tags data
- // // ---
- // Object.keys(tags_response[0]).forEach( key => {
- // tag = tags_response[0][key];
- // tags.push({
- // id: tag.id,
- // label: tag.name
- // });
- // });
+ // construct tags data
+ // ---
+ /// Object.keys(tags_response[0]).forEach( key => {
+ /// tag = tags_response[0][key];
+ /// tags.push({
+ /// id: tag.id,
+ /// label: tag.name
+ /// });
+ /// });
// construct provileges data
// ---
@@ -294,20 +278,20 @@ $(document).ready(function() {
} else {
// setup category_id options; select post's chosen category-id
- set_parent_options( $('#category_id'), categories, post.category_id );
+ set_parent_options( $('#course_id'), categories, lesson.course_id );
// parse selected tags
// ---
- var selected_tag_ids = [];
- if (post.tags_json != null) {
- var selected_tags = JSON.parse(post.tags_json);
- selected_tags.forEach( itag => { selected_tag_ids.push(itag.id); })
- }
+ /// var selected_tag_ids = [];
+ /// if (post.tags_json != null) {
+ /// var selected_tags = JSON.parse(post.tags_json);
+ /// selected_tags.forEach( itag => { selected_tag_ids.push(itag.id); })
+ /// }
// setup tags options; select post's chosen tags
- set_parent_options( $('#tags'), tags, selected_tag_ids );
+ set_parent_options( $('#privilege_id'), privileges, lesson.privilege_id );
// ALSO: if tags selectd, show tags field
- if (selected_tag_ids.length != 0) { $('.optional label[for=tags]').addClass('show'); }
+ // if (selected_tag_ids.length != 0) { $('.optional label[for=tags]').addClass('show'); }
}
});
@@ -347,28 +331,15 @@ $(document).ready(function() {
var ref_icon, ref_class;
files.forEach( item => {
- // preapre [show|hide]-file as reference
- if (item.reference == 1) {
- ref_icon = '<i class="fas fa-eye"></i>';
- ref_class = 'btn-default';
-
- } else {
- ref_icon = '<i class="fas fa-eye-slash"></i>';
- ref_class = 'btn-disabled';
- }
-
var copy = [ // copy button
'<button type="button" class="btn btn-xs btn-default js-copy"',
- 'data-url="' + item.path + '">',
+ ' data-url="' + item.path +'"',
+ ' data-type="' + item.type + '"',
+ '>',
'<i class="far fa-clone"></i>',
'</button>'
].join('');
- var ref = [ // show|hide (is_reference)
- '<button type="button" class="btn btn-xs '+ ref_class +' js-reference"',
- 'data-id="' + item.id + '">',
- ref_icon,
- '</button>'
- ].join('');
+
var del = [ // delete button
'<button type="button" class="btn btn-xs btn-danger js-delete"',
'data-id="' + item.id + '">',
@@ -377,9 +348,9 @@ $(document).ready(function() {
].join('');
var option = [ // li html
'<li>',
- '<div class="text">' + item.title + '</div>',
+ '<div class="text">' + item.label + '</div>',
'<div class="actions">',
- ref, ' ', copy, ' ', del,
+ copy, ' ', del,
'</div>',
'</li>'
].join('');
@@ -400,20 +371,20 @@ $(document).ready(function() {
$('.edit-post form').submit( event => {
event.preventDefault();
- // rearrange tags to old and new ones
- // ---
- var old_tags = [];
- var new_tags = [];
- var tags = $('#tags').select2('data');
- tags.forEach( t => {
- if (!parseInt(t.id)) {
- new_tags.push(t.text);
- } else { old_tags.push(t.id); }
- })
+ // // rearrange tags to old and new ones
+ // // ---
+ // var old_tags = [];
+ // var new_tags = [];
+ // var tags = $('#tags').select2('data');
+ // tags.forEach( t => {
+ // if (!parseInt(t.id)) {
+ // new_tags.push(t.text);
+ // } else { old_tags.push(t.id); }
+ // })
var attachments = [];
// attachments (array) of "`media_id`;`reference`" strings
- files.forEach( f => { attachments.push( f.id +';'+ f.reference ) });
+ files.forEach( f => { attachments.push( f.id ) });
// prepare data to POST
// ---
@@ -423,7 +394,7 @@ $(document).ready(function() {
course_id: parseInt($('#course_id').select2('data')[0].id),
intro: $('.edit-post form textarea[name=intro]').val(),
body: $('.edit-post form textarea[name=body]').val(),
- status: (($('input[name=status]').is(":checked")) ? 1 : 0),
+ status: ( ($('select[name=status]').val() == "0") ? 0 : 1 ),
privilege_id: parseInt($('#privilege_id').select2('data')[0].id),
// tags: old_tags,
// new_tags: new_tags,
@@ -432,65 +403,38 @@ $(document).ready(function() {
// select action url (add or update)
// ---
- var request = '/wiki/ajax?action=' + ((data.id == 0) ? 'add_post' : 'update_post');
+ var request = '/admin/api/lesson/' + ((data.id == 0) ? 'add' : 'update');
// send POST (ajax) request
$.post(request, data)
.done(function( data ) {
- window.location.href = '/wiki/admin?action=posts'; // seems ok; bach to articles
+ console.log(data);
+ //window.location.href = '/admin/lessons'; // seems ok; bach to articles
});
});
- // go-back (to posts)
+ // go-back (to lessons)
// --- -- -- - - -
$('#go-back').click( event => {
- window.location.href = '/wiki/admin?action=posts';
- });
-
-
- // delete
- // --- -- -- - - -
- $('#delete').click( event => {
- // ask before deletion
- swal({
- title: 'Είστε βέβαιος;',
- html: "Ζητήσατε να διαγραφεί το άρθρο.<br>Παρακαλώ επιβεβαιώστε την διαγραφή.<br>ΠΡΟΣΟΧΗ: Δεν θα έχετε την δυνατότητα αναίρεσης!",
- type: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#3085d6',
- cancelButtonColor: '#d33',
- cancelButtonText: 'Ακύρωση',
- confirmButtonText: 'Ναι, να διαγραφεί',
- showLoaderOnConfirm: true,
- preConfirm: function() {
- return new Promise(function(resolve) {
- $.getJSON( "/wiki/ajax", { action: "delete_post", id: id } )
- .done( function (data) {
- // return to posts-administration
- window.location.href = '/wiki/admin?action=posts';
- });
- });
- },
- allowOutsideClick: false
- }).catch(swal.noop);
-
+ if (confirm('Θέλετε να ακυρώσετε τις όποιες αλλαγές και να φύγτε από τη σελίδα;')) {
+ window.location.href = '/admin/lessons';
+ }
});
-
// copy url button
// --- -- -- - - -
- $('.wiki').on('click', 'button.js-copy', function (e) {
+ $('.admin-container').on('click', 'button.js-copy', function (e) {
var copytext = ''; // default: copy nothing
if ($(this).data('id') !== undefined) {
- copytext = pageUrl.origin +'/wiki/post?id=' + $(this).data('id');
+ copytext = window.location.origin +'/lesson/' + $(this).data('id');
}
if ($(this).data('url') !== undefined) {
- copytext = files_root + $(this).data('url');
+ copytext = window.location.origin + '/serve/file/' + $(this).data('url') + '?type=' + $(this).data('type');
}
navigator.clipboard
@@ -498,6 +442,7 @@ $(document).ready(function() {
.then(() => { // notify the copy event
$(this).addClass('copied');
setTimeout( () => { $(this).removeClass('copied'); }, 700);
+ console.log(copytext, ' copied!');
})
.catch(() => {
console.log("error on coping text");
@@ -508,7 +453,8 @@ $(document).ready(function() {
// delete (remove) file button
// --- -- -- - - -
- $('.wiki').on('click', 'button.js-delete', function (e) {
+ $('.admin-container').on('click', 'button.js-delete', function (e) {
+ console.log('delete!');
var media_id = $(this).data('id');
// remove from files array the one with id == media_id
@@ -521,12 +467,11 @@ $(document).ready(function() {
});
-
-
+
// show|hide file as referece button
// --- -- -- - - -
- $('.wiki').on('click', 'button.js-reference', function (e) {
+ $('.admin-container').on('click', 'button.js-reference', function (e) {
var id = $(this).data('id');
// toggle reference attribute of item with `id` = id
@@ -608,8 +553,7 @@ $(document).ready(function() {
id: response.id,
title: response.title,
path: response.path,
- type: response.type,
- reference: $('#reference').val()
+ type: response.type
});
draw_files_list();
diff --git a/public/assets/js/admin/files.js b/public/assets/js/admin/files.js
new file mode 100644
index 0000000..bfb0c73
--- /dev/null
+++ b/public/assets/js/admin/files.js
@@ -0,0 +1,264 @@
+// Globals
+// -----------------------------------------------------------------------------
+var privileges = [];
+var files = [];
+var table;
+
+
+
+// Supplamentary functions
+// -----------------------------------------------------------------------------
+
+// array.indexOf polyfill
+// --- -- -- - - -
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt /*, from*/)
+ {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
+
+// return file record by file-id
+// --- -- -- - - -
+function privilege_record(id) {
+ var record = 0;
+ files.forEach(el => { if (el.id == id) record = el; });
+
+ return record;
+}
+
+
+// create actions html (edit and delete buttons)
+// --- -- -- - - -
+function create_actions_html(id) {
+
+ var del, edit;
+
+ del = "";
+
+ edit = [
+ '<button type="button" class="btn btn-sm btn-warning"',
+ 'data-bs-toggle="modal" data-bs-target="#manageFiles"',
+ 'data-id="'+ id +'">',
+ '<i class="fas fa-pencil-alt"></i>',
+ '</button>'
+ ].join('\n');
+
+ return edit +' '+ del;
+}
+
+
+
+$(document).ready(function() {
+
+ // When document is ready
+ // -------------------------------------------------------------------------
+
+
+ /** init selec2 element (used to specify parent-category)
+ * --- -- -- - - -
+ */
+ $("#privilege_id").select2({
+ placeholder: 'Επίπεδο πρόσβασης',
+ dropdownParent: $('#manageFiles'),
+ width: '100%',
+ });
+
+
+ /** init_table
+ * --- -- -- - - -
+ * get categories
+ * constuct categories array
+ * render dataTable
+ */
+ function init_table() {
+
+ // get all categories from server (ajax)
+ $.getJSON( "/admin/api/files")
+ .done(function( json ) {
+ // then ...
+
+ // reset categories
+ files.length = 0;
+
+ // construct (new) categories data
+ Object.keys(json).forEach( key => {
+ el = json[key];
+ files.push({
+ id: el.id,
+ label: el.label,
+ path: el.path,
+ type: el.type,
+ privilege_id: ((el.privilege_id == null) ? 0 : el.privilege_id),
+ actions: create_actions_html(el.id)
+ });
+ });
+
+ // sort by breadcrumb
+ // categories.sort( compare_breadcrumb );
+
+ // destroy previous datatable table instances
+ $('#dt-files').dataTable().fnClearTable();
+ $('#dt-files').dataTable().fnDestroy();
+
+ // (re-)create categories datatable
+ table = $('#dt-files').DataTable({
+ language: { url: '/libs/DataTables/localization/Greek.json' },
+ data: files,
+ ordering: false,
+ columns: [
+ { data: 'id' },
+ { data: 'label' },
+ { data: 'path' },
+ { data: 'type' },
+ { data: 'privilege_id' },
+ { data: 'actions' }
+ ]
+ });
+
+ })
+ .fail(function( jqxhr, textStatus, error ) {
+ console.log( "Request Failed (" + error +")" );
+ });
+
+ }
+ init_table(); // init table on first run
+
+
+
+ /** When modal show-up
+ * -------------------------------------------------------------------------
+ * ... prepare the manage-category form
+ * ... update select2 with possible parents
+ */
+ $('#managePrivileges').on('show.bs.modal', event => {
+ var button = $(event.relatedTarget); // Button that triggered the modal
+ var id = parseInt( button.data('id') ); // category_id from data-id
+ // var modal = $(this); // modal object
+
+ // update modal literature
+ // ---
+ $('#managePrivileges .modal-title').html( // modal title
+ (id) ? 'Ενημέρωση Επιπέδου Πρόσβασης' : 'Δημιουργία Επιπέδου Πρόσβασης'
+ );
+ $('#managePrivileges form input[name=id]').val( id ); // category id (hidden)
+ $('#managePrivileges form input[name=label]').val( // category label
+ (id) ? privilege_record(id).label : ""
+ );
+ prepare_included_element(id); // prepare <select> for parents
+ })
+
+ /** prepare_included_element()
+ * ---
+ * @param id (int) : id of selected privilege
+ * -> Get all posible included privileges
+ * -> call set_included_options() to render the options
+ */
+ function prepare_included_element(id) {
+ var may_included = [];
+
+ if (id) { // get all possible parents
+
+ // TODO:
+ // specify forbiden privilege IDs for being included
+
+ // filter privileges adn prepare possible included privileges
+ privileges.forEach( el => {
+ if (id != el.id) {
+ may_included.push({
+ id: el.id,
+ label: el.label
+ });
+ }
+ });
+ console.log(may_included);
+ set_included_options(may_included, privilege_record(id).included_id);
+
+
+ } else { // new record; all categories are possible parrents
+ privileges.forEach( el => {
+ may_included.push({
+ id: el.id,
+ label: el.label,
+ });
+ })
+ set_included_options(may_included, -1);
+ }
+ }
+
+ /** set_included_options()
+ * -> create options for parents <select>
+ * -> choose selected parent
+ * @param may_included (array): array of possible included privileges
+ * @param selected (int): selected included privilege
+ */
+ function set_included_options(may_included, selected) {
+ // sort parents by breadcrumb
+ // parents.sort( compare_breadcrumb );
+
+ // remove any olded options from select2
+ $('#included_id option').each(function() { $(this).remove(); });
+
+ // first option is the root category
+ $('#included_id').append('<option value="0">— κανένα —</option>')
+
+ // add all other parent options
+ may_included.forEach( i => {
+ $('#included_id').append(`<option value="${i.id}">${i.label}</option>`);
+ });
+
+ if (selected == -1) { // new category; remove any parent pre-selection
+ $('#included_id').val(0).trigger('change');
+
+ } else { // category exists; choose selected parent
+ $('#included_id').val(selected).trigger('change');
+ }
+ }
+
+
+
+
+ // When form is submited
+ // -------------------------------------------------------------------------
+
+ $('#manageCategory form').submit( event => {
+ event.preventDefault();
+
+ // prepare data to POST
+ var data = {
+ id: parseInt($('#manageCategory form input[name=id]').val()),
+ label: $('#manageCategory form input[name=label]').val(),
+ parent_id: parseInt($('#parent_id').select2('data')[0].id)
+ };
+
+ // select action url (add or update)
+ var request = '/admin/api/category/' + ((data.id == 0) ? 'add' : 'update');
+
+ // send POST request
+ $.post(request, data)
+ .done(function( data ) {
+
+ $('#manageCategory').modal('hide'); // when done, close modal
+
+ init_table(); // reload table of categories
+ });
+
+ });
+
+});
diff --git a/public/assets/js/admin/panel.js b/public/assets/js/admin/panel.js
new file mode 100644
index 0000000..24b465e
--- /dev/null
+++ b/public/assets/js/admin/panel.js
@@ -0,0 +1 @@
+console.log('Admin Panel!'); \ No newline at end of file