From 51b3554ff2988ac61ba3f604431e09941cd95545 Mon Sep 17 00:00:00 2001 From: George Halkiadakis Date: Tue, 14 Mar 2023 03:10:19 +0200 Subject: main css classes for front/back-end --- html/app/views/simple/Model.php | 887 +++++++++++++++++++++++++++++++++ html/app/views/simple/Render.php | 182 +++++++ html/app/views/simple/category.php | 156 ++++++ html/app/views/simple/index.php | 123 +++++ html/app/views/simple/lorem-dates.php | 18 + html/app/views/simple/lorem-intros.php | 721 +++++++++++++++++++++++++++ html/app/views/simple/lorem-posts.php | 93 ++++ html/app/views/simple/post.php | 236 +++++++++ html/assets/css/admin.css | 86 ++-- html/assets/css/class.css | 348 +++++++++++++ html/assets/css/wiki.css | 348 ------------- 11 files changed, 2807 insertions(+), 391 deletions(-) create mode 100644 html/app/views/simple/Model.php create mode 100644 html/app/views/simple/Render.php create mode 100644 html/app/views/simple/category.php create mode 100644 html/app/views/simple/index.php create mode 100644 html/app/views/simple/lorem-dates.php create mode 100644 html/app/views/simple/lorem-intros.php create mode 100644 html/app/views/simple/lorem-posts.php create mode 100644 html/app/views/simple/post.php create mode 100644 html/assets/css/class.css delete mode 100644 html/assets/css/wiki.css (limited to 'html') diff --git a/html/app/views/simple/Model.php b/html/app/views/simple/Model.php new file mode 100644 index 0000000..6ea1e7d --- /dev/null +++ b/html/app/views/simple/Model.php @@ -0,0 +1,887 @@ +pdo_pythia= $db->connect(); + + /// // database for wiki + /// $this->pdo = new PDO('mysql:unix_socket=/cloudsql/pythia-251711:europe-west4:pythia-db-eu;dbname=' . DB_DATABASE . ';charset=utf8', DB_USERNAME, DB_PASSWORD, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC)); + /// $this->user_id = $_SESSION['user_session']; + + $this->pdo = $GLOBALS['DB_con']; + + } + + + /** Close Instance + * --------------------------------------------------- (probably not needed) + */ + public function close() + { + // Default + // $this->pdo=Database::disconnect(); + } + + + + ## ------------------------------------------------------------------------- + ## + ## CATEGORY METHODS + ## + ## ------------------------------------------------------------------------- + + + /** get all categories + * + * raw table data (simplest SELECT) + * + */ + public function get_categories() + { + $sql = "SELECT * FROM wiki_category ORDER BY title"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + + /** 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 function category_tree() + { + $categories = $this->get_categories(); // get all categories + + $tree = $this->to_tree($categories); // format to a tree + + $tree_wParents = $this->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 + */ + public 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'], + 'title' => $rec['title'], + 'parent' => $rec['parent_id'] + ], + 'childs' => $this->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 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'] == []) + ? [] + : $this->tree_parents($node['childs'], $push_parents) + ]; + } + + return $tree_with_parents; + } + + + /** add_category() + * + * add new category + * + * @param $recuest (array): [ title => , parent_id => ] + * @return true; + * + * TODO: catch database error -> return succes => false + */ + public function add_category($request) + { + $sql = "INSERT INTO wiki_category (title, parent_id) VALUES (:title, :parent_id)"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ + 'title' => $request['title'], + 'parent_id' => $request['parent_id'] + ]); + $inserted_id = $this->pdo->lastInsertId(); + + return [ "success" => true, 'id' => $inserted_id ]; + } + + + /** update_category() + * + * update an existing category + * + * @param $recuest (array): [ id => , title => , parent_id => ] + * @return true; + * + * TODO: catch database error -> return false + */ + public function update_category($request) + { + $sql = "UPDATE wiki_category SET title = :title, parent_id = :parent_id WHERE id = :id"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ + 'title' => $request['title'], + 'parent_id' => $request['parent_id'], + 'id' => $request['id'] + ]); ; + + return true; + } + + + + ## ------------------------------------------------------------------------- + ## + ## POST (ARTICLE) METHODS + ## + ## ------------------------------------------------------------------------- + + + /** get_post + * + * get a certain post (given the post's `id`) + * + * @param $id (int) + * @param $only_publised (bool): should the post be published? (default=true) + */ + public function get_post($id, $only_published = true) { + + $where_clause = ($only_published) ? ' AND post.status = 1' : ''; + + // depricated: $sql = "SELECT * FROM post WHERE id = :id {$where_clause}"; + // Select post + // + inject tags json + // TODO: + inject media + // --- + $sql = "SELECT wiki_post.*, + ( SELECT CONCAT('[', GROUP_CONCAT(JSON_OBJECT('id', wiki_tag.id, 'name', wiki_tag.name)), ']') + FROM wiki_tag + WHERE wiki_tag.id IN ( SELECT tag_id FROM wiki_post_tags WHERE post_id = :id ) + ) AS tags_json, + ( SELECT + CONCAT('[', GROUP_CONCAT(JSON_OBJECT( + 'id', wiki_media.id, + 'title', wiki_media.title, + 'type', wiki_media.type, + 'path', wiki_media.path, + 'reference', wiki_post_media.reference )), + ']') + FROM wiki_media + LEFT JOIN wiki_post_media ON wiki_post_media.media_id = wiki_media.id + WHERE wiki_post_media.post_id = :id + ) AS medias_json + FROM wiki_post + WHERE wiki_post.deleted IS NULL AND wiki_post.id = :id"; + + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['id' => $id]); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return ($result == []) + ? false // no result? false + : $result[0]; // else return 1st record + } + + + /** add post + * + * insert post to database + * + * @param $data (array): an array with all post-properties + * @return: an array with `status` (bool), `id` (int) given to post + */ + public function add_post($data) + { + $post = [ + 'title' => $data['title'], + 'category_id' => $data['category_id'], + 'body' => $data['body'], + 'intro' => $data['intro'], + 'status' => $data['status'], + 'userid' => $this->user_id + ]; + + $sql = "INSERT INTO wiki_post (`title`, category_id, `body`, intro, `status`, user_id) + VALUES (:title, :category_id, :body, :intro, :status, :userid)"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute($post); + + $inserted_id = $this->pdo->lastInsertId(); + + // create + link tags to the post + // --- -- -- - - - + if (isset($data['tags'])) { // existing tags... + $this->set_post_tags($inserted_id, $data['tags']); + } + if (isset($data['new_tags'])) { // new tags... + $this->create_tags_for_post($data['new_tags'], $inserted_id); + } + + // set medias for post + // --- -- -- - - - + if (isset($data['media'])) { // new tags... + $this->create_medias_for_post($data['media'], $inserted_id); + } + + return [ "success" => true, 'id' => $inserted_id ]; + } + + + /** update post + * + * update post record to database + * + * @param $data (array): an array with all post-properties + */ + public function update_post($data) + { + $post = [ + 'id' => $data['id'], + 'title' => $data['title'], + 'category_id' => $data['category_id'], + 'body' => $data['body'], + 'intro' => $data['intro'], + 'status' => $data['status'] + ]; + $sql = "UPDATE wiki_post + SET `title` = :title, category_id = :category_id, + `body` = :body, intro = :intro, + `status` = :status + WHERE id = :id"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute($post); + + // create + link tags to the post + // --- -- -- - - - + if (isset($data['tags'])) { // old tags... + $this->set_post_tags($post['id'], $data['tags']); + } + if (isset($data['new_tags'])) { // new tags... + $this->create_tags_for_post($data['new_tags'], $post['id']); + } + + // set medias for post + // --- -- -- - - - + if (isset($data['media'])) { + $this->remove_media_from_post($post['id']); // remove old + $this->create_medias_for_post($data['media'], $post['id']); // re-create new + } + + return true; + } + + + /** latest_published_posts + * + * latest PUBLISHED posts of a category (by category_id) + * + * TODO: handle sorting + * + * @param $category_id (int) ; if 0 then no category is specified + * @param $limit (int) ; 0 = no limit + * @param $fieldset (string): list of fields; default all = '*' + */ + public function latest_published_posts($category_id, $limit = 10, $fieldset = '*') + { + + $where_clause = ($category_id) + ? " AND category_id = :categoryid " + : " "; + + $limit_clause = ($limit) + ? " LIMIT ". $limit + : ""; + + $sql = "SELECT {$fieldset} FROM wiki_post + WHERE `status` = 1 AND deleted IS NULL ". $where_clause + ." ORDER BY creation_date DESC ". $limit_clause; + + $stmt = $this->pdo->prepare($sql); + + if (!$category_id) { + $stmt->execute(); + + } else { + $stmt->execute(['categoryid' => $category_id]); + } + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return ($result === null) + ? false // no result? return false + : $result; // else return results + } + + + /** delete post + * + * NOTE: + * post is *marked* as deleted; + * a cron-job will make the actual deletions in a later time + * + * @param $id (int): post id + */ + public function delete_post($id) + { + if (intval($id) == 0) { // no valid id? + return ["success" => false]; // return false + } + + $sql = "UPDATE wiki_post SET deleted = 1 WHERE id = :id"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['id' => $id]); + + return ["success" => true]; + } + + + /** all posts + * published + unpublished ; any category ; no limit + * + * @param $filedset (string): list of fields; default all = '*' + */ + public function all_posts($fieldset = '*') + { + + $sql = "SELECT {$fieldset} FROM wiki_post WHERE deleted IS NULL"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return ($result === null) + ? false // no result? return false + : $result; // else return results + } + + + + ## ------------------------------------------------------------------------- + ## + ## MEDIA METHODS + ## + ## ------------------------------------------------------------------------- + + /** 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 function create_medias_for_post( $medias, $post_id ) + { + foreach($medias as $medi_string) { + $medi = explode(';', $medi_string); // [0]: media_id, [1]: reference + $this->link_media_to_post($medi[0], $post_id, $medi[1]); // link to post + } + return true; + } + + /** remove_media_from_post + * + * remove all media records/attachments from post + * + * @param $post_id (int) + */ + private function remove_media_from_post( $post_id ) + { + $sql = "DELETE FROM wiki_post_media WHERE post_id = :postid"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['postid' => $post_id]); + 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 $post_id (int) + * @param $reference (int|bit) + */ + private function link_media_to_post( $media_id, $post_id, $reference ) + { + $sql = "INSERT INTO wiki_post_media (post_id, media_id, reference) VALUES (:postid, :mediaid, :reference)"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ + 'postid' => $post_id, + 'mediaid' => $media_id, + 'reference' => $reference + ]); + return true; + } + + + + + + ## ------------------------------------------------------------------------- + ## + ## FILE METHODS + ## + ## ------------------------------------------------------------------------- + + + /** upload_file_to_folder + * + * upload the file to CloudStorage + * in a virtual folder + * + * @param $folder + */ + public function upload_file_to_folder($folder) + { + // Checks before uploading the file + //////////////////////////////////////////////////////////////////////// + + // ** 1: file is upladed to temporary folder --------------------------- + if (! is_uploaded_file($_FILES['file']['tmp_name'])) { + return 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 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 false; // bye! + } + + // READY to finaly save/upload the file to CDN ///////////////////////// + + $store_filename = $folder .'/'. strtolower($this->clear_file_name($bare_name) .'.'. $file_ext); + + $file_destination = $this->wiki_files_root .'/'. $store_filename; + + // depricated; it is autoloaded include_once CLOUDSTORAGE; + $cloudstorage = new CloudStorage(); + + if ($cloudstorage->upload_object($file_destination, $file_tmp)) { + + return [ + 'path' => $file_destination, + 'type' => $mime_type + ]; + + } else { return false; } + } + + /** uc_split + * + * this is an mb_str_split polyfill because + * pythia uses a php version < 7.4 + * + * CHECK: https://www.php.net/manual/en/function.mb-str-split.php + */ + private function uc_split($string, $length = 1, $encoding = 'UTF-8' ) + { + if(!empty($string)){ + $split = array(); + $mb_strlen = mb_strlen($string,$encoding); + for($pi = 0; $pi < $mb_strlen; $pi += $length){ + $substr = mb_substr($string, $pi,$length,$encoding); + if( !empty($substr)) { + $split[] = $substr; + } + } + } + return $split; + } + + /** clear_file_name + * replace greek characters and strip symbols + */ + private function clear_file_name($str) + { + $el = $this->uc_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 + * @return id (int): id of created media record + */ + public 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; + } + + + ## ------------------------------------------------------------------------- + ## + ## TAG METHODS + ## + ## ------------------------------------------------------------------------- + + /** all tags + * + * @return: (array) all tags + */ + public function all_tags() + { + $sql = "SELECT * FROM wiki_tag"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return ($result === null) + ? false // no result? return false + : $result; // else return results + } + + + /** create_tag + * + * @param $tag (string): the tag label + * @return : id (int) given to tag + */ + public function create_tag($tag) + { + $sql = "INSERT INTO wiki_tag (`name`) VALUES (:label)"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ 'label' => $tag ]); + + $tag_id = $this->pdo->lastInsertId(); + + return $tag_id; + } + + /** destroy_tag + * + * @param $tag_id (int) + */ + public function destroy_tag($id) + { + // remove all post-links to this tag + $clean_posts = "DELETE FROM wiki_post_tags WHERE tag_id = :tagid"; + $clean = $this->pdo->prepare($clean_posts); + $clean->execute([ 'tagid' => $id ]); + + // remove tag from tags table + $sql = "DELETE FROM wiki_tag WHERE id = :tagid"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ 'tagid' => $id ]); + + return true; + } + + /** posts_of_tag + * + * return posts linked to the specified tag (by tag-id) + * + * @param tag_id + * @param limit + * @param fieldset (string): list of post field-names separated with a comma (,) + * @param only_published (bool) + */ + public function posts_of_tag($tag_id, $limit = 0, $fieldset = '*', $only_published = true) + { + // format post field-names for SELECT SQL + // post_fields will be somethning like... `post.*` + // or... `post.id, post.title, post.into` + // --- -- -- - - - + $fields = explode(',', str_replace(' ', '', $fieldset)); + $fields_arr = []; + foreach($fields as $f) { $fields_arr[] = 'wiki_post.'. $f; } + $post_fields = implode(', ', $fields_arr); + + $limit_clause = ($limit) + ? " LIMIT ". $limit + : ""; + + $filter = ($only_published) + ? " AND post.status = 1 " + : ""; + + $sql = "SELECT {$post_fields} FROM wiki_post + LEFT JOIN wiki_post_tags ON wiki_post_tags.post_id = wiki_post.id + LEFT JOIN wiki_tag ON wiki_tag.id = wiki_post_tags.tag_id + WHERE wiki_post.deleted IS NULL AND wiki_tag.id = :tagid {$filter} + {$limit_clause}"; + + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['tagid' => $tag_id]); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return $result; + } + + + /** tag_name + * return tag name (name = label / title / text) + * by tag_id + * + * @param $id (int): given tag-id + * @return (string) + */ + public function tag_name($id) { + $sql = "SELECT `name` FROM wiki_tag WHERE id = :id LIMIT 1"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['id' => $id]); + + $result = $stmt->fetch(); + + return $result['name']; + + } + + /** set_tags_to_post() + * + * REMOVE OLD tags from post; + * then SET NEW tags + * + * NOTE: + * make sure that any old tags are removed from post; + * new tags should not be linked in a later cycle + * + */ + private function set_post_tags($post_id, $tags) + { + $this->remove_tags_from_post($post_id); // remobve any old tags + + foreach($tags as $tag) { // set each tag to post + $this->link_tag_to_post($tag, $post_id); + } + } + + /** remove_tags_from_post + * + * @param $post_id (int) + */ + private function remove_tags_from_post( $post_id ) + { + $sql = "DELETE FROM wiki_post_tags WHERE post_id = :postid"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(['postid' => $post_id]); + return true; + } + + /** set one tag to a specific post + * + * NOTE: + * the method does not check if tag is linked already + * + */ + private function link_tag_to_post( $tag_id, $post_id ) + { + $sql = "INSERT INTO wiki_post_tags (post_id, tag_id) VALUES (:postid, :tagid)"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ + 'postid' => $post_id, + 'tagid' => $tag_id + ]); + return true; + } + + /** create tags for post + * + * creates an array of tags; links post to each one; + * @param $tags (array): a list of tags (labels/texts) + * @param $post_id (int) + */ + private function create_tags_for_post( $tags, $post_id ) + { + foreach($tags as $tag) { + $tag_id = $this->create_tag($tag); // create tag + $this->link_tag_to_post($tag_id, $post_id); // link to post + } + return true; + } + + + public function tags_stats() + { + $sql = "SELECT tag.id, tag.name, COUNT(post_tags.post_id) as totals + FROM wiki_tag + LEFT JOIN wiki_post_tags ON wiki_post_tags.tag_id = wiki_tag.id + GROUP BY wiki_tag.id"; + $stmt = $this->pdo->prepare($sql); + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + + ## ------------------------------------------------------------------------- + ## + ## USER METHODS + ## + ## ------------------------------------------------------------------------- + + /** user_info + * + * no user is passed; user info are exrtacted from Session + * + */ + private function user_info() + { + $user = []; + $keys = [ + 'user_session' => 'user', + 'employee_id' => 'employee', + 'store_id' => 'store', + 'site_id' => 'site', + 'unit_id' => 'unit', + 'employee_rID' => 'rid' + ]; + + foreach($keys as $key => $attribute) { + if (isset($_SESSION[$key]) && ($_SESSION[$key] != '')) { + $user[$attribute] = $_SESSION[$key]; + } + } + return $wiki_user; + } + + + /** update post stats + * + */ + public function update_post_stats($post_id) + { + + $user_id = $_SESSION['user_session']; // get user from session + + $sql = "INSERT INTO wiki_stats + (post_id, user_id, views) VALUES (:post, :user, 1) + ON DUPLICATE KEY UPDATE views = views + 1"; + + $stmt = $this->pdo->prepare($sql); + $stmt->execute([ + 'user' => $user_id, + 'post' => $post_id + ]); + + return true; + + } + +} diff --git a/html/app/views/simple/Render.php b/html/app/views/simple/Render.php new file mode 100644 index 0000000..ebe15fc --- /dev/null +++ b/html/app/views/simple/Render.php @@ -0,0 +1,182 @@ +"; + } + + } + + + /** all_breadcrumbs + * ------------------------------------------------------------------------- + * + * returns an array of all breadcrumbs + * where array-key of each record is category[id] + * + * NOTE: + * --- + * Render::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 all_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['title'] . $delimiter; + } + $breadcrumb .= $node['rec']['title']; // 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::all_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'], + 'title' => $kid['rec']['title'] + ]; + } + return $childs; + } + + + /** date_box + * creates a pretty date box; outputs greek months + * + * @param $date (string) in 'Y-m-d H:i:s' format + * @return html (string) of the date-box + */ + static public function date_box($date) + { + $months_Gr = ['', + 'Ιαν', 'Φεβ', 'Μαρ', 'Απρ', + 'Μάι', 'Ιουν', 'Ιουλ', 'Αυγ', + 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ' + ]; + $date = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $date); + $day = $date->format("j"); + $month = $months_Gr[intval($date->format("m"))]; + $year = $date->format("Y"); + + // print_r([$date, $day, $month, $year]); die(); + + return "
+

