From a2e68fce808dbe6fba320a6232a15380b63157f6 Mon Sep 17 00:00:00 2001 From: George Halkiadakis Date: Thu, 27 Apr 2023 20:18:01 +0300 Subject: structure simplified --- public/app/controllers/Admin.php | 58 --- public/app/controllers/Auth.php | 15 +- public/app/controllers/Cms.php | 259 ++++++++++ public/app/controllers/CmsAdmin.php | 676 ++++++++++++++++++++++++++ public/app/controllers/admin/.gitkeep | 0 public/app/controllers/admin/Course_admin.php | 676 -------------------------- public/app/controllers/admin/Users_admin.php | 42 -- public/app/controllers/cli/CliContoller.php | 4 +- public/app/controllers/cms/Course.php | 252 ---------- public/app/extends/Cache_service.php | 14 +- public/app/extends/Classroom_manager.php | 4 +- public/app/extends/SendMail_service.php | 212 ++++++++ public/app/extends/Send_mail.php | 212 -------- public/app/models/Access_model.php | 269 ++++++++++ public/app/models/Cms_model.php | 314 ++++++++++++ public/app/models/History_model.php | 34 ++ public/app/models/admin/History_model.php | 34 -- public/app/models/admin/Privilege_model.php | 42 -- public/app/models/admin/User_model.php | 299 ------------ public/app/models/cms/Course_model.php | 314 ------------ public/app/models/cms/Media_model.php | 36 -- public/app/models/cms/Page_model.php | 18 - public/app/models/ideas.md | 24 + public/app/models/todo.md | 50 +- public/app/routes/backend.php | 28 +- public/app/routes/frontend.php | 11 +- 26 files changed, 1843 insertions(+), 2054 deletions(-) delete mode 100644 public/app/controllers/Admin.php create mode 100644 public/app/controllers/Cms.php create mode 100644 public/app/controllers/CmsAdmin.php delete mode 100644 public/app/controllers/admin/.gitkeep delete mode 100644 public/app/controllers/admin/Course_admin.php delete mode 100644 public/app/controllers/admin/Users_admin.php delete mode 100644 public/app/controllers/cms/Course.php create mode 100644 public/app/extends/SendMail_service.php delete mode 100644 public/app/extends/Send_mail.php create mode 100644 public/app/models/Access_model.php create mode 100644 public/app/models/Cms_model.php create mode 100644 public/app/models/History_model.php delete mode 100644 public/app/models/admin/History_model.php delete mode 100644 public/app/models/admin/Privilege_model.php delete mode 100644 public/app/models/admin/User_model.php delete mode 100644 public/app/models/cms/Course_model.php delete mode 100644 public/app/models/cms/Media_model.php delete mode 100644 public/app/models/cms/Page_model.php create mode 100644 public/app/models/ideas.md diff --git a/public/app/controllers/Admin.php b/public/app/controllers/Admin.php deleted file mode 100644 index 6f6858c..0000000 --- a/public/app/controllers/Admin.php +++ /dev/null @@ -1,58 +0,0 @@ -POST['email']); + $record = Access_model::checkUser($req->POST['email']); // if no user exists, return false if ($record === false) return false; @@ -51,7 +50,7 @@ class Auth { if ($userManager->isPasswordValid($user, $req->POST['password'])) { // get user's security attributes - $attributes = User_Model::getUser($record['id']); + $attributes = Access_model::getUser($record['id']); $roles = json_decode($attributes['Roles_json']); $user ->setRoles($roles) @@ -111,7 +110,7 @@ class Auth { $req = Registry::get('REQUEST'); // get the record of the target user - $check = User_model::activate($req->GET['ticket']); + $check = Access_model::activate($req->GET['ticket']); if ($check == true) { Render::view('/error/general', [ @@ -151,7 +150,7 @@ class Auth { ->setPrivileges([]); // none privilege until acount confirmation // create user record - $activation_code = User_model::registerUser($req->POST, $password); + $activation_code = Access_model::registerUser($req->POST, $password); // TODO: // handle error on user registration @@ -164,7 +163,7 @@ class Auth { // ]; // } - $send_mail = Send_mail::send_activation_code([ + $send_mail = SendMail_service::send_activation_code([ 'email' => $req->POST['email'], 'name' => $req->POST['name'] .' '. $req->POST['surname'], 'code' => $activation_code['activation'] diff --git a/public/app/controllers/Cms.php b/public/app/controllers/Cms.php new file mode 100644 index 0000000..306bc88 --- /dev/null +++ b/public/app/controllers/Cms.php @@ -0,0 +1,259 @@ +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); + } + + } else { + Render::view('error/404', [ + 'error_code' => 403, + 'moto' => 'Forbidden', + 'message' => '' + ]); + } + } + + + /** get_file_attributes + * + * returns attributes of a file + * (medias are proxied for speed optimization) + * + * @param $path (string) : file path + * @return $file attributes --or-- false + */ + private static function get_file_attributes($path) + { + $medias = Cache_service::files_attributes(); + + foreach($medias as $key => $medi) { + + if ($medi['path'] == $path) { + + return $medi; + } + } + + return false; + } + + + + ## COURSES + ## ------------------------------------------------------------------------- + + + /** course + * prepare and render the course view + * + * @param $id : course id + */ + public static function course($id) + { + $id = intval($id); // course id + + // collect all data needed for course view + // --- -- -- - - - + $cache = Cache_service::courses_struct(); + $tree = $cache['tree']; + $breadcrumbs = $cache['breadcrumbs']; + $lessons = Cms_model::lessons_of_course(intval($id)); + + // find parent_ids of current category (to open the menu tree) + // --- + $course_path = []; + foreach($breadcrumbs[$id]['parents'] as $key => $parent) { + $course_path[] = intval($parent['id']); + } + $course_path[] = $id; + + // then Render + // --- -- -- - - - + Render::view('templates/course', [ + 'id' => $id, + 'title' => $breadcrumbs[ $id ]['rec']['label'], + 'categories' => $tree, + 'breadcrumbs' => $breadcrumbs, + 'lessons' => $lessons, + 'course_path' => $course_path, + // needed by footer + 'entity' => Cache_service::pages_list() + ]); + } + + + + ## LESSONS + ## ------------------------------------------------------------------------- + + + + /** lesson + * + * check if user is authorized to view the content; + * if so, prepare and render the lesson view + * + * @param $id : lesson id + */ + public static function lesson($id) + { + $id = intval($id); // lesson id + + $lesson = Cms_model::lesson($id); // get lesson + $course_id = $lesson['course_id']; // get course id + $pri_id = $lesson['privilege_id']; // privilege needed + + + if (self::is_admin_or_permited($pri_id)) { + + // collect all data needed for course view + // --- -- -- - - - + $cache = Cache_service::courses_struct(); + $tree = $cache['tree']; + $breadcrumbs = $cache['breadcrumbs']; + + // find parent_ids of current category (to open the menu tree) + // --- + $course_path = []; + foreach($breadcrumbs[$course_id]['parents'] as $key => $parent) { + $course_path[] = intval($parent['id']); + } + $course_path[] = intval($course_id); + + // NOTE: CRITICAL: + // do not pass Class::method directly to the eported variables + // $entity = Cache_service::pages_list(); + + // var_dump($entity); die(); + + // then Render + // --- -- -- - - - + Render::view('templates/lesson', [ + // main content + 'id' => $id, + 'course_id' => $course_id, + 'title' => $lesson['title'], + 'lesson' => $lesson, + // needed for side panel and breadcrunbs + 'categories' => $tree, + 'breadcrumbs' => $breadcrumbs, + 'course_path' => $course_path, + // needed by footer + 'entity' => Cache_service::pages_list() + ]); + + } else { // user is not authorized + Render::view('error/general', [ + 'title' => 'Δεν έχετε πρόσβαση', + 'message' => 'Θα πρέπει να αγοράσετε το πακέτο πρόσβασης ' + . $pri_id + .' για να δείτε το περιεχόμενο! +

