diff options
| author | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-04-24 04:36:57 +0300 |
|---|---|---|
| committer | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-04-24 04:36:57 +0300 |
| commit | 17d243371c43847e9dbdeb21395afc0e8f5437ae (patch) | |
| tree | 7dfe75fe664826f2b1575d13f09e75a500b605fb | |
| parent | 74f1c04022328280b996dde0b7089e13d96acf70 (diff) | |
| download | classroom-17d243371c43847e9dbdeb21395afc0e8f5437ae.tar.gz classroom-17d243371c43847e9dbdeb21395afc0e8f5437ae.tar.bz2 classroom-17d243371c43847e9dbdeb21395afc0e8f5437ae.zip | |
edit lesson: putting all together; media with privile level
| -rw-r--r-- | core/classes/Render.php | 10 | ||||
| -rw-r--r-- | public/app/controllers/Auth.php | 7 | ||||
| -rw-r--r-- | public/app/controllers/admin/Course_admin.php | 282 | ||||
| -rw-r--r-- | public/app/controllers/cms/Course.php | 36 | ||||
| -rw-r--r-- | public/app/models/cms/Course_model.php | 11 | ||||
| -rw-r--r-- | public/app/routes/backend.php | 77 | ||||
| -rw-r--r-- | public/app/routes/frontend.php | 8 | ||||
| -rw-r--r-- | public/app/views/admin/admin-menu.php | 6 | ||||
| -rw-r--r-- | public/app/views/admin/edit_lesson.php | 2 | ||||
| -rw-r--r-- | public/app/views/admin/files.php | 96 | ||||
| -rw-r--r-- | public/app/views/admin/panel.php | 4 | ||||
| -rw-r--r-- | public/app/views/components/header_includes.php | 5 | ||||
| -rw-r--r-- | public/assets/css/class.css | 1 | ||||
| -rw-r--r-- | public/assets/css/overides.css | 20 | ||||
| -rw-r--r-- | public/assets/js/admin/edit_lesson.js | 198 | ||||
| -rw-r--r-- | public/assets/js/admin/files.js | 264 | ||||
| -rw-r--r-- | public/assets/js/admin/panel.js | 1 |
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> Προεπισκόπηση </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> Νέο Επίπεδο + </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 |