{$day}

+

{$month}

+

{$year}

+
"; + } + + /** date_friendly + * outputs an unformated greek-language date string + * + * @param $date (string) in 'Y-m-d H:i:s' format + */ + static public function date_friendly($date, $display_time = false) + { + $months_Gr = ['', + 'Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μάι', 'Ιουν', + 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ' + ]; + $date = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $date); + $day = $date->format("j"); + $month = $months_Gr[intval($date->format("m"))]; + $year = $date->format("Y"); + $time = ($display_time) ? $date->format("H:i") : ''; + + return "{$day} {$month} {$year} {$time}"; + } + +} diff --git a/html/app/views/simple/category.php b/html/app/views/simple/category.php new file mode 100644 index 0000000..15d8b5e --- /dev/null +++ b/html/app/views/simple/category.php @@ -0,0 +1,156 @@ +category_tree(); +$breadcrumbs = Render::all_breadcrumbs($categories); + +// get ALL posts of category +$posts = $wiki->latest_published_posts($category_id, 0); + + +// special case: +// ----------------------------------------------------------------------------- +// ONE post + NO subcaegories ..then redirect to the only post +if (($breadcrumbs[ $category_id ]['childs'] == []) && (count($posts) == 1)) { + header('Location: /wiki/post?id='. $posts[0]['id'], true, 302); + die(); +} + + +// find parent_ids of current category (to open the menu tree) +$category_path = []; +foreach($breadcrumbs[$category_id]['parents'] as $parent) { + $category_path[] = intval($parent['id']); +} +$category_path[] = intval($category_id); + + +// format $posts array +// --- +$posts_formated = []; +foreach($posts as $post) { + $date = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $post['creation_date']); + $posts_formated[] = [ + 'id' => $post['id'], + 'title' => $post['title'], + 'intro' => $post['intro'] ?? false, + 'date' => Render::date_box($post['creation_date']) + ]; +} + + +// then render the page ... +// ----------------------------------------------------------------------------- +?> + + + + Wiki + + false ]); ?> + + + + + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+ + +