+ Αγόρασε τώρα το ' + ]); + } + + } + + + ## PAGES + ## ------------------------------------------------------------------------- + + + /** lesson + * + * check if user is authorized to view the content; + * if so, prepare and render the lesson view + * + * @param $id : lesson id + */ + public static function page($id) + { + $id = intval($id); // lesson id + + $pages = Cache_service::pages_list(); + $page = $pages[$id]; + + // then Render + // --- -- -- - - - + Render::view('templates/page', [ + // main content + 'id' => $id, + 'page' => $page, + // needed by footer + 'entity' => $pages + ]); + + } + +} diff --git a/public/app/controllers/CmsAdmin.php b/public/app/controllers/CmsAdmin.php new file mode 100644 index 0000000..9629741 --- /dev/null +++ b/public/app/controllers/CmsAdmin.php @@ -0,0 +1,676 @@ +query( + "INSERT INTO course (parent_id, label) VALUES (:parent, :label)", + [ + ':parent' => Registry::get('REQUEST')->POST['parent_id'], + ':label' => Registry::get('REQUEST')->POST['label'] + ] + )->lastInsertID(); + + $cache = self::update_courses_cache(); // update category caches + return $cache['breadcrumbs']; // return + } + + + /** update course + * --- + * @param void (get data from POST) + */ + public static function update_course() + { + Registry::use('database')->query( + "UPDATE course SET parent_id = :parent, label = :label + WHERE id = :id", + [ + ':id' => Registry::get('REQUEST')->POST['id'], + ':parent' => Registry::get('REQUEST')->POST['parent_id'], + ':label' => Registry::get('REQUEST')->POST['label'] + ] + ); + $cache = self::update_courses_cache(); + return $cache['breadcrumbs']; + } + + + /** update courses cache + * --- -- -- - - - + * Forces re-caching of courses tree + * Shall run if anything changes to courses + * + * @param void + * @return (array): ['tree' => ..., 'breadcrumbs' => ... ] + */ + public static function update_courses_cache() + { + return Cache_service::courses_struct(PROXY_IGNORE_CACHE); + } + + + ## ------------------------------------------------------------------------- + ## + ## LESSON METHODS + ## + ## ------------------------------------------------------------------------- + + + /** all lessons + * + */ + public static function all_lessons() + { + Render::json(Registry::use('database')->runQuery( + "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson + LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id", + [] + )); + } + + + /** 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'] + ] + ); + + if (isset($post['media'])) { + // update media's privileges; NOTE: media table + self::update_medias_privileges($post['media'], $post['privilege_id']); + + // set lesson's media files; NOTE: lesson_media table + // ERROR: self::create_medias_for_lesson($post['media'], $id, $post['privilege_id']); + self::create_medias_for_lesson($post['media'], $id); + + // recache media + Cache_service::files_attributes(PROXY_IGNORE_CACHE); + } + + 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'] + ] + ); + + if (isset($post['media'])) { + // 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'] ] + ); + + if (isset($post['media'])) { + // update lesson's media files + // ERROR: self::create_medias_for_lesson($post['media'], $post['id'], $post['privilege_id']); + self::create_medias_for_lesson($post['media'], $post['id']); + + // recache media + Cache_service::files_attributes(PROXY_IGNORE_CACHE); + } + + return true; + } + + + ## ------------------------------------------------------------------------- + ## + ## FILE METHODS + ## + ## ------------------------------------------------------------------------- + + + /** files + * echo all files + * + * @return (array) + */ + public static function files() + { + return Cache_service::files_attributes(); + } + + + /** upload_file + * upload the file to the file system + * + * the method reads the POST and FILES array + * to retrieve all needed parametres + * + * 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; + + + // Checks before uploading the file + //////////////////////////////////////////////////////////////////////// + + // ** 1: file is upladed to temporary folder --------------------------- + if (! is_uploaded_file($files['file']['tmp_name'])) { + return ['success' => false]; // bye! + } + + // ** 2: File belongs to the allowed MIME types ------------------------ + $allowed_file_types = array( + 'application/pdf', + 'image/png', 'image/jpeg', + 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ); + // Recomended MIME type checking via mime_content_type(): + $mime_type = mime_content_type($files['file']['tmp_name']); + if (! in_array($mime_type, $allowed_file_types)) { // File type NOT allowed ... + return ['success' => false]; // bye! + } + + $file_name = $files['file']['name']; + $file_type = $files['file']['type']; // do not take it for granted + $file_size = $files['file']['size']; + $file_tmp = $files['file']['tmp_name']; + + $bare_name = pathinfo($file_name, PATHINFO_FILENAME); + $file_ext = pathinfo($file_name, PATHINFO_EXTENSION); + + + // ** 3: filename or size checks may be added -------------------------- + if ($file_name == "") { + return ['success' => false]; // bye! + } + + + // READY to finaly save/upload the file to CDN ///////////////////////// + + $folder = MEDIA_STORAGE_ROOT . $post['folder']; + if (!file_exists($folder)) { // create folder if not exists + mkdir($folder, 0757, true); + } + + // print_r([ + // 'dir' => $folder, + // 'file' => $bare_name, + // 'ext' => $file_ext, + // 'type' => $file_type + // ]); die(); + + $relative_filename = $post['folder'] + .'/' + . strtolower(self::clear_file_name($bare_name) .'.'. $file_ext); + $store_filename = MEDIA_STORAGE_ROOT . $relative_filename; + + if (move_uploaded_file($files["file"]["tmp_name"], $store_filename)) { + return [ + 'success' => true, + 'path' => $relative_filename, + 'type' => $mime_type + ]; + + } else { return ['success' => false]; } + + } + + + /** clear_file_name + * replace greek characters and strip symbols + */ + private static function clear_file_name($str) + { + $el = mb_split( "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωάέήίόύώϊϋς ", ""); + $en = str_split("ABGDEZHUIKLMNJOPRSTYFXCVabgdezhuiklmnjoprstyfxcvaehioyviys-"); + $strip = str_split("!@#$%^&*()+~`[]{};'/<>?=\""); + + return str_replace($strip, '', str_replace($el, $en, $str)); + } + + + /** define_media + * + * create a record in media table + * + * @param $data (array): [title => , path => , type => mime-type] + * @return id (int): id of created media record + */ + private static function define_media($data) + { + $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 lesson + * + * links lesson to each media-file of the media `id`s array + * + * @param $media (array): a list of media-file `id`s + * @param $lesson_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; + } + + + /** create media for page + * + * links page to each media-file of the media `id`s array + * + * @param $media (array): a list of media-file `id`s + * @param $page_id (int) + */ + private static function create_medias_for_page($medias, $page_id) + { + foreach($medias as $key => $medi) { + self::link_media_to_page($medi, $page_id); // link to page + } + 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; + } + + /** link one media-file to a specific page + * + * NOTE: + * the method does not check if media is linked already + * so be sure that the pair of (media_id, page_id) not exist + * + * @param $media_id (int) + * @param $page_id (int) + */ + private static function link_media_to_page( $media_id, $page_id ) + { + Registry::use('database')->runQuery( + "INSERT INTO page_media (page_id, media_id) + VALUES (:page, :media)", + [ + 'page' => $page_id, + 'media' => $media_id, + ] + ); + return true; + } + + + /** update medias privileges + * + * UPDATE media SET privige WHERE IN {list} + * + * @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; + } + + + + + + + ## ------------------------------------------------------------------------- + ## + ## PAGE METHODS + ## + ## ------------------------------------------------------------------------- + + + /** all pages + * + */ + public static function all_pages() + { + Render::json(Registry::use('database')->runQuery( + "SELECT * FROM page", [] + )); + } + + + /** get_page + * + * @param $id (int) : page's id + */ + public static function get_page($id) + { + // get (first) page with id = $id; render as json + Render::json(Registry::use('database')->query( + "SELECT page.*, + ( SELECT + CONCAT('[', GROUP_CONCAT(JSON_OBJECT( + 'id', media.id, + 'label', media.label, + 'type', media.type, + 'path', media.path )), + ']') + FROM media + LEFT JOIN page_media ON page_media.media_id = media.id + WHERE page_media.page_id = :id + ) AS medias_json + FROM page + WHERE page.id = :id", + [ ':id' => $id] + )->getFirst()); + } + + + /** add_page + * insert a new page + * + * all parametres are passed via $_POST array + * + * POST @param title + * POST @param body + * POST @param status (int): 0 = draft , 1 = published + */ + public static function add_page() + { + $post = Registry::get('REQUEST')->POST; + + // insert page content into daabase + $id = Registry::use('database')->query( + "INSERT INTO page (title, `body`, `status`) + VALUES (:title, :body, :status)", + [ + ':title' => $post['title'], + ':body' => $post['body'], + 'status' => $post['status'] + ] + )->lastInsertID(); + + + if (isset($post['media'])) { + // update media's privileges; + // NOTE: media table; pages have public files (privilege_id=0) + self::update_medias_privileges($post['media'], 0); + + // set page's media files + // NOTE: page_media table + self::create_medias_for_page($post['media'], $id); + } + + self::update_pages_cache(); // update pages cache + + return true; + } + + + /** update_page + * + * POST @param id (int) + * POST @param title + * POST @param body + * POST @param status (int): 0 = draft , 1 = published + */ + public static function update_page() + { + $post = Registry::get('REQUEST')->POST; + + // print_r($post); die(); + + // update page's content + Registry::use('database')->query( + "UPDATE page + SET title = :title, `body` = :body, `status` = :status + WHERE id = :id", + [ + ':id' => $post['id'], + ':title' => $post['title'], + ':body' => $post['body'], + ':status' => $post['status'] + ] + ); + + if (isset($post['media'])) { + // update media's privileges + self::update_medias_privileges($post['media'], 0); + } + + // remove all old page's links to media files + Registry::use('database')->runQuery( + "DELETE from page_media WHERE page_id = :page", + [ ':page' => $post['id'] ] + ); + + if (isset($post['media'])) { + // update page's media files + self::create_medias_for_page($post['media'], $post['id']); + } + + self::update_pages_cache(); // update pages cache + + return true; + } + + + /** update pages cache + * --- -- -- - - - + * Forces re-caching of pages tree + * Shall run if anything changes to courses + * + * @param void + * @return (array): ['tree' => ..., 'breadcrumbs' => ... ] + */ + public static function update_pages_cache() + { + return Cache_service::pages_list(PROXY_IGNORE_CACHE); + } + +} \ No newline at end of file diff --git a/public/app/controllers/admin/.gitkeep b/public/app/controllers/admin/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/public/app/controllers/admin/Course_admin.php b/public/app/controllers/admin/Course_admin.php deleted file mode 100644 index fcf38c9..0000000 --- a/public/app/controllers/admin/Course_admin.php +++ /dev/null @@ -1,676 +0,0 @@ -query( - "INSERT INTO course (parent_id, label) VALUES (:parent, :label)", - [ - ':parent' => Registry::get('REQUEST')->POST['parent_id'], - ':label' => Registry::get('REQUEST')->POST['label'] - ] - )->lastInsertID(); - - $cache = self::update_courses_cache(); // update category caches - return $cache['breadcrumbs']; // return - } - - - /** update course - * --- - * @param void (get data from POST) - */ - public static function update_course() - { - Registry::use('database')->query( - "UPDATE course SET parent_id = :parent, label = :label - WHERE id = :id", - [ - ':id' => Registry::get('REQUEST')->POST['id'], - ':parent' => Registry::get('REQUEST')->POST['parent_id'], - ':label' => Registry::get('REQUEST')->POST['label'] - ] - ); - $cache = self::update_courses_cache(); - return $cache['breadcrumbs']; - } - - - /** update courses cache - * --- -- -- - - - - * Forces re-caching of courses tree - * Shall run if anything changes to courses - * - * @param void - * @return (array): ['tree' => ..., 'breadcrumbs' => ... ] - */ - public static function update_courses_cache() - { - return Cache_service::courses_struct(PROXY_IGNORE_CACHE); - } - - - ## ------------------------------------------------------------------------- - ## - ## LESSON METHODS - ## - ## ------------------------------------------------------------------------- - - - /** all lessons - * - */ - public static function all_lessons() - { - Render::json(Registry::use('database')->runQuery( - "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson - LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id", - [] - )); - } - - - /** 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'] - ] - ); - - if (isset($post['media'])) { - // update media's privileges; NOTE: media table - self::update_medias_privileges($post['media'], $post['privilege_id']); - - // set lesson's media files; NOTE: lesson_media table - // ERROR: self::create_medias_for_lesson($post['media'], $id, $post['privilege_id']); - self::create_medias_for_lesson($post['media'], $id); - - // recache media - Cache_service::files_attributes(PROXY_IGNORE_CACHE); - } - - 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'] - ] - ); - - if (isset($post['media'])) { - // 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'] ] - ); - - if (isset($post['media'])) { - // update lesson's media files - // ERROR: self::create_medias_for_lesson($post['media'], $post['id'], $post['privilege_id']); - self::create_medias_for_lesson($post['media'], $post['id']); - - // recache media - Cache_service::files_attributes(PROXY_IGNORE_CACHE); - } - - return true; - } - - - ## ------------------------------------------------------------------------- - ## - ## FILE METHODS - ## - ## ------------------------------------------------------------------------- - - - /** files - * echo all files - * - * @return (array) - */ - public static function files() - { - return Cache_service::files_attributes(); - } - - - /** upload_file - * upload the file to the file system - * - * the method reads the POST and FILES array - * to retrieve all needed parametres - * - * 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; - - - // Checks before uploading the file - //////////////////////////////////////////////////////////////////////// - - // ** 1: file is upladed to temporary folder --------------------------- - if (! is_uploaded_file($files['file']['tmp_name'])) { - return ['success' => false]; // bye! - } - - // ** 2: File belongs to the allowed MIME types ------------------------ - $allowed_file_types = array( - 'application/pdf', - 'image/png', 'image/jpeg', - 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ); - // Recomended MIME type checking via mime_content_type(): - $mime_type = mime_content_type($files['file']['tmp_name']); - if (! in_array($mime_type, $allowed_file_types)) { // File type NOT allowed ... - return ['success' => false]; // bye! - } - - $file_name = $files['file']['name']; - $file_type = $files['file']['type']; // do not take it for granted - $file_size = $files['file']['size']; - $file_tmp = $files['file']['tmp_name']; - - $bare_name = pathinfo($file_name, PATHINFO_FILENAME); - $file_ext = pathinfo($file_name, PATHINFO_EXTENSION); - - - // ** 3: filename or size checks may be added -------------------------- - if ($file_name == "") { - return ['success' => false]; // bye! - } - - - // READY to finaly save/upload the file to CDN ///////////////////////// - - $folder = MEDIA_STORAGE_ROOT . $post['folder']; - if (!file_exists($folder)) { // create folder if not exists - mkdir($folder, 0757, true); - } - - // print_r([ - // 'dir' => $folder, - // 'file' => $bare_name, - // 'ext' => $file_ext, - // 'type' => $file_type - // ]); die(); - - $relative_filename = $post['folder'] - .'/' - . strtolower(self::clear_file_name($bare_name) .'.'. $file_ext); - $store_filename = MEDIA_STORAGE_ROOT . $relative_filename; - - if (move_uploaded_file($files["file"]["tmp_name"], $store_filename)) { - return [ - 'success' => true, - 'path' => $relative_filename, - 'type' => $mime_type - ]; - - } else { return ['success' => false]; } - - } - - - /** clear_file_name - * replace greek characters and strip symbols - */ - private static function clear_file_name($str) - { - $el = mb_split( "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωάέήίόύώϊϋς ", ""); - $en = str_split("ABGDEZHUIKLMNJOPRSTYFXCVabgdezhuiklmnjoprstyfxcvaehioyviys-"); - $strip = str_split("!@#$%^&*()+~`[]{};'/<>?=\""); - - return str_replace($strip, '', str_replace($el, $en, $str)); - } - - - /** define_media - * - * create a record in media table - * - * @param $data (array): [title => , path => , type => mime-type] - * @return id (int): id of created media record - */ - private static function define_media($data) - { - $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 lesson - * - * links lesson to each media-file of the media `id`s array - * - * @param $media (array): a list of media-file `id`s - * @param $lesson_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; - } - - - /** create media for page - * - * links page to each media-file of the media `id`s array - * - * @param $media (array): a list of media-file `id`s - * @param $page_id (int) - */ - private static function create_medias_for_page($medias, $page_id) - { - foreach($medias as $key => $medi) { - self::link_media_to_page($medi, $page_id); // link to page - } - 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; - } - - /** link one media-file to a specific page - * - * NOTE: - * the method does not check if media is linked already - * so be sure that the pair of (media_id, page_id) not exist - * - * @param $media_id (int) - * @param $page_id (int) - */ - private static function link_media_to_page( $media_id, $page_id ) - { - Registry::use('database')->runQuery( - "INSERT INTO page_media (page_id, media_id) - VALUES (:page, :media)", - [ - 'page' => $page_id, - 'media' => $media_id, - ] - ); - return true; - } - - - /** update medias privileges - * - * UPDATE media SET privige WHERE IN {list} - * - * @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; - } - - - - - - - ## ------------------------------------------------------------------------- - ## - ## PAGE METHODS - ## - ## ------------------------------------------------------------------------- - - - /** all pages - * - */ - public static function all_pages() - { - Render::json(Registry::use('database')->runQuery( - "SELECT * FROM page", [] - )); - } - - - /** get_page - * - * @param $id (int) : page's id - */ - public static function get_page($id) - { - // get (first) page with id = $id; render as json - Render::json(Registry::use('database')->query( - "SELECT page.*, - ( SELECT - CONCAT('[', GROUP_CONCAT(JSON_OBJECT( - 'id', media.id, - 'label', media.label, - 'type', media.type, - 'path', media.path )), - ']') - FROM media - LEFT JOIN page_media ON page_media.media_id = media.id - WHERE page_media.page_id = :id - ) AS medias_json - FROM page - WHERE page.id = :id", - [ ':id' => $id] - )->getFirst()); - } - - - /** add_page - * insert a new page - * - * all parametres are passed via $_POST array - * - * POST @param title - * POST @param body - * POST @param status (int): 0 = draft , 1 = published - */ - public static function add_page() - { - $post = Registry::get('REQUEST')->POST; - - // insert page content into daabase - $id = Registry::use('database')->query( - "INSERT INTO page (title, `body`, `status`) - VALUES (:title, :body, :status)", - [ - ':title' => $post['title'], - ':body' => $post['body'], - 'status' => $post['status'] - ] - )->lastInsertID(); - - - if (isset($post['media'])) { - // update media's privileges; - // NOTE: media table; pages have public files (privilege_id=0) - self::update_medias_privileges($post['media'], 0); - - // set page's media files - // NOTE: page_media table - self::create_medias_for_page($post['media'], $id); - } - - self::update_pages_cache(); // update pages cache - - return true; - } - - - /** update_page - * - * POST @param id (int) - * POST @param title - * POST @param body - * POST @param status (int): 0 = draft , 1 = published - */ - public static function update_page() - { - $post = Registry::get('REQUEST')->POST; - - // print_r($post); die(); - - // update page's content - Registry::use('database')->query( - "UPDATE page - SET title = :title, `body` = :body, `status` = :status - WHERE id = :id", - [ - ':id' => $post['id'], - ':title' => $post['title'], - ':body' => $post['body'], - ':status' => $post['status'] - ] - ); - - if (isset($post['media'])) { - // update media's privileges - self::update_medias_privileges($post['media'], 0); - } - - // remove all old page's links to media files - Registry::use('database')->runQuery( - "DELETE from page_media WHERE page_id = :page", - [ ':page' => $post['id'] ] - ); - - if (isset($post['media'])) { - // update page's media files - self::create_medias_for_page($post['media'], $post['id']); - } - - self::update_pages_cache(); // update pages cache - - return true; - } - - - /** update pages cache - * --- -- -- - - - - * Forces re-caching of pages tree - * Shall run if anything changes to courses - * - * @param void - * @return (array): ['tree' => ..., 'breadcrumbs' => ... ] - */ - public static function update_pages_cache() - { - return Cache_service::pages_list(PROXY_IGNORE_CACHE); - } - -} \ No newline at end of file diff --git a/public/app/controllers/admin/Users_admin.php b/public/app/controllers/admin/Users_admin.php deleted file mode 100644 index 4e335c1..0000000 --- a/public/app/controllers/admin/Users_admin.php +++ /dev/null @@ -1,42 +0,0 @@ -runQuery( - "SELECT * FROM privilege",[] - ); - } - - /** edit privileges - * --- - */ - public static function edit_privileges() - { - // TODO: ... - } - - - /** update course - * --- - */ - public static function update_course() - { - // TODO: ... - } - -} \ No newline at end of file diff --git a/public/app/controllers/cli/CliContoller.php b/public/app/controllers/cli/CliContoller.php index 4549b50..949985d 100644 --- a/public/app/controllers/cli/CliContoller.php +++ b/public/app/controllers/cli/CliContoller.php @@ -39,12 +39,12 @@ class CliContoller if (php_sapi_name() != 'cli') return false; # PROXY call: - # CMS \ Course_model::courses_struct(): + # CMS \ Cms_model::courses_struct(): # + Refresh Cache # -------------------------------------- echo textColor("Creating category_products tree Cache ... ", \NORMAL); $reply = proxy( - [\app\models\cms\Course_model::class, 'courses_struct'], + [\app\models\Cms_model::class, 'courses_struct'], [], \CACHE_ROOT_TTL, \PROXY_IGNORE_CACHE ); diff --git a/public/app/controllers/cms/Course.php b/public/app/controllers/cms/Course.php deleted file mode 100644 index 47eac5f..0000000 --- a/public/app/controllers/cms/Course.php +++ /dev/null @@ -1,252 +0,0 @@ -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); - } - - } else { - Render::view('error/404', [ - 'error_code' => 403, - 'moto' => 'Forbidden', - 'message' => '' - ]); - } - } - - - /** get_file_attributes - * - * returns attributes of a file - * (medias are proxied for speed optimization) - * - * @param $path (string) : file path - * @return $file attributes --or-- false - */ - private static function get_file_attributes($path) - { - $medias = Cache_service::files_attributes(); - - foreach($medias as $key => $medi) { - - if ($medi['path'] == $path) { - - return $medi; - } - } - - return false; - } - - - - /** COURSES - * ------------------------------------------------------------------------- - */ - - /** course - * prepare and render the course view - * - * @param $id : course id - */ - public static function course($id) - { - $id = intval($id); // course id - - // collect all data needed for course view - // --- -- -- - - - - $cache = Cache_service::courses_struct(); - $tree = $cache['tree']; - $breadcrumbs = $cache['breadcrumbs']; - $lessons = Course_model::lessons_of_course(intval($id)); - - // find parent_ids of current category (to open the menu tree) - // --- - $course_path = []; - foreach($breadcrumbs[$id]['parents'] as $key => $parent) { - $course_path[] = intval($parent['id']); - } - $course_path[] = $id; - - // then Render - // --- -- -- - - - - Render::view('templates/course', [ - 'id' => $id, - 'title' => $breadcrumbs[ $id ]['rec']['label'], - 'categories' => $tree, - 'breadcrumbs' => $breadcrumbs, - 'lessons' => $lessons, - 'course_path' => $course_path, - // needed by footer - 'entity' => Cache_service::pages_list() - ]); - } - - - - /** LESSONS - * ------------------------------------------------------------------------ - */ - - - /** lesson - * - * check if user is authorized to view the content; - * if so, prepare and render the lesson view - * - * @param $id : lesson id - */ - public static function lesson($id) - { - $id = intval($id); // lesson id - - $lesson = Course_model::lesson($id); // get lesson - $course_id = $lesson['course_id']; // get course id - $pri_id = $lesson['privilege_id']; // privilege needed - - - if (self::is_admin_or_permited($pri_id)) { - - // collect all data needed for course view - // --- -- -- - - - - $cache = Cache_service::courses_struct(); - $tree = $cache['tree']; - $breadcrumbs = $cache['breadcrumbs']; - - // find parent_ids of current category (to open the menu tree) - // --- - $course_path = []; - foreach($breadcrumbs[$course_id]['parents'] as $key => $parent) { - $course_path[] = intval($parent['id']); - } - $course_path[] = intval($course_id); - - // NOTE: CRITICAL: - // do not pass Class::method directly to the eported variables - // $entity = Cache_service::pages_list(); - - // var_dump($entity); die(); - - // then Render - // --- -- -- - - - - Render::view('templates/lesson', [ - // main content - 'id' => $id, - 'course_id' => $course_id, - 'title' => $lesson['title'], - 'lesson' => $lesson, - // needed for side panel and breadcrunbs - 'categories' => $tree, - 'breadcrumbs' => $breadcrumbs, - 'course_path' => $course_path, - // needed by footer - 'entity' => Cache_service::pages_list() - ]); - - } else { // user is not authorized - Render::view('error/general', [ - 'title' => 'Δεν έχετε πρόσβαση', - 'message' => 'Θα πρέπει να αγοράσετε το πακέτο πρόσβασης ' - . $pri_id - .' για να δείτε το περιεχόμενο! -

- Αγόρασε τώρα το ' - ]); - } - - } - - - /** lesson - * - * check if user is authorized to view the content; - * if so, prepare and render the lesson view - * - * @param $id : lesson id - */ - public static function page($id) - { - $id = intval($id); // lesson id - - $pages = Cache_service::pages_list(); - $page = $pages[$id]; - - // then Render - // --- -- -- - - - - Render::view('templates/page', [ - // main content - 'id' => $id, - 'page' => $page, - // needed by footer - 'entity' => $pages - ]); - - } - - - - -} \ No newline at end of file diff --git a/public/app/extends/Cache_service.php b/public/app/extends/Cache_service.php index 52311f3..c435b75 100644 --- a/public/app/extends/Cache_service.php +++ b/public/app/extends/Cache_service.php @@ -1,8 +1,8 @@ POST); + $register = Access_model::registerUser($req->POST); if ($register['success']) { @@ -83,7 +83,7 @@ class Classroom_manager extends UserManager public function create_OTP($id) { $otp = rand(100000,999999); - User_model::set_OTP($id, $otp); + Access_model::set_OTP($id, $otp); return true; } diff --git a/public/app/extends/SendMail_service.php b/public/app/extends/SendMail_service.php new file mode 100644 index 0000000..c717e61 --- /dev/null +++ b/public/app/extends/SendMail_service.php @@ -0,0 +1,212 @@ + user email + * name => user full name + * subject => subject + * body => email message body + * ] + * + * then send the email; + * + * @param $activator (array): [ + * email => user email, + * name => user full name, + * code => activation code + * ] + * + * @return success_starus (boolean) + * + * TODO: + * check for email templating: + * + https://stackoverflow.com/questions/2391171/how-to-get-output-from-local-script-in-php + * + https://stackoverflow.com/questions/171318/how-do-i-capture-php-output-into-a-variable + */ + public static function send_activation_code($activator) + { + $activation_url = SITE_URL ."/account/activate?ticket=". $activator['code']; + $envelope = [ + 'email' => $activator['email'], + 'name' => $activator['name'], + 'subject' => 'Εγγραφή στο Classroom', + 'body' => "Χαίρετε,
+ το παρόν αυτοποιημένο email σάς έχει σταλεί + γιατί έχει γίνει αίτημα εγγραφής σας στο Classroom.
+
+ Όνομα επαφής: ". $activator['name'] ."
+ Email : ". $activator['email'] ."
+
+ Για να ενεργοποιήσετε την πρόσβασή σας στο site + θα πρέπει ακολουθήσετε τον παρακάτω σύνδεσμο: + ". $activation_url .". +
+
+ Μετά την ενεργοποίηση θα έχετε πρόσσβαση + στο περιεχόμενο του site.
+ Μην απαντήσετε στο email, + δεν υπάρχει φυσική επαφή η οποία να λαμβάνει τυχόν replies." + ]; + + switch (DEFAULT_MAIL_SERVICE) { + case 'mailjet': + $reply = self::send_mailjet($envelope); + break; + + case 'phpMailer': + $reply = self::send_phpMailer($envelope); + break; + } + + return $reply; + + } + + + /** send phpMailer + * + * send email using phpMailer class; + * optional SMTP server configuration; + * + * @param $envelope + * @return success_starus (boolean) + */ + public static function send_phpMailer($envelope) + { + $mail = new PHPMailer(); + + # // setup smpt + $mail->SMTPDebug = 3; + $mail->IsSMTP(); + $mail->Host = MAIL_HOST; + # $mail->SMPTAuth = false; + $mail->SMTPSecure = MAIL_ENCRYPTION; + # // $mail->Protocol = 'mail'; + + $mail->Mailer = MAIL_MAILER; + $mail->Port = MAIL_PORT; + $mail->Username = MAIL_USERNAME; + $mail->Password = MAIL_PASSWORD; + + // setup format + $mail->CharSet = 'utf-8'; + $mail->IsHTML(true); + + // setup THE mail + // --- -- -- - - - + // It's important not to use the submitter's address as the from address + // as it's forgery, which will cause your messages to fail SPF checks. + // Use an address in your own domain as the from address; + // put the submitter's address in a reply-to + + $mail->setFrom(NO_REPLY_EMAIL, MAIL_FROM_NAME); + $mail->addAddress($envelope['email'], $envelope['name']); + $mail->addReplyTo(REPLY_TO_EMAIL, MAIL_FROM_NAME); + $mail->Subject = $envelope['subject']; + $mail->Body = $envelope['body']; + + return (!$mail->send()) ? false : true; + + } + + + /** send_mailjet + * + * send email using CURL mail-relay of Mailjet + * + * @param $envelope + * @return success_starus (boolean) + */ + public static function send_mailjet($envelope) + { + $body = [ + 'Messages' => [ + [ + 'From' => [ + 'Email' => REPLY_TO_EMAIL, + 'Name' => MAIL_FROM_NAME + ], + 'To' => [ + [ + 'Email' => $envelope['email'], + 'Name' => $envelope['name'] + ] + ], + 'Subject' => $envelope['subject'], + 'HTMLPart' => $envelope['body'] + ] + ] + ]; + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, "https://api.mailjet.com/v3.1/send"); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json') + ); + curl_setopt( + $ch, + CURLOPT_USERPWD, + MAILJET_APIKEY. ':'. MAILJET_SECRET + ); + $server_output = curl_exec($ch); + curl_close ($ch); + + $response = json_decode($server_output); + + return ($response->Messages[0]->Status == 'success'); + + } + + + +} + + + + + +/** NOTE: + * ----------------------------------------------------------------------------- + * (brainstorming) + * + +Optimization per concept alternative technologies +--- -- -- - - - + +## Session +File Session +Database Session +Redis Session +Memcached Session + +--- + +## Cache +File Cache +Database Cache +Redis Cache +Memcashed Cache + +--- + +## Log +File Log +Database Log +Log event to Slack +Log event to Email + + * ----------------------------------------------------------------------------- + */ diff --git a/public/app/extends/Send_mail.php b/public/app/extends/Send_mail.php deleted file mode 100644 index 52eab53..0000000 --- a/public/app/extends/Send_mail.php +++ /dev/null @@ -1,212 +0,0 @@ - user email - * name => user full name - * subject => subject - * body => email message body - * ] - * - * then send the email; - * - * @param $activator (array): [ - * email => user email, - * name => user full name, - * code => activation code - * ] - * - * @return success_starus (boolean) - * - * TODO: - * check for email templating: - * + https://stackoverflow.com/questions/2391171/how-to-get-output-from-local-script-in-php - * + https://stackoverflow.com/questions/171318/how-do-i-capture-php-output-into-a-variable - */ - public static function send_activation_code($activator) - { - $activation_url = SITE_URL ."/account/activate?ticket=". $activator['code']; - $envelope = [ - 'email' => $activator['email'], - 'name' => $activator['name'], - 'subject' => 'Εγγραφή στο Classroom', - 'body' => "Χαίρετε,
- το παρόν αυτοποιημένο email σάς έχει σταλεί - γιατί έχει γίνει αίτημα εγγραφής σας στο Classroom.
-
- Όνομα επαφής: ". $activator['name'] ."
- Email : ". $activator['email'] ."
-
- Για να ενεργοποιήσετε την πρόσβασή σας στο site - θα πρέπει ακολουθήσετε τον παρακάτω σύνδεσμο: - ". $activation_url .". -
-
- Μετά την ενεργοποίηση θα έχετε πρόσσβαση - στο περιεχόμενο του site.
- Μην απαντήσετε στο email, - δεν υπάρχει φυσική επαφή η οποία να λαμβάνει τυχόν replies." - ]; - - switch (DEFAULT_MAIL_SERVICE) { - case 'mailjet': - $reply = self::send_mailjet($envelope); - break; - - case 'phpMailer': - $reply = self::send_phpMailer($envelope); - break; - } - - return $reply; - - } - - - /** send phpMailer - * - * send email using phpMailer class; - * optional SMTP server configuration; - * - * @param $envelope - * @return success_starus (boolean) - */ - public static function send_phpMailer($envelope) - { - $mail = new PHPMailer(); - - # // setup smpt - $mail->SMTPDebug = 3; - $mail->IsSMTP(); - $mail->Host = MAIL_HOST; - # $mail->SMPTAuth = false; - $mail->SMTPSecure = MAIL_ENCRYPTION; - # // $mail->Protocol = 'mail'; - - $mail->Mailer = MAIL_MAILER; - $mail->Port = MAIL_PORT; - $mail->Username = MAIL_USERNAME; - $mail->Password = MAIL_PASSWORD; - - // setup format - $mail->CharSet = 'utf-8'; - $mail->IsHTML(true); - - // setup THE mail - // --- -- -- - - - - // It's important not to use the submitter's address as the from address - // as it's forgery, which will cause your messages to fail SPF checks. - // Use an address in your own domain as the from address; - // put the submitter's address in a reply-to - - $mail->setFrom(NO_REPLY_EMAIL, MAIL_FROM_NAME); - $mail->addAddress($envelope['email'], $envelope['name']); - $mail->addReplyTo(REPLY_TO_EMAIL, MAIL_FROM_NAME); - $mail->Subject = $envelope['subject']; - $mail->Body = $envelope['body']; - - return (!$mail->send()) ? false : true; - - } - - - /** send_mailjet - * - * send email using CURL mail-relay of Mailjet - * - * @param $envelope - * @return success_starus (boolean) - */ - public static function send_mailjet($envelope) - { - $body = [ - 'Messages' => [ - [ - 'From' => [ - 'Email' => REPLY_TO_EMAIL, - 'Name' => MAIL_FROM_NAME - ], - 'To' => [ - [ - 'Email' => $envelope['email'], - 'Name' => $envelope['name'] - ] - ], - 'Subject' => $envelope['subject'], - 'HTMLPart' => $envelope['body'] - ] - ] - ]; - - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, "https://api.mailjet.com/v3.1/send"); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, array( - 'Content-Type: application/json') - ); - curl_setopt( - $ch, - CURLOPT_USERPWD, - MAILJET_APIKEY. ':'. MAILJET_SECRET - ); - $server_output = curl_exec($ch); - curl_close ($ch); - - $response = json_decode($server_output); - - return ($response->Messages[0]->Status == 'success'); - - } - - - -} - - - - - -/** NOTE: - * ----------------------------------------------------------------------------- - * (brainstorming) - * - -Optimization per concept alternative technologies ---- -- -- - - - - -## Session -File Session -Database Session -Redis Session -Memcached Session - ---- - -## Cache -File Cache -Database Cache -Redis Cache -Memcashed Cache - ---- - -## Log -File Log -Database Log -Log event to Slack -Log event to Email - - * ----------------------------------------------------------------------------- - */ diff --git a/public/app/models/Access_model.php b/public/app/models/Access_model.php new file mode 100644 index 0000000..d5ae19e --- /dev/null +++ b/public/app/models/Access_model.php @@ -0,0 +1,269 @@ +query( + "SELECT * FROM user WHERE email = :email AND active = 1", + [ ':email' => $email ] + )->getFirst(); + + // if no user, return false + if ($user === false) return false; + + return $user; + } + + + /** get user (by index key) + * + * user detailed array + * includes all user properties + granted roles + privileges + * + * @param $id (int) : user id + * @param $value (string) + */ + public static function getUser($id) + { + $user = Registry::use('database')->query( + "SELECT user.*, + ( -- construct array (json) of roles granted to user + SELECT CONCAT( + '[', + GROUP_CONCAT(role.id), + ']' + ) + FROM `role` + WHERE role.id IN ( + SELECT user_role.role_id + FROM user_role + WHERE user_role.user_id = :id + ) + ) AS Roles_json, + ( -- construct array of (root-)privileges granted to user + SELECT CONCAT( + '[', + GROUP_CONCAT(privilege.id), + ']' + ) + FROM privilege + WHERE privilege.id IN ( + SELECT user_privilege.privilege_id + FROM user_privilege + WHERE user_privilege.user_id = :id + ) + ) AS RootPrivileges_json, + ( -- construct array of (root-)privileges granted to user + SELECT CONCAT( + '[', + GROUP_CONCAT(privilege.includes), + ']' + ) + FROM privilege + WHERE privilege.id IN ( + SELECT user_privilege.privilege_id + FROM user_privilege + WHERE user_privilege.user_id = :id + ) + ) AS SubPrivileges_json + FROM user + WHERE id = :id", + [ ':id' => $id ] + )->getFirst(); + + + // if no user, return false + if ($user === false) return false; + + + // TODO: + // * merge root+sub privilede lists + // * convert json strings to php arrays + + + // TODO: + // cache user super array + + return $user; + } + + + /** create user + * + * creates user record; + * assigns privileged (usualy defaults); + * creates activation_code + * + * @param $data (array): Request->POST array + * @param $password (string): secure hashed password + * + * @return $activation_code + * + */ + public static function registerUser($data, $password, $privileges = DEFAULT_PRIVILEGES) + { + $required_fields = [ + 'name', + 'surname', + 'email', + 'password' + ]; + + // check required fields + $isOK = true; + foreach($required_fields as $fi) { + if (empty($data[$fi])) $isOK = false; + } + // if empty required fields exists ... return false + if (!$isOK) { + return [ "success" => false, 'error' => EMPTY_REQUIRED_FIELDS ]; + } + + // create an activation code + $activation_code = md5($data['email'].time().rand(0, 10000)); + + // if isOK go on and... + // create user record + $new_user_id = Registry::use('database')->query( + "INSERT INTO user + (`first_name`, `last_name`, `email`, `password`, `active`, `activation`) + VALUES + (:nam, :surname, :email, :pass, :act, :actcode)", + [ + ':nam' => $data['name'], + ':surname' => $data['surname'], + ':email' => $data['email'], + ':pass' => $password, + ':act' => 0, // needs email confirmation to be activated ... + ':actcode' => $activation_code // ... with the activation code + ] + )->lastInsertID(); + + // set default privileges + self::set_user_privileges($new_user_id, $privileges); + + // set reader role + self::set_user_role($new_user_id, 5); + + // update history + History_model::trackUserAccess($new_user_id, TRACK_ACCOUNT, 'Create User Account'); + + // return success and user id + return [ + "success" => true, + 'id' => $new_user_id , + 'activation' => $activation_code + ]; + } + + + /** activate + * + * check if activation code is valid; + * if valid, set account active; + * + * @param $ticket (hex/MD5): activation code; + * + */ + public static function activate($ticket) + { + $user = Registry::use('database')->query( + "SELECT * FROM user WHERE activation = :ticket", + [ 'ticket' => $ticket ] + )->getFirst(); + + // if no user with this activation code, return false + if ($user === false) return false; + + // remove activation code from user record + Registry::use('database')->runQuery( + "UPDATE user + SET active = 1, `activation` = NULL + WHERE activation = :ticket", + [ 'ticket' => $ticket ] + ); + + // update history + History_model::trackUserAccess($user['id'], TRACK_ACCOUNT, 'User Account Activated'); + + return true; + + } + + + + ## Set permission methods + ## ------------------------------------------------------------------------- + + + /** set_user_privileges + * + * @param $privileges (array) + */ + public static function set_user_privileges($user, $privileges) + { + $db = Registry::use('database'); // database connection + foreach($privileges as $pri) { // pri = privilege id + $db->runQuery( + "INSERT INTO user_privilege (user_id, privilege_id) VALUES (:user, :pri)", + [ ':user' => $user, ":pri" => $pri ] + ); + } + return true; + } + + + /** set_user_role + * + * user, role are (int) IDs + */ + public static function set_user_role($user, $role) + { + + Registry::use('database')->runQuery( + "INSERT INTO user_role (user_id, role_id) VALUES (:user, :role)", + [ ':user' => $user, ":role" => $role ] + ); + + return true; + } + + + ## List methods + ## ------------------------------------------------------------------------- + + + /** privileges + * --- + * get all privileges (as a list) + * used by cacher; speeds up seveal things + * + * @param void + * @return privileges (array) + */ + public static function privileges() + { + return Registry::use('database')->runQuery( + "SELECT * FROM privilege",[] + ); + } + + +} diff --git a/public/app/models/Cms_model.php b/public/app/models/Cms_model.php new file mode 100644 index 0000000..fad11b8 --- /dev/null +++ b/public/app/models/Cms_model.php @@ -0,0 +1,314 @@ +runQuery( + "SELECT * FROM course ORDER BY label", + [] + ); + } + + /** courses struct + * + * a super array with almost any info needed about courses + * + * @return (array) ['tree' => ..., 'breadcrumbs' => ... ] + */ + public static function courses_struct() + { + $tree = self::category_tree(); + return [ + 'tree' => $tree, + 'breadcrumbs' => self::breadcrumbs($tree) + ]; + } + + + /** constuct a category_tree + * + * returns a tree representation of the categories + * + * NOTE: + * category_tree() is an expensive method; + * it calls 2 other methods implementing recursive algorithms + * thus it uses many sources to run (particularly RAM). + * Caching the result is strogly recommended. + * + */ + public static function category_tree() + { + $categories = self::get_categories(); // get all categories + + $tree = self::to_tree($categories); // format to a tree + + $tree_wParents = self::tree_parents($tree); // add parents section for each tree-node + + return $tree_wParents; + } + + + /** to_tree + * + * constructs a tree from raw-table data; + * this is a private method and uses a recursive algorithm + * + * @param $dataset (array): flar array of records with id/parent-id pairs + * @return $root (array): id of root category + * + * (**) each node has 2 parts: + * .... .. rec : all record attributes/data as passed into $dataset + * .... .. childs : array of (children) nodes + */ + private static function to_tree($dataset, $root = 0) + { + $return = []; + + // loop data ; search for direct children of root + foreach($dataset as $key => $rec) { + + $child = $rec['id']; + $parent = $rec['parent_id']; + + if ($parent == $root) { // a direct child is found + + unset($dataset[$key]); // remove item (no need to traverse again) + + // Append the child into result array ; parse its children + $return[] = [ + 'rec' => [ + 'id' => $rec['id'], + 'label' => $rec['label'], + 'order' => $rec['order'], + 'parent' => $rec['parent_id'] + ], + 'childs' => self::to_tree($dataset, $child) // recursively + ]; + } + } + return empty($return) ? [] : $return; + } + + + /** tree_parents + * + * adds a section to each tree node with all parents of each node + * + * @param $tree (array) : nodes array (each node has `rec` and `childs` sections ) + * @param $parents (array); DO NOT SET IT (takes values automaticaly) + * @return array of nodes with an extra node[parents] section + * + */ + private static function tree_parents($tree, $parents = []) + { + $tree_with_parents = []; + + foreach($tree as $key => $node) { + // parents to be pushed for node's children + $push_parents = $parents; // parents so far + $push_parents[] = $node['rec']; // this record will be a new parent + + $tree_with_parents[$key] = [ + 'rec' => $node['rec'], + 'parents' => $parents, + 'childs' => ($node['childs'] == []) + ? [] + : self::tree_parents($node['childs'], $push_parents) + ]; + } + + return $tree_with_parents; + } + + + /** all_breadcrumbs + * ------------------------------------------------------------------------- + * + * returns an array of all breadcrumbs + * where array-key of each record is category[id] + * + * NOTE: + * --- + * Cms_model::all_breadcrumbs returns an indexed super-array; + * each array item includes a banch of information: [ + * breadcrumb, + * rec: [ id , title ], + * parents: [ [id, title] , ... ] + * childs: [ [id, title] , ... ], + * level + * ] + * + * Use Cases: + * --- + * as a super-array, the output can be used in many cases + * for example... + * into form elements + * .. while selecting category for a post + * .. or editing a category + * or directry referring to category's parents/childs + * + * Arguments: + * --- + * @param $tree (array) : category tree (with childs and parents parts) + * @param $detimiter (string, optional) : string to split breadcrumb's path-nodes + * @param $exception (int, optional) : id of category to exclude (subcategories shall be excluded too) + * @param $l (int, not-pass) : depth level of the node; DO NOT SET (takes values automaticaly) + * @return array of breadcrumbs + * ------------------------------------------------------------------------- + */ + static public function breadcrumbs($tree, $delimiter = " / ", $exception = 0, $l = 0) + { + $all = []; // results array + + foreach($tree as $node) { // loop through all nodes + + if (intval($node['rec']['id']) != $exception) { // if node is not exception + + // construct breadcrumb html of node + // --- -- -- - - - + $breadcrumb = ""; + foreach($node['parents'] as $par) { // first: join path titles + $breadcrumb .= $par['label'] . $delimiter; + } + $breadcrumb .= $node['rec']['label']; // last: append title + + // make a new super record + // --- -- -- - - - + $all[$node['rec']['id']] = [ // set record is as key + 'breadcrumb' => $breadcrumb, // add breadcrump to results + 'rec' => $node['rec'], // + node info + 'parents' => $node['parents'], // + parents array + 'childs' => self::first_level_childs($node), // + direct childs + 'level' => $l // + level + ]; + + // recursively traverse children nodes + // --- -- -- - - - + if (isset($node['childs']) && $node['childs'] != []) { + $child_breadcrumbs = self::breadcrumbs( + $node['childs'], + $delimiter, + $exception, + $l+1 + ); + + $all = $all + $child_breadcrumbs; // concatenate arrays (keep array-keys) + } + } + + } + return $all; + + } + + /** first_level_childs + * --- -- -- - - - + * used by all_breadcrumbs() + */ + static private function first_level_childs($node) + { + $childs = []; + if ($node['childs'] == []) { + return []; + } + foreach($node['childs'] as $key => $kid) { + $childs[] = [ + 'id' => $kid['rec']['id'], + 'label' => $kid['rec']['label'] + ]; + } + return $childs; + } + + + + + /** LESSONS + * ------------------------------------------------------------------------- + */ + + + /** lessons of course + * + * @param $id (int) : course_id + */ + public static function lessons_of_course($id) + { + // TODO: order results in some way + + return Registry::use('database')->runQuery( + "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson + LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id + WHERE lesson.status = 1 AND lesson.course_id = :id + ORDER BY lesson_privilege.privilege_id ASC", + [':id' => $id] + ); + } + + /** lesson + * + * @param $id (int) : lesson id + */ + public static function lesson($id) + { + // TODO: order results in some way + + return Registry::use('database')->query( + "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson + LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id + WHERE lesson.status = 1 AND lesson.id = :id", + [':id' => $id] + )->getFirst(); + } + + + + /** files + * return all files + * @param void + * @return array + */ + public static function files() + { + return Registry::use('database')->runQuery("SELECT * from media", []); + } + + + /** pages + * + * return all pages as array; + * array key of each item shall be the page-id + * + * @param void + * @return array + */ + public static function pages() + { + $result = []; + $pages = Registry::use('database')->runQuery("SELECT * FROM page", []); + foreach($pages as $key => $page) { + $result[ $page['id'] ] = [ + 'title' => $page['title'], + 'body' => $page['body'], + 'status' => $page['status'] + ]; + } + // print_r($result); die(); + return $result; + } + +} diff --git a/public/app/models/History_model.php b/public/app/models/History_model.php new file mode 100644 index 0000000..f50539f --- /dev/null +++ b/public/app/models/History_model.php @@ -0,0 +1,34 @@ +query( + "INSERT INTO history + (user_id, `type`, `message`, `note`, `ip`) + VALUES + (:uid, :type, :msg, :note, :ip)", + [ + ':uid' => $user_id, + ':type' => $type, + ':msg' => $message, + ':note' => $note, + ':ip' => Registry::get('REQUEST')->IP + ] + )->lastInsertID(); + + } + + + +} \ No newline at end of file diff --git a/public/app/models/admin/History_model.php b/public/app/models/admin/History_model.php deleted file mode 100644 index 04ce630..0000000 --- a/public/app/models/admin/History_model.php +++ /dev/null @@ -1,34 +0,0 @@ -query( - "INSERT INTO history - (user_id, `type`, `message`, `note`, `ip`) - VALUES - (:uid, :type, :msg, :note, :ip)", - [ - ':uid' => $user_id, - ':type' => $type, - ':msg' => $message, - ':note' => $note, - ':ip' => Registry::get('REQUEST')->IP - ] - )->lastInsertID(); - - } - - - -} \ No newline at end of file diff --git a/public/app/models/admin/Privilege_model.php b/public/app/models/admin/Privilege_model.php deleted file mode 100644 index c385484..0000000 --- a/public/app/models/admin/Privilege_model.php +++ /dev/null @@ -1,42 +0,0 @@ -runQuery( - "SELECT * FROM privilege",[] - ); - } - - /** edit privileges - * --- - */ - public static function edit_privileges() - { - // TODO: ... - } - - - /** update course - * --- - */ - public static function update_course() - { - // TODO: ... - } - -} \ No newline at end of file diff --git a/public/app/models/admin/User_model.php b/public/app/models/admin/User_model.php deleted file mode 100644 index 2885dc1..0000000 --- a/public/app/models/admin/User_model.php +++ /dev/null @@ -1,299 +0,0 @@ -query( - "SELECT * FROM user WHERE email = :email AND active = 1", - [ ':email' => $email ] - )->getFirst(); - - // if no user, return false - if ($user === false) return false; - - return $user; - } - - - /** get user (by index key) - * - * user detailed array - * includes all user properties + granted roles + privileges - * - * @param $id (int) : user id - * @param $value (string) - */ - public static function getUser($id) - { - $user = Registry::use('database')->query( - "SELECT user.*, - ( -- construct array (json) of roles granted to user - SELECT CONCAT( - '[', - GROUP_CONCAT(role.id), - ']' - ) - FROM `role` - WHERE role.id IN ( - SELECT user_role.role_id - FROM user_role - WHERE user_role.user_id = :id - ) - ) AS Roles_json, - ( -- construct array of (root-)privileges granted to user - SELECT CONCAT( - '[', - GROUP_CONCAT(privilege.id), - ']' - ) - FROM privilege - WHERE privilege.id IN ( - SELECT user_privilege.privilege_id - FROM user_privilege - WHERE user_privilege.user_id = :id - ) - ) AS RootPrivileges_json, - ( -- construct array of (root-)privileges granted to user - SELECT CONCAT( - '[', - GROUP_CONCAT(privilege.includes), - ']' - ) - FROM privilege - WHERE privilege.id IN ( - SELECT user_privilege.privilege_id - FROM user_privilege - WHERE user_privilege.user_id = :id - ) - ) AS SubPrivileges_json - FROM user - WHERE id = :id", - [ ':id' => $id ] - )->getFirst(); - - - // if no user, return false - if ($user === false) return false; - - - // TODO: - // * merge root+sub privilede lists - // * convert json strings to php arrays - - - // TODO: - // cache user super array - - return $user; - } - - - /** create user - * - * creates user record; - * assigns privileged (usualy defaults); - * creates activation_code - * - * @param $data (array): Request->POST array - * @param $password (string): secure hashed password - * - * @return $activation_code - * - */ - public static function registerUser($data, $password, $privileges = DEFAULT_PRIVILEGES) - { - $required_fields = [ - 'name', - 'surname', - 'email', - 'password' - ]; - - // check required fields - $isOK = true; - foreach($required_fields as $fi) { - if (empty($data[$fi])) $isOK = false; - } - // if empty required fields exists ... return false - if (!$isOK) { - return [ "success" => false, 'error' => EMPTY_REQUIRED_FIELDS ]; - } - - - // TODO: - // check if email exists - // ... - - // create an activation code - $activation_code = md5($data['email'].time().rand(0, 10000)); - - // if isOK go on and... - // create user record - $new_user_id = Registry::use('database')->query( - "INSERT INTO user - (`first_name`, `last_name`, `email`, `password`, `active`, `activation`) - VALUES - (:nam, :surname, :email, :pass, :act, :actcode)", - [ - ':nam' => $data['name'], - ':surname' => $data['surname'], - ':email' => $data['email'], - ':pass' => $password, - ':act' => 0, // needs email confirmation to be activated ... - ':actcode' => $activation_code // ... with the activation code - ] - )->lastInsertID(); - - // set default privileges - self::set_user_privileges($new_user_id, $privileges); - - // set reader role - self::set_user_role($new_user_id, 5); - - // update history - History::trackUserAccess($new_user_id, TRACK_ACCOUNT, 'Create User Account'); - - // return success and user id - return [ - "success" => true, - 'id' => $new_user_id , - 'activation' => $activation_code - ]; - } - - - /** set_user_privileges - * - * @param $privileges (array) - */ - public static function set_user_privileges($user, $privileges) - { - $db = Registry::use('database'); // database connection - foreach($privileges as $pri) { // pri = privilege id - $db->runQuery( - "INSERT INTO user_privilege (user_id, privilege_id) VALUES (:user, :pri)", - [ ':user' => $user, ":pri" => $pri ] - ); - } - return true; - } - - - /** set_user_role - * - * user, role are (int) IDs - */ - public static function set_user_role($user, $role) - { - - Registry::use('database')->runQuery( - "INSERT INTO user_role (user_id, role_id) VALUES (:user, :role)", - [ ':user' => $user, ":role" => $role ] - ); - - return true; - } - - - /** activate - * - * check if activation code is valid; - * if valid, set account active; - * - * @param $ticket (hex/MD5): activation code; - * - */ - public static function activate($ticket) - { - $user = Registry::use('database')->query( - "SELECT * FROM user WHERE activation = :ticket", - [ 'ticket' => $ticket ] - )->getFirst(); - - // if no user with this activation code, return false - if ($user === false) return false; - - // remove activation code from user record - Registry::use('database')->runQuery( - "UPDATE user - SET active = 1, `activation` = NULL - WHERE activation = :ticket", - [ 'ticket' => $ticket ] - ); - - // update history - History::trackUserAccess($user['id'], TRACK_ACCOUNT, 'User Account Activated'); - - return true; - - } - - -} - -/* example query getUser (super-array) ---- -- -- - - - - -SELECT user.*, -( -- array (json) of roles granted to user - SELECT CONCAT('[', GROUP_CONCAT(role.id), ']') - FROM `role` - WHERE role.id IN ( - SELECT user_role.role_id - FROM user_role - WHERE user_role.user_id = 1 - ) -) AS Roles_json, -( - SELECT CONCAT( - '[', - GROUP_CONCAT(privilege.id), - ']' - ) - FROM privilege - WHERE privilege.id IN ( - SELECT user_privilege.privilege_id - FROM user_privilege - WHERE user_privilege.user_id = 1 - ) -) AS RootPrivileges_json, -( - SELECT CONCAT( -- array of array of sub-privileges - '[', - GROUP_CONCAT( -- array (json) of subprivileges - ( - SELECT CONCAT( - '[', - GROUP_CONCAT(included_id), - ']' - ) - FROM privilege_includes - WHERE privilege_id = privilege.id - ) - ), - ']' - ) - FROM privilege - WHERE privilege.id IN ( - SELECT user_privilege.privilege_id - FROM user_privilege - WHERE user_id = 1 - ) -) AS SubPrivileges_json -FROM user -WHERE id = 1 ---- */ \ No newline at end of file diff --git a/public/app/models/cms/Course_model.php b/public/app/models/cms/Course_model.php deleted file mode 100644 index 6614993..0000000 --- a/public/app/models/cms/Course_model.php +++ /dev/null @@ -1,314 +0,0 @@ -runQuery( - "SELECT * FROM course ORDER BY label", - [] - ); - } - - /** courses struct - * - * a super array with almost any info needed about courses - * - * @return (array) ['tree' => ..., 'breadcrumbs' => ... ] - */ - public static function courses_struct() - { - $tree = self::category_tree(); - return [ - 'tree' => $tree, - 'breadcrumbs' => self::breadcrumbs($tree) - ]; - } - - - /** constuct a category_tree - * - * returns a tree representation of the categories - * - * NOTE: - * category_tree() is an expensive method; - * it calls 2 other methods implementing recursive algorithms - * thus it uses many sources to run (particularly RAM). - * Caching the result is strogly recommended. - * - */ - public static function category_tree() - { - $categories = self::get_categories(); // get all categories - - $tree = self::to_tree($categories); // format to a tree - - $tree_wParents = self::tree_parents($tree); // add parents section for each tree-node - - return $tree_wParents; - } - - - /** to_tree - * - * constructs a tree from raw-table data; - * this is a private method and uses a recursive algorithm - * - * @param $dataset (array): flar array of records with id/parent-id pairs - * @return $root (array): id of root category - * - * (**) each node has 2 parts: - * .... .. rec : all record attributes/data as passed into $dataset - * .... .. childs : array of (children) nodes - */ - private static function to_tree($dataset, $root = 0) - { - $return = []; - - // loop data ; search for direct children of root - foreach($dataset as $key => $rec) { - - $child = $rec['id']; - $parent = $rec['parent_id']; - - if ($parent == $root) { // a direct child is found - - unset($dataset[$key]); // remove item (no need to traverse again) - - // Append the child into result array ; parse its children - $return[] = [ - 'rec' => [ - 'id' => $rec['id'], - 'label' => $rec['label'], - 'order' => $rec['order'], - 'parent' => $rec['parent_id'] - ], - 'childs' => self::to_tree($dataset, $child) // recursively - ]; - } - } - return empty($return) ? [] : $return; - } - - - /** tree_parents - * - * adds a section to each tree node with all parents of each node - * - * @param $tree (array) : nodes array (each node has `rec` and `childs` sections ) - * @param $parents (array); DO NOT SET IT (takes values automaticaly) - * @return array of nodes with an extra node[parents] section - * - */ - private static function tree_parents($tree, $parents = []) - { - $tree_with_parents = []; - - foreach($tree as $key => $node) { - // parents to be pushed for node's children - $push_parents = $parents; // parents so far - $push_parents[] = $node['rec']; // this record will be a new parent - - $tree_with_parents[$key] = [ - 'rec' => $node['rec'], - 'parents' => $parents, - 'childs' => ($node['childs'] == []) - ? [] - : self::tree_parents($node['childs'], $push_parents) - ]; - } - - return $tree_with_parents; - } - - - /** all_breadcrumbs - * ------------------------------------------------------------------------- - * - * returns an array of all breadcrumbs - * where array-key of each record is category[id] - * - * NOTE: - * --- - * Course_model::all_breadcrumbs returns an indexed super-array; - * each array item includes a banch of information: [ - * breadcrumb, - * rec: [ id , title ], - * parents: [ [id, title] , ... ] - * childs: [ [id, title] , ... ], - * level - * ] - * - * Use Cases: - * --- - * as a super-array, the output can be used in many cases - * for example... - * into form elements - * .. while selecting category for a post - * .. or editing a category - * or directry referring to category's parents/childs - * - * Arguments: - * --- - * @param $tree (array) : category tree (with childs and parents parts) - * @param $detimiter (string, optional) : string to split breadcrumb's path-nodes - * @param $exception (int, optional) : id of category to exclude (subcategories shall be excluded too) - * @param $l (int, not-pass) : depth level of the node; DO NOT SET (takes values automaticaly) - * @return array of breadcrumbs - * ------------------------------------------------------------------------- - */ - static public function breadcrumbs($tree, $delimiter = " / ", $exception = 0, $l = 0) - { - $all = []; // results array - - foreach($tree as $node) { // loop through all nodes - - if (intval($node['rec']['id']) != $exception) { // if node is not exception - - // construct breadcrumb html of node - // --- -- -- - - - - $breadcrumb = ""; - foreach($node['parents'] as $par) { // first: join path titles - $breadcrumb .= $par['label'] . $delimiter; - } - $breadcrumb .= $node['rec']['label']; // last: append title - - // make a new super record - // --- -- -- - - - - $all[$node['rec']['id']] = [ // set record is as key - 'breadcrumb' => $breadcrumb, // add breadcrump to results - 'rec' => $node['rec'], // + node info - 'parents' => $node['parents'], // + parents array - 'childs' => self::first_level_childs($node), // + direct childs - 'level' => $l // + level - ]; - - // recursively traverse children nodes - // --- -- -- - - - - if (isset($node['childs']) && $node['childs'] != []) { - $child_breadcrumbs = self::breadcrumbs( - $node['childs'], - $delimiter, - $exception, - $l+1 - ); - - $all = $all + $child_breadcrumbs; // concatenate arrays (keep array-keys) - } - } - - } - return $all; - - } - - /** first_level_childs - * --- -- -- - - - - * used by all_breadcrumbs() - */ - static private function first_level_childs($node) - { - $childs = []; - if ($node['childs'] == []) { - return []; - } - foreach($node['childs'] as $key => $kid) { - $childs[] = [ - 'id' => $kid['rec']['id'], - 'label' => $kid['rec']['label'] - ]; - } - return $childs; - } - - - - - /** LESSONS - * ------------------------------------------------------------------------- - */ - - - /** lessons of course - * - * @param $id (int) : course_id - */ - public static function lessons_of_course($id) - { - // TODO: order results in some way - - return Registry::use('database')->runQuery( - "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson - LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id - WHERE lesson.status = 1 AND lesson.course_id = :id - ORDER BY lesson_privilege.privilege_id ASC", - [':id' => $id] - ); - } - - /** lesson - * - * @param $id (int) : lesson id - */ - public static function lesson($id) - { - // TODO: order results in some way - - return Registry::use('database')->query( - "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson - LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id - WHERE lesson.status = 1 AND lesson.id = :id", - [':id' => $id] - )->getFirst(); - } - - - - /** files - * return all files - * @param void - * @return array - */ - public static function files() - { - return Registry::use('database')->runQuery("SELECT * from media", []); - } - - - /** pages - * - * return all pages as array; - * array key of each item shall be the page-id - * - * @param void - * @return array - */ - public static function pages() - { - $result = []; - $pages = Registry::use('database')->runQuery("SELECT * FROM page", []); - foreach($pages as $key => $page) { - $result[ $page['id'] ] = [ - 'title' => $page['title'], - 'body' => $page['body'], - 'status' => $page['status'] - ]; - } - // print_r($result); die(); - return $result; - } - -} diff --git a/public/app/models/cms/Media_model.php b/public/app/models/cms/Media_model.php deleted file mode 100644 index 6a4a681..0000000 --- a/public/app/models/cms/Media_model.php +++ /dev/null @@ -1,36 +0,0 @@ -, rights =>, path => ] - - // validate ticket - // ValidateAccess::for($ticket) - - // serve - // header("Content-type: type"); - // passthru('cat $media_path'); - - - return Registry::use('database')->query( - "SELECT * FROM pages - WHERE FullFriendlyUrl = :url AND IsActive = 1", - [ ':url' => $url ] - )->getFirst(); - } - -} - - - -// check: -// https://stackoverflow.com/questions/1353850/serve-image-with-php-script-vs-direct-loading-an-image \ No newline at end of file diff --git a/public/app/models/cms/Page_model.php b/public/app/models/cms/Page_model.php deleted file mode 100644 index fda5fb1..0000000 --- a/public/app/models/cms/Page_model.php +++ /dev/null @@ -1,18 +0,0 @@ -query( - "SELECT * FROM pages - WHERE FullFriendlyUrl = :url AND IsActive = 1", - [ ':url' => $url ] - )->getFirst(); - } - -} \ No newline at end of file diff --git a/public/app/models/ideas.md b/public/app/models/ideas.md new file mode 100644 index 0000000..300612d --- /dev/null +++ b/public/app/models/ideas.md @@ -0,0 +1,24 @@ +# interesting projects + +projects for brainstorming + +## php ORM projects + +* [riverside\php-orm](https://github.com/riverside/php-orm) seems the lightest and most promissing + +* https://github.com/mareimorsy/DB + +* https://propelorm.org/ + +* https://redbeanphp.com/index.php?p=/download + +* you can find more in a [related github topic](https://github.com/topics/php-orm) + + +Thoughts: +create a DatabaseModel class (??) based on the riverside\php-orm then use it in anom framerok + + +## php micro-framework + +* [riverside\php-express](https://github.com/riverside/php-express) seems quite interesting diff --git a/public/app/models/todo.md b/public/app/models/todo.md index defd21e..ec027a6 100644 --- a/public/app/models/todo.md +++ b/public/app/models/todo.md @@ -1,55 +1,41 @@ - - -Privilege ---- - -Privilege::list_of_privileges( privilege_id ) - -Privilege::privileges_tree() - - - - - -User +Rearange --- -User::get_privileges +-- #1 -- +[ok] app\controllers\cms\Course -> [*] app\controllers\Course -User::has_privilege( privilege_id ) +[ok] app\controllers\admin\Users_admin -> [delete] +[ok] app\controllers\Admin -> [delete] -User::reset_password() - -> create otp - -> send email +[ok] app\controllers\admin\Course_admin -> [*] app\controllers\Course_admin +[ok] app\models\admin\User_model -> [*] app\models\User_model -User::set_privilege( privilege_id ) +[ok] app\models\admin\Privilege_model -> [merge_to] -> [**] app\models\User_model +[ok] app\models\admin\History_model -> [*] app\models\History_model -User::track_action() - -> user_id - -> action : Contoller::method(args) - -> timestamp +[ok] app\models\cms\Page_model -> [delete] +[ok] app\models\cms\Media_model -> [delete] +[ok] app\models\cms\Course_model -> [*] app\models\Course_model -Lesson: ---- -Lesson::create_lesson( POST ) +[*] = move -Lesson::get_lesson( lesson_id ) +-- #2 -- +[ok] app\controllers\Course -> [rename] app\controllers\Cms -Course ---- +[ok] app\controllers\Course_admin -> [rename] app\controllers\CmsAdmin -Course::create_course( POST ) +[ok] app\models\Course_model -> [rename] app\models\Cms_model -Course::courses_tree() +[ok] app\extends\Send_mail -> [rename] SendMail_service diff --git a/public/app/routes/backend.php b/public/app/routes/backend.php index 2bbd056..027c50a 100644 --- a/public/app/routes/backend.php +++ b/public/app/routes/backend.php @@ -1,7 +1,7 @@ Course_admin::add_lesson()]); + Render::json(['success' => CmsAdmin::add_lesson()]); }, 'post'); Route::add('/admin/api/lesson/update', function () { // ajax: update lesson Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Render::json(['success' => Course_admin::update_lesson()]); + Render::json(['success' => CmsAdmin::update_lesson()]); }, 'post'); Route::add('/admin/api/lessons', function () { // ajax: get all lessons Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Course_admin::all_lessons(); + CmsAdmin::all_lessons(); }); @@ -153,14 +153,14 @@ Route::add('/admin/files', function () { // admin-files panel Route::add('/admin/api/files', function () { // ajax: get lesson Auth::allowRoles([1, 2, 3]); - Render::json(Course_admin::files()); + Render::json(CmsAdmin::files()); }); // ajax: file-upload // --- Route::add('/admin/api/file_upload', function () { Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Render::json(Course_admin::upload_file()); + Render::json(CmsAdmin::upload_file()); }, 'post'); @@ -198,24 +198,24 @@ Route::add('/admin/edit_page/([0-9]*)', function ($id) { // edit page form Route::add('/admin/api/page/([0-9]*)', function ($id) { // ajax: get page Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Course_admin::get_page($id); + CmsAdmin::get_page($id); }); Route::add('/admin/api/page/add', function () { // ajax: add page Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Render::json(['success' => Course_admin::add_page()]); + Render::json(['success' => CmsAdmin::add_page()]); }, 'post'); Route::add('/admin/api/page/update', function () { // ajax: update page Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Render::json(['success' => Course_admin::update_page()]); + Render::json(['success' => CmsAdmin::update_page()]); }, 'post'); Route::add('/admin/api/pages', function () { // ajax: get all pages Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Course_admin::all_pages(); + CmsAdmin::all_pages(); }); diff --git a/public/app/routes/frontend.php b/public/app/routes/frontend.php index be06b1c..96e9786 100644 --- a/public/app/routes/frontend.php +++ b/public/app/routes/frontend.php @@ -2,8 +2,7 @@ use app\controllers\Auth; use app\controllers\Classroom_user; -use app\controllers\cms\Course; -use app\models\cms\Course_model; +use app\controllers\Cms; use app\extends\Cache_service; @@ -34,19 +33,19 @@ Route::notFound( function() { // 404 error page Route::add('/course/([0-9]*)', // request course by course-id - function($id) { Course::course($id); } + function($id) { Cms::course($id); } ); Route::add('/lesson/([0-9]*)', // request lesson by lesson-id - function($id) { Course::lesson($id); } + function($id) { Cms::lesson($id); } ); Route::add('/page/([0-9]*)', // request lesson by lesson-id - function($id) { Course::page($id); } + function($id) { Cms::page($id); } ); Route::add('/serve/file/([0-9a-zA-Z-_\.\/]*)', // serve file by path - function ($path) { Course::serve_file($path); } // (also ?type= is sent) + function ($path) { Cms::serve_file($path); } // (also ?type= is sent) ); -- cgit v1.2.3