+ +

+ + + + + + +

Υποκατηγορίες

+
+ + $child) : ?> + + + + + + + +
+ + + + + + + + +

Άρθρα / Δημοσιεύσεις

+ + $posts_formated, + 'fieldset' => ['intro', 'date'] + ]); + //////////////////////////////////////////////////// + ?> + + + +
+ +
+
+
+ + + + diff --git a/html/app/views/simple/index.php b/html/app/views/simple/index.php new file mode 100644 index 0000000..9860db0 --- /dev/null +++ b/html/app/views/simple/index.php @@ -0,0 +1,123 @@ +category_tree(); +$breadcrumbs = Render::all_breadcrumbs($categories); +$posts = $wiki->latest_published_posts(0, 7); // 0 = no category ; 7 = posts limit + +// format posts array; inject breadcrumbs +// --- +$posts_formated = []; +foreach($posts as $post) { + $posts_formated[] = [ + 'id' => $post['id'], + 'title' => $post['title'], + 'category' => $breadcrumbs[ $post['category_id'] ]['breadcrumb'], + 'intro' => $post['intro'] ?? false, + 'date' => Render::date_box($post['creation_date']) + ]; +} + +// optional fields to be printed into post-list +// --- +$fieldset = [ + 'date', + 'intro', + 'category' +]; + +// then render the page ... +// ----------------------------------------------------------------------------- +?> + + + + Wiki + + false ]); ?> + + + + + +
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ + +
+ +

+ Welcome to Wiki! +

+ + +

Ίσως κάποιο εισαγωγικό κείμενο, στατικό ή δυναμικό. + Pellentesque non mauris ut orci volutpat sollicitudin ut vel ligula. Nullam et leo eu ante ornare ultrices a a leo. Proin non accumsan ex, sit amet finibus purus. +

+ Vestibulum posuere maximus nisl eget consequat. Etiam venenatis lectus sed libero vestibulum viverra. Sed nec nibh ac nulla venenatis tincidunt eu ut diam. Sed tincidunt, arcu sit amet pharetra ornare, libero nunc placerat nunc, at dictum dui diam sit amet metus. Sed venenatis vehicula nisl at hendrerit. +

+ + +

Τελευταίες Δημοσιεύσεις

+ + $posts_formated, + 'fieldset' => $fieldset + ]); + //////////////////////////////////////////////////////// + ?> + +
+ + +
+ +
+ +
+ + + + diff --git a/html/app/views/simple/lorem-dates.php b/html/app/views/simple/lorem-dates.php new file mode 100644 index 0000000..7a7c000 --- /dev/null +++ b/html/app/views/simple/lorem-dates.php @@ -0,0 +1,18 @@ +setTimestamp(time() - rand(0,30000)*1000 + rand(1,1000) ); + + // return time() - rand(0,30000)*1000 + rand(1,1000); + return $dt->format('Y-m-d H:i:s'); +} + + +include 'pythia.php'; + +$posts = $wiki->latest_posts(0, 900); + +foreach($posts as $post) { + $wiki->set_date($post['id'], some_date()); +} +echo "done!"; \ No newline at end of file diff --git a/html/app/views/simple/lorem-intros.php b/html/app/views/simple/lorem-intros.php new file mode 100644 index 0000000..a3629e1 --- /dev/null +++ b/html/app/views/simple/lorem-intros.php @@ -0,0 +1,721 @@ +latest_published_posts(0, 900, 'id, title, intro'); +$tags = $wiki->all_tags(); + +shuffle($posts); +shuffle($titles); + + +for ($i = 0 ; $i < 500 ; $i++) { + // $wiki->set_intro($posts[$i]['id'], some_lorem_intro()); + // $wiki->set_status($posts[$i]['id'], 0); + // $wiki->set_title($posts[$i]['id'], $titles[$i]); + shuffle($tags); + $c = ($i % 3) + 1; + for ($j = 0; $j < $c; $j ++) { $wiki->link_tag_to_post($tags[$j]['id'], $posts[$i]['id']); } +} +echo "done!"; + + + diff --git a/html/app/views/simple/lorem-posts.php b/html/app/views/simple/lorem-posts.php new file mode 100644 index 0000000..5f7fccb --- /dev/null +++ b/html/app/views/simple/lorem-posts.php @@ -0,0 +1,93 @@ +". $lorem[$i] ."

"; + } + + return $html; +} + +function some_lorem_title() { + // make an array of + $lorem = explode(".", // sentences only + str_replace(". ", ".", + implode(" ", LOREM) // made from all Lorem-ipsums + ) + ); + shuffle($lorem); + return $lorem[0]; +} + + +include 'pythia.php'; + + +$categories = $wiki->get_categories(); + +$non_root = []; +foreach($categories as $node) { + if (intval($node['parent_id']) > 0) { $non_root[] = $node['id']; } + // if (intval($node['parent_id']) > 5) { $non_root[] = $node['id']; } +} +// print_r($non_root); die(); + +echo "
";
+for( $i = 0 ; $i < 250 ; $i++ ) {
+
+    $temp = $non_root;
+    shuffle($temp);
+
+    $post = [
+        'title' => some_lorem_title(),
+        'category' => intval($temp[0]),
+        'body' => some_lorem_html()
+    ];
+    print_r($post);
+
+    $wiki->add_post($post);
+}
+echo "
"; \ No newline at end of file diff --git a/html/app/views/simple/post.php b/html/app/views/simple/post.php new file mode 100644 index 0000000..9e37cd9 --- /dev/null +++ b/html/app/views/simple/post.php @@ -0,0 +1,236 @@ +get_post($post_id); +$categories = $wiki->category_tree(); +$breadcrumbs = Render::all_breadcrumbs($categories); + +if ($post == false) { // when post not found + $post = [ // create a virtual record with error messages + 'id' => 0, + 'title' => 'Το άρθρο που αναζητήσατε δεν υπάρχει', + 'category_id' => 0, + 'intro' => 'Για να βρειτε το άρθρο που ψάχνετε χρησιμοποιήστε την αναζήτηση + του wiki ή πλοηγηθείτε στο δέντρο των κατηγοριών.', + 'body' => "" + ]; + +} else { // post found; prepare article's data to render + + $category_id = $post['category_id']; + + // find parent_ids of current category (to open the menu tree) + $category_path = []; + foreach($breadcrumbs[$category_id]['parents'] as $parent) { + $category_path[] = intval($parent['id']); + } + $category_path[] = intval($category_id); + +} + + +// format special parts of post +$parsedown = new Parsedown(); +$body_html = $parsedown->text($post['body']); // body: markdown to html + +// decode json output (tags + medias) +if ($post['tags_json'] != null) { $tags = json_decode($post['tags_json']); } +if ($post['medias_json'] != null) { $medias = json_decode($post['medias_json']); } + + +// then render the page ... +// ----------------------------------------------------------------------------- +?> + + + + Wiki + + false ]); ?> + + + + +
+ +
+ + +
+ +
+ +
+ +
+ + + +
+ + +
+ + +

+ + + + + + + + +

+ + + + + + +

+ + + +
+ +
+ + + +
+

Συνημμένα:

+
    + + reference == 1) : ?> +
  1. + + title?> + +
  2. + + +
+
+ + + + +
+ Επικέτες: + + name?> + +
+ + +
+ +
+ +
+
+ + + + + + + + + + + + + + diff --git a/html/assets/css/admin.css b/html/assets/css/admin.css index 0dd338f..4bd8718 100644 --- a/html/assets/css/admin.css +++ b/html/assets/css/admin.css @@ -1,35 +1,35 @@ :root { - --wiki-btn-blue: #337ab7; - --wiki-btn-cyan: #5bc0de; - --wiki-btn-grey: #c9c9c9; - --wiki-btn-grey-light: #c9c9c999; + --class-btn-blue: #337ab7; + --class-btn-cyan: #5bc0de; + --class-btn-grey: #c9c9c9; + --class-btn-grey-light: #c9c9c999; } -.wiki .manage { +.classroom .manage { width: 100%; max-width: 1600px; margin: 0 auto 2em; } -.wiki .manage.maximized { max-width: 1960px; } -.wiki .manage.minimized { max-width: 960px; } -.wiki .manage.edit-post { max-width: 1420px; } +.classroom .manage.maximized { max-width: 1960px; } +.classroom .manage.minimized { max-width: 960px; } +.classroom .manage.edit-post { max-width: 1420px; } -.wiki .edit-post textarea[name=intro] { height: 12vh; } -.wiki .edit-post textarea[name=body] { +.classroom .edit-post textarea[name=intro] { height: 12vh; } +.classroom .edit-post textarea[name=body] { height: calc(100vh - 492px); min-height:320px; font-family: Consolas, 'Ubuntu Mono','Courier New', Courier, monospace; color: #333; } -.wiki .edit-post .post-media button.over-bar { margin: -26px 0 0 0; } -.wiki .edit-post .post-media ul { +.classroom .edit-post .post-media button.over-bar { margin: -26px 0 0 0; } +.classroom .edit-post .post-media ul { border: 1px solid #ccc; border-radius: 4px; padding: 6px; } -.wiki .edit-post .post-media ul li { +.classroom .edit-post .post-media ul li { border-bottom: 1px solid #ddd; list-style-type: none; padding: 3px 6px; @@ -38,92 +38,92 @@ justify-content: space-between; align-items: center; } -.wiki .edit-post .post-media ul li:last-child { border-bottom: none ;} -.wiki .edit-post .post-media ul li:hover { background: #eee; } +.classroom .edit-post .post-media ul li:last-child { border-bottom: none ;} +.classroom .edit-post .post-media ul li:hover { background: #eee; } -.wiki .manage h2 { +.classroom .manage h2 { font-size: 20px; color: #445; margin: 20px 0; } -.wiki .manage.edit-post textarea { width: 100% !important;} -.wiki .manage.edit-post .select2-selection { min-height: 34px; } +.classroom .manage.edit-post textarea { width: 100% !important;} +.classroom .manage.edit-post .select2-selection { min-height: 34px; } -.wiki .manage.edit-post select#status.published { +.classroom .manage.edit-post select#status.published { background-color: #5cb85c; border-color: #4cae4c; color: #fff; text-align: center; } -.wiki .manage.edit-post select#status option { +.classroom .manage.edit-post select#status option { background: #fff; color: #333; } /* --- optional fields: defaut=hide --- */ -.wiki .edit-post .optional label { +.classroom .edit-post .optional label { margin-bottom: 1em; padding-bottom: .5em; - border-bottom: 1px solid var(--wiki-btn-grey-light) + border-bottom: 1px solid var(--class-btn-grey-light) } -.wiki .edit-post .optional label.show { +.classroom .edit-post .optional label.show { margin-bottom: unset; border: none; } -.wiki .edit-post .optional label:hover { +.classroom .edit-post .optional label:hover { cursor: pointer; - color: var(--wiki-btn-blue); + color: var(--class-btn-blue); } -.wiki .edit-post .optional label::after { float: right; content: "+"; } -.wiki .edit-post .optional label.show::after { content: "–"; } -.wiki .edit-post .optional label + div { display: none; } -.wiki .edit-post .optional label.show + div { display: block; } +.classroom .edit-post .optional label::after { float: right; content: "+"; } +.classroom .edit-post .optional label.show::after { content: "–"; } +.classroom .edit-post .optional label + div { display: none; } +.classroom .edit-post .optional label.show + div { display: block; } .alert.alert-info.title-bar { min-height: 52px; margin-top: 20px; } .alert.alert-info.title-bar button { margin-left: 8px; } -.wiki .title-bar { +.classroom .title-bar { display: flex; justify-content: space-between; align-items: center; padding: 8px 8px 8px 16px; } -.wiki .action-btn button { float: right; } +.classroom .action-btn button { float: right; } -.wiki .top-bar { padding: 4px 0; } +.classroom .top-bar { padding: 4px 0; } -.wiki .top-bar .breadcrumb .btn { +.classroom .top-bar .breadcrumb .btn { margin-left: 0; margin-right: 8px; border-radius: 3px; - color: var(--wiki-btn-blue); + color: var(--class-btn-blue); } -.wiki .top-bar .btn:hover { - background-color: var(--wiki-btn-grey-light); +.classroom .top-bar .btn:hover { + background-color: var(--class-btn-grey-light); } -.wiki .top-bar .btn.selected { +.classroom .top-bar .btn.selected { color: #fff; - background-color: var(--wiki-btn-blue); + background-color: var(--class-btn-blue); } /* --- dataTables --- */ -.wiki .manage .dataTable tbody tr:hover { background: #f8f8f8; } -.wiki .manage .dataTable .dt-actions { width: 48px; text-align: right } +.classroom .manage .dataTable tbody tr:hover { background: #f8f8f8; } +.classroom .manage .dataTable .dt-actions { width: 48px; text-align: right } /* --- unpublished posts -- */ -.wiki .manage .dataTable .fa-eye-slash { color: #999; } +.classroom .manage .dataTable .fa-eye-slash { color: #999; } -.wiki .js-copy:hover::after { +.classroom .js-copy:hover::after { opacity: 0; pointer-events: none; background: #ddd7; @@ -137,7 +137,7 @@ transform: translate(-50%, 0); } -.wiki .js-copy.copied:hover::after { +.classroom .js-copy.copied:hover::after { content: "Αντιγράφηκε"; opacity: 1; margin-top: -22px; diff --git a/html/assets/css/class.css b/html/assets/css/class.css new file mode 100644 index 0000000..6acb49e --- /dev/null +++ b/html/assets/css/class.css @@ -0,0 +1,348 @@ +/** Classroom sections + * (main layout css-classes) + * ----------------------------------------------------------------------------- + * .classroom { + * .top-bar { + * .breadcrumb + * .admin + * } + * .side-bar { + * .search + * .categories + * } + * .content + * } + * ----------------------------------------------------------------------------- + */ + + +/* Variables + * ---------------------------------------------------------------------------- + */ + +:root { + --class-blue: #337ab7; + --class-blue-opaque: #337ab799; + --class-blue-dark: #082e44; + --class-orange: #f47920; + --class-orange-opaque: #f4792099; + --class-grey-background: #eeedeb; + --class-red: #d13; +} + + + +/* main layout classes + * ---------------------------------------------------------------------------- + */ + +.classroom .top-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 14px; +} + +.classroom .top-bar .breadcrumb { margin: 0; padding: 0; background: none; } + + +.classroom .top-bar .admin .dropdown-menu .fas { + width: 32px; + text-align: center; +} + + + +/* category list menu + * ----------------------------------------------------------------------------- + */ + + .classroom .side-bar { padding-top: 20px; } + .classroom .side-bar h3 { + font-weight: 600; font-size: 1.4em; + color: var(--class-blue); +} + + /* --- ul > li > a --- */ + +.classroom .side-bar ul { list-style: none; padding: 0; } +.classroom .side-bar ul li { margin: 0.5em 0; } +.classroom .side-bar ul li a { + display: flex; + padding: .2em; + background: #4442; + border-radius: 6px; + transition: background 0.3s; +} +.classroom .side-bar ul li a:hover, +.classroom .side-bar ul li a:active { + background: #cde; + text-decoration: none; +} + +/* --- li > a > span --- */ + +.classroom .side-bar ul li a span { + display: block; + align-self: center; + padding: .3em; +} +.classroom .side-bar ul li a .label { + font-size: 13px; + color: var(--class-blue-dark); + flex-grow: 1; + text-align: left; + padding-left: 1em; + border-radius: 6px; + white-space: normal; + font-weight: 400; +} + +/* --- inner --- */ + +.classroom .side-bar ul .inner { + padding-left: 1em; + overflow: hidden; + display: none; +} +.classroom .side-bar ul .inner.show { display: block; } + +/* --- has-childs | toggler --- */ + +.classroom .side-bar ul li a.has-childs { background: #9bf4; } +.classroom .side-bar ul li a.has-childs:hover { background: #cde;} + +.classroom .side-bar ul li a.has-childs .label { + font-weight: 600; + border-radius: 6px 0 0 6px; + width: calc(100% - 36px); +} + +.classroom .side-bar ul li a.has-childs .toggler { + text-align: center; + width: 32px; + font-size: 15px; + border-radius: 4px; + background: #7774; + font-weight: 700; +} +.classroom .side-bar ul li a.has-childs .toggler:hover { + background: var(--class-blue); + color: #fff; +} +.classroom .side-bar ul li a.selected { + background: var(--class-orange-opaque); +} +.classroom .side-bar ul li a:hover, +.classroom .side-bar ul li a:active, +.classroom .side-bar ul li a:focus, +.classroom .side-bar ul li a:visited, +.classroom .side-bar ul li a:link, +.classroom .side-bar ul li a span:hover, +.classroom .side-bar ul li a span:active, +.classroom .side-bar ul li a span:focus, +.classroom .side-bar ul li a span:visited, +.classroom .side-bar ul li a span:link, +.classroom .side-bar ul li a span:target { text-decoration: none !important; } + + + +.classroom .post img { max-width: 100%; } + + + +/** breadcrumb + * ----------------------------------------------------------------------------- + */ + +.classroom .breadcrumb { counter-reset: var(--class-blue); } +.classroom .breadcrumb i.fas { padding: 0 6px; } + + + + +/** content + * ----------------------------------------------------------------------------- + */ + +.classroom .content--wrapper { + max-width: 1024px; + width: 100%; + margin: 1em auto; + padding: 0 2em 2em 2em; +} +.classroom .content h2 { + font-weight: 700; + font-size: 24px; + padding: 8px 3em 1em 0; /* 1em 3em 1em 0; */ + color: var(--class-blue-dark); +} +.classroom .content h3 { + font-weight: 600; font-size: 1.4em; + padding: 1em 3em 1em 0; + color: var(--class-blue); + border-bottom: 1px dashed var(--class-blue-opaque); +} +.classroom .content h3 span { font-weight: 400; color: #444; } +.classroom .content p { + line-height: 150%; + padding: 5px 0; +} + +/* --- post: medias -- */ + +.classroom .content--wrapper .medias { + padding: 2em 0 1em; + border-top: 1px solid var(--class-grey-background); +} +.classroom .content--wrapper .medias ol li { + margin-left: -12px; + padding: 2px 8px; +} +.classroom .content--wrapper .medias ol li::marker { + font-size: 12px; + color: #999; +} + +/* --- post: tags --- */ + +.classroom .content--wrapper .tags { + padding: 2em 0 1em; + border-top: 1px solid var(--class-grey-background); +} +.classroom .content--wrapper .tags a { + color: #444; + background-color: var(--class-grey-background); + padding: 6px 12px; + margin: 6px; + border-radius: 3px; +} + + + + +/** sub-categories on category page + * ----------------------------------------------------------------------------- + */ + +.classroom .category-childs { margin: 1em; padding: 1em; } + +.classroom .category-childs a { + display: inline-block; + margin: .5em; + padding: .2em 2em; + border-radius: 2em; + border: 1px solid var(--class-blue); + line-height: 2em; + font-weight: 600; + transition: background 0.3s; +} + +.classroom .category-childs a:hover { + background-color: var(--class-blue); + color: #fffe; + text-decoration: none; +} + + + + + +/** articles-list + * ----------------------------------------------------------------------------- + * 22 | Title of artivle + * July | path / of / categories + * 2023 | * some intro of article + * ----------------------------------------------------------------------------- + */ +.classroom .post-card { + padding: .5em; + margin: 1em; + border-bottom: 1px solid #7773; + display: flex; +} +.classroom .post-card .post-card--content { + flex-grow: 1; + width: calc(100% - 90px); +} +.classroom .post-card h3 { padding: 0; margin: 0; border: none; } +.classroom .post-card h3 a { display: block; } +.classroom .post-card h3 a:hover { color: #f47920; } +.classroom .post-card .category { color: #566; } +.classroom .post-card .intro { + font-style: italic; + font-size: 16px; +} +/* --- date block --- */ +.classroom .date { + display: flex; + flex-direction: column; + width: 90px; +} +.classroom .date p { + padding: 0; margin: 0; + line-height: 115%; + text-align: center; +} +.classroom .date .day { font-size: 24px; color: var(--class-blue-opaque); } +.classroom .date .month { font-size: 16px; color: #555; } +.classroom .date .year { font-size: 12px; color: #666; } + + + + + +/** post (article) + * ----------------------------------------------------------------------------- + */ + + + +.classroom .post h2 { padding-bottom: 0; } + +.classroom .post h2 span { float:right; margin-right: -3em;} + +.classroom .post .intro { + font-style: italic; + font-size: 18px; + color: var(--class-blue-dark); + padding-bottom: 1em; +} + +.classroom .post .post--date { + padding: 0; + color: var(--class-blue); +} + +.classroom .article-body img { + display: block; + max-width: 100%; + margin: 1em auto; +} +/* +.classroom .article-body a.modal-toggler { + padding: 0 16px; + background: #def; + color: #334; + border-radius: 3px; + text-decoration: none; + border: 1px solid var(--class-blue-opaque); +} +.classroom .article-body a.modal-toggler:hover { background: var(--class-blue); color: #fff; } +*/ +.classroom .article-body table { + margin: 20px auto; + min-width: 67%; + border-spacing: 6px 0; + border-collapse: separate; +} +.classroom .article-body table tr { margin: 4px; } +.classroom .article-body table thead th { + color: var(--class-btn-blue); + border-bottom: 3px solid var(--class-blue); + padding: 0 8px; +} +.classroom .article-body table tbody td { + padding: 6px 8px; + border-bottom: 1px solid var(--class-grey-background); +} \ No newline at end of file diff --git a/html/assets/css/wiki.css b/html/assets/css/wiki.css deleted file mode 100644 index 7599de9..0000000 --- a/html/assets/css/wiki.css +++ /dev/null @@ -1,348 +0,0 @@ -/** Wiki sections - * (main layout css-classes) - * ----------------------------------------------------------------------------- - * .wiki { - * .top-bar { - * .breadcrumb - * .admin - * } - * .side-bar { - * .search - * .categories - * } - * .content - * } - * ----------------------------------------------------------------------------- - */ - - -/* Variables - * ---------------------------------------------------------------------------- - */ - -:root { - --wiki-blue: #337ab7; - --wiki-blue-opaque: #337ab799; - --wiki-blue-dark: #082e44; - --wiki-orange: #f47920; - --wiki-orange-opaque: #f4792099; - --wiki-grey-background: #eeedeb; - --wiki-red: #d13; -} - - - -/* main layout classes - * ---------------------------------------------------------------------------- - */ - -.wiki .top-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px 14px; -} - -.wiki .top-bar .breadcrumb { margin: 0; padding: 0; background: none; } - - -.wiki .top-bar .admin .dropdown-menu .fas { - width: 32px; - text-align: center; -} - - - -/* category list menu - * ----------------------------------------------------------------------------- - */ - - .wiki .side-bar { padding-top: 20px; } - .wiki .side-bar h3 { - font-weight: 600; font-size: 1.4em; - color: var(--wiki-blue); -} - - /* --- ul > li > a --- */ - -.wiki .side-bar ul { list-style: none; padding: 0; } -.wiki .side-bar ul li { margin: 0.5em 0; } -.wiki .side-bar ul li a { - display: flex; - padding: .2em; - background: #4442; - border-radius: 6px; - transition: background 0.3s; -} -.wiki .side-bar ul li a:hover, -.wiki .side-bar ul li a:active { - background: #cde; - text-decoration: none; -} - -/* --- li > a > span --- */ - -.wiki .side-bar ul li a span { - display: block; - align-self: center; - padding: .3em; -} -.wiki .side-bar ul li a .label { - font-size: 13px; - color: var(--wiki-blue-dark); - flex-grow: 1; - text-align: left; - padding-left: 1em; - border-radius: 6px; - white-space: normal; - font-weight: 400; -} - -/* --- inner --- */ - -.wiki .side-bar ul .inner { - padding-left: 1em; - overflow: hidden; - display: none; -} -.wiki .side-bar ul .inner.show { display: block; } - -/* --- has-childs | toggler --- */ - -.wiki .side-bar ul li a.has-childs { background: #9bf4; } -.wiki .side-bar ul li a.has-childs:hover { background: #cde;} - -.wiki .side-bar ul li a.has-childs .label { - font-weight: 600; - border-radius: 6px 0 0 6px; - width: calc(100% - 36px); -} - -.wiki .side-bar ul li a.has-childs .toggler { - text-align: center; - width: 32px; - font-size: 15px; - border-radius: 4px; - background: #7774; - font-weight: 700; -} -.wiki .side-bar ul li a.has-childs .toggler:hover { - background: var(--wiki-blue); - color: #fff; -} -.wiki .side-bar ul li a.selected { - background: var(--wiki-orange-opaque); -} -.wiki .side-bar ul li a:hover, -.wiki .side-bar ul li a:active, -.wiki .side-bar ul li a:focus, -.wiki .side-bar ul li a:visited, -.wiki .side-bar ul li a:link, -.wiki .side-bar ul li a span:hover, -.wiki .side-bar ul li a span:active, -.wiki .side-bar ul li a span:focus, -.wiki .side-bar ul li a span:visited, -.wiki .side-bar ul li a span:link, -.wiki .side-bar ul li a span:target { text-decoration: none !important; } - - - -.wiki .post img { max-width: 100%; } - - - -/** breadcrumb - * ----------------------------------------------------------------------------- - */ - -.wiki .breadcrumb { counter-reset: var(--wiki-blue); } -.wiki .breadcrumb i.fas { padding: 0 6px; } - - - - -/** content - * ----------------------------------------------------------------------------- - */ - -.wiki .content--wrapper { - max-width: 1024px; - width: 100%; - margin: 1em auto; - padding: 0 2em 2em 2em; -} -.wiki .content h2 { - font-weight: 700; - font-size: 24px; - padding: 8px 3em 1em 0; /* 1em 3em 1em 0; */ - color: var(--wiki-blue-dark); -} -.wiki .content h3 { - font-weight: 600; font-size: 1.4em; - padding: 1em 3em 1em 0; - color: var(--wiki-blue); - border-bottom: 1px dashed var(--wiki-blue-opaque); -} -.wiki .content h3 span { font-weight: 400; color: #444; } -.wiki .content p { - line-height: 150%; - padding: 5px 0; -} - -/* --- post: medias -- */ - -.wiki .content--wrapper .medias { - padding: 2em 0 1em; - border-top: 1px solid var(--wiki-grey-background); -} -.wiki .content--wrapper .medias ol li { - margin-left: -12px; - padding: 2px 8px; -} -.wiki .content--wrapper .medias ol li::marker { - font-size: 12px; - color: #999; -} - -/* --- post: tags --- */ - -.wiki .content--wrapper .tags { - padding: 2em 0 1em; - border-top: 1px solid var(--wiki-grey-background); -} -.wiki .content--wrapper .tags a { - color: #444; - background-color: var(--wiki-grey-background); - padding: 6px 12px; - margin: 6px; - border-radius: 3px; -} - - - - -/** sub-categories on category page - * ----------------------------------------------------------------------------- - */ - -.wiki .category-childs { margin: 1em; padding: 1em; } - -.wiki .category-childs a { - display: inline-block; - margin: .5em; - padding: .2em 2em; - border-radius: 2em; - border: 1px solid var(--wiki-blue); - line-height: 2em; - font-weight: 600; - transition: background 0.3s; -} - -.wiki .category-childs a:hover { - background-color: var(--wiki-blue); - color: #fffe; - text-decoration: none; -} - - - - - -/** articles-list - * ----------------------------------------------------------------------------- - * 22 | Title of artivle - * July | path / of / categories - * 2023 | * some intro of article - * ----------------------------------------------------------------------------- - */ -.wiki .post-card { - padding: .5em; - margin: 1em; - border-bottom: 1px solid #7773; - display: flex; -} -.wiki .post-card .post-card--content { - flex-grow: 1; - width: calc(100% - 90px); -} -.wiki .post-card h3 { padding: 0; margin: 0; border: none; } -.wiki .post-card h3 a { display: block; } -.wiki .post-card h3 a:hover { color: #f47920; } -.wiki .post-card .category { color: #566; } -.wiki .post-card .intro { - font-style: italic; - font-size: 16px; -} -/* --- date block --- */ -.wiki .date { - display: flex; - flex-direction: column; - width: 90px; -} -.wiki .date p { - padding: 0; margin: 0; - line-height: 115%; - text-align: center; -} -.wiki .date .day { font-size: 24px; color: var(--wiki-blue-opaque); } -.wiki .date .month { font-size: 16px; color: #555; } -.wiki .date .year { font-size: 12px; color: #666; } - - - - - -/** post (article) - * ----------------------------------------------------------------------------- - */ - - - -.wiki .post h2 { padding-bottom: 0; } - -.wiki .post h2 span { float:right; margin-right: -3em;} - -.wiki .post .intro { - font-style: italic; - font-size: 18px; - color: var(--wiki-blue-dark); - padding-bottom: 1em; -} - -.wiki .post .post--date { - padding: 0; - color: var(--wiki-blue); -} - -.wiki .article-body img { - display: block; - max-width: 100%; - margin: 1em auto; -} -/* -.wiki .article-body a.modal-toggler { - padding: 0 16px; - background: #def; - color: #334; - border-radius: 3px; - text-decoration: none; - border: 1px solid var(--wiki-blue-opaque); -} -.wiki .article-body a.modal-toggler:hover { background: var(--wiki-blue); color: #fff; } -*/ -.wiki .article-body table { - margin: 20px auto; - min-width: 67%; - border-spacing: 6px 0; - border-collapse: separate; -} -.wiki .article-body table tr { margin: 4px; } -.wiki .article-body table thead th { - color: var(--wiki-btn-blue); - border-bottom: 3px solid var(--wiki-blue); - padding: 0 8px; -} -.wiki .article-body table tbody td { - padding: 6px 8px; - border-bottom: 1px solid var(--wiki-grey-background); -} \ No newline at end of file -- cgit v1.2.3