summaryrefslogtreecommitdiff
path: root/html/app/views
diff options
context:
space:
mode:
authorGeorge Halkiadakis <gchalkiadakis@sklavenitis.co.gr>2023-03-14 03:10:19 +0200
committerGeorge Halkiadakis <gchalkiadakis@sklavenitis.co.gr>2023-03-14 03:10:19 +0200
commit51b3554ff2988ac61ba3f604431e09941cd95545 (patch)
tree6e2331212ed6f0556e420450deb93e6822e846cd /html/app/views
parent8b928471412bbf9016df9b01b2308f003e455d7d (diff)
downloadclassroom-51b3554ff2988ac61ba3f604431e09941cd95545.tar.gz
classroom-51b3554ff2988ac61ba3f604431e09941cd95545.tar.bz2
classroom-51b3554ff2988ac61ba3f604431e09941cd95545.zip
main css classes for front/back-end
Diffstat (limited to 'html/app/views')
-rw-r--r--html/app/views/simple/Model.php887
-rw-r--r--html/app/views/simple/Render.php182
-rw-r--r--html/app/views/simple/category.php156
-rw-r--r--html/app/views/simple/index.php123
-rw-r--r--html/app/views/simple/lorem-dates.php18
-rw-r--r--html/app/views/simple/lorem-intros.php721
-rw-r--r--html/app/views/simple/lorem-posts.php93
-rw-r--r--html/app/views/simple/post.php236
8 files changed, 2416 insertions, 0 deletions
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 @@
+<?php
+/** Wiki Model Class
+ * -----------------------------------------------------------------------------
+ *
+ * + connects to the Wiki database; forwards SQL queries and returns data
+ *
+ * + forms data to be used in the application
+ *
+ * + implements some optimizations (data-caching)
+ *
+ * -----------------------------------------------------------------------------
+ */
+class Wiki_Model
+{
+
+ protected $pdo;
+
+ protected $pdo_pythia;
+
+ protected $user_id; // connected user-id
+
+ protected $wiki_files_root = 'uploads/wiki/files'; // root path for wiki file attachments
+
+
+ /** __construct
+ *
+ * Connects to the Wiki database
+ *
+ */
+ public function __construct()
+ {
+ /// // Default
+ /// $db = new Database();
+ /// $this->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 @@
+<?php
+/** Wiki Render Class
+ * -----------------------------------------------------------------------------
+ *
+ * + automates the creation of common html elements used in Wiki
+ *
+ * + methods are called statically (without object-instantiation)
+ *
+ * + Render class constructs the Render::all_breadcrumbs() super-array
+ *
+ * -----------------------------------------------------------------------------
+ */
+class Render
+{
+
+ /** Render::template($data)
+ * (as any render function in anom framework)
+ *
+ * @param $file (string) : filename [with path] of a php/html (acting as template)
+ * @param $data : array of variable-name:value pairs (extracted/used into template)
+ */
+ static function template($file, $data) {
+
+ if (file_exists($file)) {
+ extract( $data );
+ require( $file );
+
+ } else {
+ echo "<!-- view ". $file ." is missing -->";
+ }
+
+ }
+
+
+ /** 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 <select-option> 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 "<div class='date'>
+ <p class='day'>{$day}</p>
+ <p class='month'>{$month}</p>
+ <p class='year'>{$year}</p>
+ </div>";
+ }
+
+ /** 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 @@
+<?php
+// init app
+// -----------------------------------------------------------------------------
+include 'pythia.php';
+
+
+// read request parameters
+// -----------------------------------------------------------------------------
+$category_id = intval($_GET['id']);
+
+
+// Get all data needed
+// (so you don't have to make multiple requests to the wiki-database)
+// -----------------------------------------------------------------------------
+
+$categories = $wiki->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 ...
+// -----------------------------------------------------------------------------
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Wiki</title>
+
+ <?php Render::template("sections/common_libs.php", [ 'admin' => false ]); ?>
+
+</head>
+<body>
+ <?php include HEAD; ?>
+
+ <div class="container-fluid main-content wiki">
+
+ <div class="top-bar">
+ <div class="breadcrumb">
+ <?php // breadcrumbs
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/breadcrumbs.php", [
+ 'id' => $category_id,
+ 'title' => $breadcrumbs[ $category_id ]['rec']['title'],
+ 'parents' => $breadcrumbs[ $category_id ]['parents']
+ ]); ////////////////////////////////////////////////////
+ ?>
+ </div>
+
+ <div class="admin">
+ <?php include "sections/admin-dropdown.php"; ?>
+ </div>
+
+ </div><!-- /top-bar -->
+
+ <div class="row">
+ <div class="col-xl-2 col-lg-3 col-sm-4 side-bar">
+
+ <!-- <h3>Κατηγορίες</h3> -->
+ <?php // categories hierarchical menu
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/categories_menu.php", [
+ 'categories' => $categories,
+ 'open_path' => $category_path
+ ]); ////////////////////////////////////////////////////
+ ?>
+
+ </div>
+ <div class="col-xl-10 col-lg-9 col-sm-8 content">
+
+ <div class="content--wrapper">
+
+ <!-- Category Title -->
+ <h2>
+ <?=$breadcrumbs[ $category_id ]['rec']['title']?>
+ </h2>
+
+
+ <!-- Sub-Categories -->
+
+ <?php if ($breadcrumbs[ $category_id ]['childs'] != []) : ?>
+
+ <h3>Υποκατηγορίες</h3>
+ <div class="category-childs">
+
+ <?php foreach($breadcrumbs[ $category_id ]['childs'] as $key => $child) : ?>
+
+ <a href="/wiki/category?id=<?=$child['id']?>">
+ <?=$child['title']?>
+ </a>
+
+ <?php endforeach; ?>
+
+ </div>
+
+ <?php endif; ?>
+
+
+ <!-- Articles List -->
+
+ <?php if ($posts) : ?>
+
+ <h3>Άρθρα / Δημοσιεύσεις</h3>
+
+ <?php // list of posts/articles
+ ////////////////////////////////////////////////////
+ Render::template("sections/articles_list.php", [
+ 'articles' => $posts_formated,
+ 'fieldset' => ['intro', 'date']
+ ]);
+ ////////////////////////////////////////////////////
+ ?>
+ <?php endif; ?>
+
+
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <script src="/wiki/js/wiki.js"></script>
+</body>
+</html>
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 @@
+<?php
+// init app
+// -----------------------------------------------------------------------------
+include 'pythia.php';
+
+// Get all data needed
+// (so you don't have to make multiple requests to the wiki-database)
+// -----------------------------------------------------------------------------
+$categories = $wiki->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 ...
+// -----------------------------------------------------------------------------
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Wiki</title>
+
+ <?php Render::template("sections/common_libs.php", [ 'admin' => false ]); ?>
+
+</head>
+<body>
+ <?php include HEAD; ?>
+
+ <div class="container-fluid main-content wiki">
+
+ <div class="top-bar">
+
+ <div class="breadcrumb">
+ <?php // breadcrumbs
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/breadcrumbs.php", [
+ 'id' => 0,
+ 'title' => '',
+ 'parents' => []
+ ]); ////////////////////////////////////////////////////////
+ ?>
+ </div>
+
+ <div class="admin">
+ <?php include "sections/admin-dropdown.php"; ?>
+ </div>
+
+ </div><!-- /top-bar -->
+
+ <div class="row">
+
+ <div class="col-xl-2 col-lg-3 col-sm-4 side-bar">
+
+ <!-- <h3>Κατηγορίες</h3> -->
+ <?php // categories hierarchical menu
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/categories_menu.php", [
+ 'categories' => $categories,
+ 'open_path' => []
+ ]); ////////////////////////////////////////////////////
+ ?>
+
+ </div><!-- /side-bar -->
+
+ <div class="col-xl-10 col-lg-9 col-sm-8 content">
+
+ <!-- ARTICLES LIST -->
+ <div class="content--wrapper">
+
+ <h2>
+ Welcome to Wiki!
+ </h2>
+
+
+ <p>Ίσως κάποιο εισαγωγικό κείμενο, στατικό ή δυναμικό.
+ 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.
+ </p><p>
+ 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.
+ </p>
+
+
+ <h3>Τελευταίες Δημοσιεύσεις</h3>
+
+ <?php // list of posts/articles
+ ////////////////////////////////////////////////////////
+ Render::template("sections/articles_list.php", [
+ 'articles' => $posts_formated,
+ 'fieldset' => $fieldset
+ ]);
+ ////////////////////////////////////////////////////////
+ ?>
+
+ </div>
+
+
+ </div><!-- /content -->
+
+ </div>
+
+ </div> <!-- /main-content wiki -->
+
+ <script src="/wiki/js/wiki.js"></script>
+</body>
+</html>
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 @@
+<?php
+function some_date() {
+ $dt = new DateTime();
+ $dt->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 @@
+<?php
+
+define("LOREM", [
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla sit amet venenatis urna. Mauris dictum justo diam, quis aliquet massa accumsan placerat. Vivamus eu dapibus erat. Fusce at pretium enim. Curabitur eget pulvinar lectus. Sed bibendum lectus nisi, eget elementum elit laoreet sed. Curabitur pretium dapibus magna sed rhoncus. Donec at velit eget metus posuere tincidunt.'
+ ,
+ '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.'
+ ,
+ 'Fusce libero lorem, tristique id nunc vel, pellentesque consectetur lacus. Aenean elit elit, euismod at neque ac, dapibus efficitur lacus. Sed fermentum vehicula luctus. Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus.'
+ ,
+ 'Nullam imperdiet tempus odio at pretium. 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. Integer augue felis, ullamcorper in volutpat et, maximus eget erat.'
+ ,
+ 'Suspendisse turpis sapien, cursus non aliquam sed, accumsan sed arcu. Sed pellentesque ipsum metus. Maecenas placerat enim non massa laoreet convallis. Etiam quis dolor interdum, condimentum ipsum vitae, ullamcorper nulla. Nulla ornare volutpat sagittis. Morbi at sapien nibh.'
+ ,
+ 'Curabitur nec quam placerat, placerat elit sit amet, tincidunt neque. Pellentesque nec maximus neque. Aenean maximus quam in sem aliquet, lobortis blandit sem lobortis. Aliquam id venenatis tortor, quis sollicitudin turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.'
+ ,
+ 'Phasellus suscipit turpis nec consequat euismod. Vivamus eleifend sapien quis mollis scelerisque. Cras et nunc sed turpis dictum porta. Cras vel scelerisque ex, ac volutpat dui. Donec eleifend porta est sit amet fringilla.'
+ ,
+ 'Nunc sed purus sed nibh lobortis luctus a vitae ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus.'
+ ,
+ 'Phasellus consequat purus eu porta sagittis. In hac habitasse platea dictumst. Maecenas lorem diam, scelerisque non neque euismod, lobortis congue massa.'
+ ,
+ 'Nunc sed purus sed nibh lobortis luctus a vitae ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus. In pulvinar facilisis ornare.'
+ ,
+ 'Etiam sit amet nisi accumsan, tincidunt nisi at, malesuada dolor. Donec consequat metus turpis, eu suscipit libero consequat ac. Praesent malesuada sagittis vestibulum. Aenean gravida in elit et ornare. Pellentesque non bibendum est. Cras augue dolor, euismod ut faucibus at, suscipit eu erat. Suspendisse interdum nisl eget nibh vehicula, et commodo urna rhoncus. Nunc sed faucibus libero.'
+ ,
+ 'Nam et quam dolor. Suspendisse quis ante dapibus, porta est sed, accumsan risus. Etiam faucibus, augue id sollicitudin blandit, nisi nisl feugiat nibh, fringilla pharetra quam eros sed eros.'
+ ,
+ 'Praesent ullamcorper blandit tortor, non sollicitudin erat viverra ultrices. Quisque in vehicula leo. Proin ut urna quis quam vestibulum bibendum ut at odio. Duis ullamcorper leo et aliquet tempor. Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor. Maecenas viverra nunc ligula, id interdum ipsum dapibus quis.'
+ ,
+ 'Vestibulum congue libero nisi, vitae dapibus turpis mollis at. Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in.'
+ ,
+ 'Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus. Duis eget quam sodales, feugiat risus ut, feugiat turpis.'
+ ,
+ 'Quisque sollicitudin risus tellus, eu gravida nisi commodo sit amet. Sed nec libero nec metus auctor mattis eu tristique diam. Aenean metus nisl, blandit nec mollis quis, luctus at nunc.'
+ ,
+ 'Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor. Maecenas viverra nunc ligula, id interdum ipsum dapibus quis. Vestibulum congue libero nisi, vitae dapibus turpis mollis at.'
+ ,
+ 'Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in. Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus. Duis eget quam sodales'
+ ,
+ 'Duis ullamcorper leo et aliquet tempor. Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor.'
+ ,
+ 'Maecenas viverra nunc ligula, id interdum ipsum dapibus quis. Vestibulum congue libero nisi, vitae dapibus turpis mollis at. Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in. Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus.'
+ ,
+ 'Eu suscipit libero consequat ac. Praesent malesuada sagittis vestibulum. Aenean gravida in elit et ornare. Pellentesque non bibendum est. Cras augue dolor, euismod ut faucibus at, suscipit eu erat. Suspendisse interdum nisl eget nibh vehicula, et commodo urna rhoncus. Nunc sed faucibus libero. Nam et quam dolor.'
+ ,
+ 'Suspendisse quis ante dapibus, porta est sed, accumsan risus. Etiam faucibus, augue id sollicitudin blandit, nisi nisl feugiat nibh, fringilla pharetra quam eros sed eros.'
+ ,
+ 'Ut vitae justo eu velit aliquam fringilla in et magna. Ut suscipit varius nunc, ut accumsan massa sollicitudin sed. Suspendisse sit amet egestas libero. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec porta dui ut felis aliquet eleifend a eget metus.'
+ ,
+ 'Nulla et dui ex. Suspendisse potenti. Quisque dictum tristique mi, ac commodo sem ullamcorper eget. Donec sagittis augue sit amet metus aliquet, ac blandit mi convallis. Nulla semper vel erat at sagittis. Nullam eu massa nibh. Integer nec sem consequat arcu cursus condimentum. Duis commodo, enim eget vestibulum aliquam, quam nibh interdum lacus, id feugiat lacus augue at magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras dictum id nunc sit amet efficitur.'
+ ,
+ 'Suspendisse potenti. Ut ullamcorper lectus dui, non dignissim est efficitur quis. Mauris a tempus eros, quis accumsan lacus. Aliquam commodo feugiat pretium. Aenean tincidunt sollicitudin sem sit amet elementum.'
+ ,
+ 'In rutrum, lorem vel vestibulum ultricies, purus lectus varius nibh, sit amet vulputate risus elit et lectus. Quisque lectus tortor, varius sed tincidunt sit amet, cursus non risus.'
+ ,
+ 'Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus a. In pulvinar facilisis ornare.'
+ ,
+ 'Mauris dui metus, lacinia sit amet faucibus ut, vehicula et urna. Vestibulum at sem sed turpis eleifend venenatis. Mauris euismod nisl nibh. Etiam gravida purus mi, varius sollicitudin nunc iaculis a. Nunc egestas venenatis risus, sed interdum velit tincidunt quis. Praesent mattis magna eget condimentum convallis. Nulla urna justo, pulvinar a sagittis nec, feugiat vel ex. Sed quis malesuada lorem.'
+ ,
+ 'Mauris dui metus, lacinia sit amet faucibus ut, vehicula et urna. Vestibulum at sem sed turpis eleifend venenatis. Mauris euismod nisl nibh.'
+ ,
+ 'Etiam gravida purus mi, varius sollicitudin nunc iaculis a. Nunc egestas venenatis risus, sed interdum velit tincidunt quis. Praesent mattis magna eget condimentum convallis. Nulla urna justo, pulvinar a sagittis nec, feugiat vel ex. Sed quis malesuada lorem.'
+ ,
+ 'Quisque lectus arcu, cursus vel pellentesque sit amet, faucibus vitae turpis. Vestibulum mauris augue, lacinia a mollis at, placerat ut eros. Suspendisse gravida tempor lacus, ac tincidunt lectus facilisis ut. Morbi lacinia dui at fermentum viverra.'
+ ,
+ 'Integer dapibus lorem a ligula vestibulum dapibus. Suspendisse potenti. Maecenas tincidunt risus nec volutpat tincidunt. Vivamus tincidunt mauris nec nunc accumsan tristique. Maecenas mattis porttitor malesuada. Aliquam lobortis eu ligula et aliquet.'
+ ,
+ 'Praesent ut leo tristique tellus commodo sagittis nec eget nibh. Ut ut lacus laoreet, semper turpis ac, dictum nulla. In tincidunt ut ipsum in accumsan.'
+ ,
+ 'Nunc ac neque nec sapien pretium tempus. Maecenas consectetur ut libero quis vulputate. Donec consectetur viverra est vel consectetur. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur vel ullamcorper diam.'
+ ,
+ 'Pellentesque posuere dui in metus rutrum, sed accumsan tellus rhoncus. Nam facilisis nibh hendrerit leo condimentum dictum. Maecenas in egestas nisi, at tristique nunc.'
+ ,
+ 'Nulla sit amet venenatis urna. Mauris dictum justo diam, quis aliquet massa accumsan placerat. Vivamus eu dapibus erat. Fusce at pretium enim. Curabitur eget pulvinar lectus. Sed bibendum lectus nisi, eget elementum elit laoreet sed. Donec at velit eget metus posuere tincidunt.'
+ ,
+ '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.'
+ ,
+ 'Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium. 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.'
+ ,
+ 'Suspendisse turpis sapien, cursus non aliquam sed, accumsan sed arcu. Sed pellentesque ipsum metus. Maecenas placerat enim non massa laoreet convallis.'
+ ,
+ 'Etiam quis dolor interdum, condimentum ipsum vitae, ullamcorper nulla. Nulla ornare volutpat sagittis. Morbi at sapien nibh. Curabitur nec quam placerat, placerat elit sit amet, tincidunt neque. Pellentesque nec maximus neque. Aenean maximus quam in sem aliquet, lobortis blandit sem lobortis.'
+ ,
+ 'Aliquam id venenatis tortor, quis sollicitudin turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus suscipit turpis nec consequat euismod. Vivamus eleifend sapien quis mollis scelerisque. Cras et nunc sed turpis dictum porta. Cras vel scelerisque ex, ac volutpat dui. Donec eleifend porta est sit amet fringilla. Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium.'
+ ,
+ 'Aliquam id venenatis tortor, quis sollicitudin turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus suscipit turpis nec consequat euismod. Vivamus eleifend sapien quis mollis scelerisque.'
+ ,
+ 'Cras et nunc sed turpis dictum porta. Cras vel scelerisque ex, ac volutpat dui. Donec eleifend porta est sit amet fringilla. Sed luctus condimentum magna id aliquet.'
+ ,
+ 'Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium.'
+ ,
+ '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.'
+]);
+
+function some_lorem_intro() {
+
+ $lorem = LOREM;
+
+ shuffle($lorem);
+
+ return $lorem[0];
+}
+
+$text = "Nigeria delays plans to replace its banknotes
+Twitter restricted in Turkey, firm says
+2 children died, six others injured after bus crashes into daycare near Montreal
+Lockerbie bombing suspect pleads not guilty
+Internal police documents detail misconduct of officers charged in Tyre Nichols' death
+Big Oil faces scrutiny after huge profit jump
+'You don't belong here': See tense confrontation between Romney and Santos
+Meghan and Harry will be deposed in Samantha Markle's defamation lawsuit: judge
+Why I'm embracing the joy in Sam Smith's kingdom of queer delights
+How companies are trying to help victims of the earthquakes
+TotalEnergies hits pause on new Adani deal
+Australia rejects coal mine near Great Barrier Reef
+Joe Rogan under fire for antisemitism in his defense of Rep. Ilhan Omar
+UK competition regulator warns Microsoft's Activision deal could harm millions
+Google plans to use AI in search results
+Mexico bans 'shark tourism'
+Jim Carrey lists $29M LA mansion while offering a glimpse of his own art
+Morning light from window of airplane wing
+Expert reveals the safest seat on an airplane
+A pest control technician on a routine call recently unraveled
+Massive trove of acorns cleverly stashed in the walls of a California home.
+700 pounds of acorns found inside walls of California home
+'Liar!': Marjorie Taylor Greene interrupts Biden's speech
+Opinion: The terrible diet that unites Nancy Pelosi and Donald Trump
+Kyrie Irving #11 of the Brooklyn Nets dribbles against the Boston Celtics during the first half at TD Garden
+Kyrie Irving's trade causes ripple effect across the NBA
+Earthquake death toll surpasses 11,000
+TOPSHOT - Rescuers and civilians look for survivors under the rubble of collapsed buildings in Kahramanmaras
+Close to the quake's epicentre
+The day after a 7.8-magnitude earthquake struck the country's southeast
+Rescuers in Turkey and Syria braved frigid weather, aftershocks and collapsing buildings
+As they dug for survivors buried by an earthquake that killed more than 5,000 people.
+Some of the heaviest devastation occurred near the quake&#39;s epicentre between Kahramanmaras and Gaziantep
+A city of two million where entire blocks now lie in ruins under gathering snow.
+Aid agencies and emergency workers in Turkey and Syria say the number is likely to increase
+People remain trapped amid freezing conditions
+Why was the earthquake so deadly?
+Hope and despair as rescuers search for survivors in city
+Video shows residents being pulled from earthquake debris
+Sister shields sibling from dust during 36-hour wait for help
+I tried Microsoft's new AI-powered Bing. Here's what it's like
+Google details plans to use AI in search results
+Uber reports 'strongest quarter ever' while the rest of Silicon Valley stalls
+Turkey's stock market halted after earthquake leads to sharp selloff
+Here's what keeps Jerome Powell up at night and interest rates high
+US-China trade defies talk of decoupling to hit record high in 2022
+Two rail unions reach deal with CSX railroad for paid sick time
+Sen. Daines' Twitter account suspended after posting profile picture of himself hunting
+Chinese savers stashed away $2.6 trillion last year but property crash will cool 'revenge spending'
+Bed Bath & Beyond closes stores and raises $1 billion to stave off bankruptcy
+Microsoft unveils revamped Bing search engine using AI technology more powerful than ChatGPT
+Zoom will lay off 1,300 employees and CEO is taking a massive pay cut
+IRS suggests waiting to file your taxes if you got a special payment from your state in 2022
+Want your tax refund faster? Here's what to do
+Offices are more than 50% filled for the first time since the pandemic started
+US Labor Department accuses Amazon of failing to keep warehouse workers safe
+Disney just threw down the gauntlet in the WFH battle
+This former tech worker is helping change laws for people who get laid off
+What to expect at work this year
+Twitter restricted in Turkey, according to network monitoring firm
+I tried Microsoft's new AI-powered Bing. Here's what it's like
+Google details plans to use AI in search results
+Uber reports 'strongest quarter ever' while the rest of Silicon Valley stalls
+Sen. Daines' Twitter account suspended after posting profile picture of himself hunting
+Microsoft unveils revamped Bing search engine using AI technology more powerful than ChatGPT
+Zoom will lay off 1,300 employees and CEO is taking a massive pay cut
+Democratic senator urges Apple and Google to ban TikTok from their app stores
+ChatGPT creator launches subscription service for viral AI chatbot
+Meta shares surge nearly 20% as Zuckerberg pledges to make 2023 a 'year of efficiency'
+Apple and Google's app stores wield 'gatekeeper' power and should be reined in, Commerce Department says
+Apple is the only US tech giant to have avoided significant layoffs. Will it last?
+ChatGPT creator rolls out 'imperfect' tool to help teachers spot potential cheating
+State of the Union, recession, crypto woes and more tech layoffs: What investors are watching
+The CEO of America's second-largest bank is preparing for possible US debt default
+FTX to politicians: Give us back our donations or we'll sue you
+The bear market could make a comeback
+This dwarf planet has a ring instead of a moon, and scientists don't know why
+Codebreakers find and decode lost letters of Mary, Queen of Scots
+Earth core structure. Elements of this image furnished by NASA
+Hidden molten rock layer found beneath Earth's tectonic plates
+Edible crab (Cancer pagurus) on sand covered with algi during low tide.
+Neanderthals had a taste for a seafood delicacy that's still popular today
+Jupiter now has 92 moons after new discovery
+New asteroid photobombs Webb telescope
+Tesla roadster launched from the Falcon Heavy rocket with a dummy driver
+SpaceX put a Tesla sportscar into space five years ago. Where is it now?
+The last full moon of the decade, with a saffron-colored tinge, better known as the full hovers over Los Angeles
+How to see February's full snow moon
+Cosmic seaplanes and self-growing bricks could help us explore other worlds
+Sci-fi ideas that could change the future of space exploration
+Green comet seen from Earth for first time since Stone Age
+A 319-million-year-old brain has been discovered. It could be the oldest of its kind
+Neanderthals hunted massive elephants that once roamed northern Europe
+Watch green comet pass Earth for the first time since Stone Age
+Workers feed cows at a dairy farming company in Handan, Hebei Province, China
+China says it successfully cloned 3 highly productive 'super cows'
+Viking burial mound at Heath Wood being excavated.
+Vikings brought their animals with them to Britain over 1,000 years ago
+Supernova reveals rare pair of stars believed to be one of only about 10 like it in the Milky Way
+Vessels from the embalming workshop
+Discovery of embalming workshop reveals how ancient Egyptians mummified the dead
+Meteor showers bring 'astronomy to us'
+Green comet will appear in the night sky for the first time since the Stone Age
+Mysterious flying whirlpool captured in night sky over Hawaii
+Evolutionary biologist Dolph Schluter is pictured at one of his ponds containing stickleback
+Fish at the University of British Columbia.
+Meet the man who has transformed our understanding of evolution
+The best photos of Mars
+Eastern chimpanzee juvenile male aged 8 years playing with his brother schweinfurtheii)
+Gombe National Park, Tanzania. June 2012.
+Does having a teen feel like living with a chimpanzee?
+You may not be far off, study shows
+CNN Exclusive: Inside the secretive process to select the first astronauts for NASA's next moon mission
+Facing the extremes as an Arctic photographer
+While opening a new window on the origin of more complex molecules that are the first step in the creation of the building blocks of life.
+A large, dark cloud is contained within the frame.
+In its top half it is textured like smoke and has wispy gaps
+At the bottom and at the sides it fades gradually out of view.
+On the left are several orange stars: three each with six large spikes
+Behind the cloud which colours it pale blue and orange.
+Many tiny stars are visible, and the background is black.
+Observing the universe with the James Webb Space Telescope
+The AD-1 oblique wing research aircraft was photographed during a wing sweep test flight.
+The aircraft was flown 79 times during the research program conducted at NASA Dryden between 1979 and 1982.
+Why NASA tested a plane with a pivoting wing
+Newly discovered asteroid makes one of the closest approaches of Earth
+NASA shows rise in near-Earth asteroids
+Professor breaks down why Earth's inner core may have stopped
+Scientists have learned the primates employ gestures that follow some of the same rules intrinsic to human language.
+Experts made the discovery after studying videos of wild chimps living in Uganda
+Humans can understand apes' sign language, new study finds
+Fossils reveal the mysterious primate relatives that lived in the ancient Arctic
+Anna Torv, Bella Ramsey, Pedro Pascal the Last of Us episode 2
+What scientists say about the real-life zombie fungi that inspired 'The Last of Us'
+Rocket Lab&#39;s Electron rocket lifts off at its new pad, called Launch Complex 2
+Rocket Lab launches Electron rocket from the US for the first time
+Artist concept of Demonstration for Rocket to Agile Cislunar Operations (DRACO) spacecraft
+Which will demonstrate a nuclear thermal rocket engine.
+Nuclear thermal propulsion technology could be used for future NASA crewed missions to Mars.
+NASA to test nuclear thermal rocket engine for the first time in 50 years
+The Doomsday Clock reveals how close we are to total annihilation
+Mummified 'golden boy' found covered in 49 precious amulets
+Rare 17-pound meteorite discovered in Antarctica
+Webb telescope peers into the frozen heart of a space cloud
+Trilobites armed with tridents could be the earliest known example of sexual combat
+Like a house constantly under renovation, we are ever-changing and replacing old parts with new ones
+Water, proteins and even cells.
+We are all made of stars: The long trip from the big bang to the human body
+Discovery of 250 eggs suggests that giant dinosaurs weren&#39;t caring parents
+Discovery in India reveals intimate details about lives of some of the largest dinosaurs
+South Korea's lunar probe captures stunning Earth, moon images
+Video: UFO-shaped cloud stuns eyewitnesses in Turkey
+Origins of plague could have emerged centuries before outbreaks, new study suggests
+After a historic first mission, what does the future hold for this controversial rocket?
+Car-size laser deflects lightning atop a mountain in Switzerland
+Astronomers have released a gargantuan survey of the galactic plane of the Milky Way.
+The new dataset contains a staggering 3.32 billion celestial objects — arguably the largest such catalog so far.
+Billions of celestial objects captured by new survey of the Milky Way
+A whale belonging to one of the rarest species is 'likely to die,' after entanglement, NOAA says
+One of the 92 fossilized dinosaur nests discovered at a site in central India.
+Unusual dinosaur fossil discovery made in India
+A SpaceX Falcon 9 launches a GPS III satellite to orbit January 18, 2023
+SpaceX launches next-generation GPS satellite
+Short-beaked Echidna Tachyglossus aculeatus. (Photo by: Avalon/Universal Images Group via Getty Images)
+This egg-laying mammal blows bubbles to cool off
+Bronze Age family harvesting grain, as depicted by artist Nikola Nevenov.
+In parts of Ancient Greece, first-cousin marriage was not only allowed but encouraged, DNA shows
+Tiny, foot-long dwarf boa identified in Ecuador is new to science
+Foot-long dwarf boa found in Ecuadorian Amazon
+The SpaceX Falcon Heavy rocket is launched on classified mission USSF-67 for the U.S. Space Force at Cape Canaveral, Florida, U.S. January 15, 2023. REUTERS/Joe Skipper
+SpaceX's most powerful rocket returns to flight and nails synchronized landing
+Illustration reflects the conclusion that the exoplanet LHS 475
+Rocky and almost precisely the same size as Earth.
+The planet whips around its star in just two days, far faster than any planet in the Solar System.
+Researchers will follow up this summer with additional observations with Webb
+LHS 475 b is relatively close, 41 light-years away, in the constellation Octans.
+Illustration of a planet and its star on a black background.
+The planet is large, in the foreground at the centre, and the star is smaller
+The planet is rocky. The top quarter of the planet (the side facing the star) is lit, while the rest is in shadow.
+The star is bright yellowish-white, with no clear features.
+2 Earth-size worlds revealed beyond our solar system
+Scientists have taken a fresh look at the fossil flower, which was first documented in 1872 and then largely forgotten about.
+Unusually large fossilized flower preserved in amber identified
+An artistic rending of the star Gaia17bpp being partially eclipsed by the dust cloud surrounding a smaller companion star.
+Unusually brightening star captures attention as a stellar oddity
+Illustration reflects the conclusion that the exoplanet LHS 475 b is rocky and almost precisely the same size as Earth.
+The planet whips around its star in just two days, far faster than any planet in the Solar System.
+Researchers will follow up, they hope will allow them to definitively conclude if the planet has an atmosphere.
+LHS 475 b is relatively close, 41 light-years away, in the constellation Octans.
+The planet is large, in the foreground at the centre, and the star is smaller, in the background and also at the centre.
+The planet is rocky.
+The top quarter of the planet (the side facing the star) is lit, while the rest is in shadow.
+The star is bright yellowish-white, with no clear features.
+James Webb Space Telescope discovers its first exoplanet
+Alken Enge (Denmark) -- at this exceptional site the remains of at least 380 individuals
+Victims of an armed conflict, were deposited almost 2000 years ago.
+Brutality of prehistoric life revealed by Europe's bog bodies
+The Soyuz MS-22 crew ship is pictured on Oct. 8, 2022
+Foreground docked to the Rassvet module as the International Space Station orbited 264 miles above Europe.
+Background, is the Prichal docking module attached to the Nauka multipurpose laboratory module.
+Roscosmos will send replacement spacecraft to return crew to Earth after Soyuz leak
+Wonders of the universe
+Rocket start-up fails attempt to launch satellites off Alaska's coast
+Newly discovered Earth-size planet TOI 700 e orbits within the habitable zone of its star in this illustration.
+Its Earth-size sibling, TOI 700 d, can be seen in the distance.
+Second potentially habitable Earth-size planet found orbiting nearby star
+A galactic merger brought a pair of supermassive black holes together
+A green comet will appear in the night sky for the first time in 50,000 years
+Cosmic Girl 747 sits on the runway surrounded by technical service equipment
+Virgin Orbit's LauncherOne rocket suffers failure on first launch attempt from the UK
+Earth Radiation Budget Satellite (ERBS)
+Reentered Earth'ss atmosphere at
+Dead NASA satellite returns to Earth after 38 years
+View of the Pantheon in the historic centre of Rome
+Mystery of why Roman buildings have survived so long has been unraveled, scientists say
+Why ancient Roman structures like the Pantheon still stand
+'Astonishing' snowy owl spotted in Southern California neighborhood
+People stand beside St Michaels Tower as they watch the full moon, sometimes known as Wolf Moon
+Rise behind Glastonbury Tor in Glastonbury, Britain
+What to expect from tonight's wolf moon
+New space missions will launch to the moon, Jupiter and a metal world in 2023
+Homo heidelbergensis pair wearing cave bear skins for protection from the cold.
+Stone Age humans stepped out in cave bear fur 300,000 years ago
+New image of the Serpens constellation glitters with starlight
+Walt Cunningham adjusts his pressure suit before the Apollo 7 launch on October 11, 1968.
+Last surviving Apollo 7 astronaut has died
+Look up to see January's first celestial event, the Quadrantid meteor shower
+All of the moments and discoveries that provided us with wonder in 2022
+In this 30 second exposure, a meteor streaks across the sky during the annual Perseid meteor shower
+Keep an eye on the sky for 2023's celestial events
+Cirrhilabrus finifenmaa
+Meet a rainbow fish and other new species discovered in 2022
+NASA images showcase eerie beauty of winter on Mars
+Close up photograph of the mammal foot among the ribs of Microraptor
+These may be used in any article in association with this story.
+Rare evidence that dinosaurs feasted on mammals uncovered
+A 15-metric ton meteorite crashed in Africa. Now 2 new minerals have been found in it
+Astronauts complete spacewalk after space debris triggered one-day delay
+The European robotic arm controlled by cosmonaut Anna Kikina surveys the Soyuz MS-22
+Crew ship after the detection of a leak that cancelled the spacewalk
+NASA and Russia weigh options for astronaut return after spacecraft leak
+Sea turtle amputee rescued from net entanglement finds forever home
+Groundbreaking Mars mission comes to an end
+Milky Way over Hawes in the Yorkshire Dales National Park, UK. Meteor from Ursids Meteor shower visible.
+Faint aurora visible on the horizon as a red tinge.
+Near new moon creates perfect viewing conditions for the Ursids meteor shower
+Artist&#39;s life reconstruction of adult and newly born Triassic ichthyosaurs Shonisaurus, 2022.
+Paleontologists solve mystery of fossil death bed
+Doomed exoplanet will be obliterated as it spirals into a star
+The Perseverance rover is about to have a big first on Mars
+Perseverance rover is about to build a first-of-its-kind depot on Mars
+'Game changer' satellite will measure most of the water on the planet
+A diplodocus skeleton at the Carnegie Museum of Natural History in Pittsburgh.
+Could these dinosaurs whip their tails faster than the speed of sound?
+Soyuz spacecraft docked to International Space Station springs 'fairly significant' coolant leak
+'Rail cars' of material released after NASA spacecraft hit asteroid
+Juno mission captured this infrared view of Jupiters volcanic moon
+The most volcanic world in the solar system is about to be visited by a NASA spacecraft
+Dazzling galactic diamonds shine in new Webb telescope image
+A bright meteor can be seen during the Geminids meteor shower
+Massive Martian dust devil passed over the Perseverance rover, and it recorded the eerie sounds
+The shipwreck was discovered at a depth of about 1,350 feet (411 meters) and was captured in sonar imagery.
+Researchers aim to return next year with an ROV to capture footage of the wreck.
+Medieval ship found in Norway's biggest lake
+Historic moon mission ends with splashdown of Orion capsule
+Watch NASA's Orion spacecraft splashdown in Pacific Ocean
+Historic moon mission concludes with splashdown of Orion capsule
+A two million- year-old trunk from a larch tree still stuck in the permafrost within the coastal deposits.
+The tree was carried to the sea by the rivers that eroded the former forested landscape.
+A lost ecosystem revealed in Greenland by oldest environmental DNA
+Ankylosaurs used their sledgehammer tails to fight each other
+Japanese fashion mogul Yusaku Maezawa is seen in Tokyo on January 07, 2022.
+Full crew for SpaceX's privately funded moon mission announced
+A year only lasts 17.5 hours on the 'hell planet'
+This artists impression shows a kilonova produced by two colliding neutron stars.
+While studying the aftermath of a long gamma-ray burst (GRB)
+Two independent teams of astronomers using a host of telescopes in space and on Earth
+Have uncovered the unexpected hallmarks of a kilonova
+The colossal explosion triggered by colliding neutron stars.
+Rare cosmic collision acted like one of the 'factories of gold' in the universe
+The last full moon of the year known as the Cold Moon rises behind Galata tower in Ista
+Full 'cold moon' shines bright and eclipses Mars in a rare event
+Ingenuity Mars Helicopter completed a successful Flight 31 on September
+NASA Ingenuity helicopter just broke one of its own records on Mars
+Artist_s reconstruction of Kap København Formation two million years ago
+Oldest DNA sheds light on a 2 million-year-old ecosystem that has no modern parallel
+Stunning necklace found at burial site of powerful Anglo-Saxon woman
+Necklace reconstruction and layout side by side.
+The Harpole Treasure in photos
+The American lion, otherwise known as Pathera atrox
+The largest extinct cat to live in North America
+Rare ice age fossils discovered on the drought-stricken Mississippi River
+Dinosaur larger than T. rex couldn't swim well despite large fish being on the menu
+San Diego Zoo Wildlife Alliance
+DNA analysis of soil from paw prints could help save Sumatra's tigers
+See photos from the Apollo era like never before
+NASA astronaut and Expedition 68 Flight Engineer
+Frank Rubio
+Pictured during a spacewalk
+Tethered to the International Space Station&#39;s starboard truss structure.
+Astronauts will give the space station a power boost during Saturday spacewalk
+NASA's Viking 1 may have landed at the site of an ancient Martian megatsunami
+Toxoplasmosis, an infection caused by the T. gondii parasite
+Best known in cats but it might also be affecting wolf behavior.
+'Mind control' by parasites influences wolf-pack dynamics in Yellowstone National Park
+NASA's historic moon mission enters the final leg of its journey
+Webb telescope spies clouds beneath the thick haze of Saturn's moon Titan
+Original slate plaque modelled after an owl in the Museo de Huelva.
+Replica of the Valencina Slate Plaque with inserted owl feathers
+Two drilled holes at the top of the plaque.
+Popular toy of prehistoric children revealed by new research
+Rare cosmic event beamed light at Earth from 8.5 billion light-years away
+VP Harris, French President Macron see Webb telescope's latest chaotic image
+The manned spaceship Shenzhou-15, atop the Long March-2F Y15 carrier rocket
+Blasts off from the Jiuquan Satellite Launch Center
+New era begins with China's launch of crewed mission to its space station
+A Daubenton&#39;s bat (Myotis daubentonii) in flight and hunting at night.
+Bats use the same techniques as death metal singers to vocalize, study finds
+The bone fragments of a new giant turtle species
+Have scientists estimating a 3.7-meter-long body (12.1 feet), larger than the size of a car.
+Ancient giant sea turtle with never-before-seen features found in Europe, scientists say
+NASA's Orion spacecraft reaches record-breaking distance from Earth on Artemis I mission
+'It's getting dark': 'Good Night Oppy' recounts the sudden death of a Mars rover
+Newly identified dinosaur that lived on island of dwarfed creatures had an unusual head
+SpaceX launches tomato seeds, other supplies to International Space Station
+Kimberella fossil
+Discovered Russia in 2018, shows evidence of the earliest known animal meal
+Scientists said.
+World's 'oldest meal' discovered in 550-million-year-old fossil
+NASA's Orion spacecraft snaps a selfie on its journey beyond the far side of the moon
+This illustration shows what exoplanet WASP-39 b could look like
+Based on current understanding of the planet.
+New data on 'hot Saturn' exoplanet is a 'game changer,' scientists say
+Angler Andy Hackett is celebrating after catching one of the worlds biggest goldfish.
+The gigantic orange specimen, aptly nicknamed The Carrot, weighed a whopping 67lbs 4ozs.
+Fisherman catches 67-pound goldfish
+Great bustards eat corn poppies for their medicinal properties.
+World's heaviest flying bird uses plants to self-medicate, scientists say
+A view of Shanidar Cave in northern Iraq.
+Neanderthals cooked meals with pulses 70,000 years ago
+Dwarf tomato seeds to launch to space station aboard SpaceX resupply flight
+Sseen from Harbor town Marina on Merritt Island, Fla.
+The moon is visible in the sky.
+Malcolm Denemark/Florida Today via AP
+Historic Artemis I mission is just beginning its lunar journey
+NASA still won't rename the James Webb Space Telescope, citing new investigation into namesake's career
+Mystery parasites on zombie ant fungus identified by scientists
+Sutherland and Fiona, mother-daughter chimpanzees
+Part of the Ngogo chimpanzee community
+The Kibale National Park in Uganda.
+Scientists captured footage of Fiona holding a leaf out to her mother with no clear motive behind it.
+Chimpanzees share experiences with each other, a trait once thought to be only human
+Early observations show the LOFTID demonstration was successful.
+In addition to achieving its primary objective of surviving the intense speed and heating of re-entry
+The aft side of the heat shield was well protected from the heat of re-entry.
+Inflatable aeroshells can keep payloads safe during atmospheric entry.
+Inflatable heat shield a 'huge success' that could land humans on Mars
+Webb telescope finds two of the most distant galaxies ever observed
+A Leonids meteor streaks across the sky over Ankara
+Leonid meteor shower could bring an outburst of up to 250 meteors per hour
+The size of a basketball, this meteorite landed in southeastern England in February 2021.
+Where did Earth's water come from? This meteorite might hold the answer
+Artemis I mission shares spectacular view of Earth after a historic launch
+Cosmic hourglass captured by the James Webb Space Telescope reveals birth of a star
+Space Launch System rocket is seen at Kennedy Space Center
+Historic moon mission troubleshoots fuel leak ahead of launch
+First-time NASA spacewalkers ventured outside the space station Tuesday
+Meet Commander Moonikin Campos, the mannequin going farther than any astronaut
+Why NASA is returning to the moon 50 years later with Artemis I
+Why NASA wants to return to the moon before sending humans to Mars
+Teams from Johnson Space Center, Exploration Ground Systems, and Jacobs TOSC conduct
+Final inspections of Mooniki
+The inside the Space Station Processing Facility at NASA
+Kennedy Space Center in Florida. Moonikin will be installed into the Orion crew module.
+Checked connectivity and performed fit checks on his flight suit
+Ensure he is ready for flight aboard the Artemis flight test.
+Artemis I will be an uncrewed test flight
+The Orion spacecraft and Space Launch System rocket as an integrated system
+Meet Commander Moonikin Campos, the mannequin going farther than any astronaut
+Snoopy, mannequins and Apollo 11 items will swing by the moon aboard Artemis I
+Rocket with the Orion spacecraft aboard is seen at sunrise atop
+Launch team conducts the wet dress rehearsal test at NASA
+What the words you'll hear during the moon mission launch really mean
+Clues at ancient lake site reveal earliest known cooked meal
+The biggest wild card in the climate crisis
+Belching lakes, mystery craters, 'zombie fires'
+How the climate crisis is transforming the Arctic permafrost
+The NASA Moon rocket makes its way from the Vehicle Assembly
+Building headed to Pad 39B
+The Artemis I mission for Monday
+NASA still targets Artemis I launch next week despite minor hurricane damage
+Space Shuttle Challenger remnants discovered underwater by documentary crew
+A bright fireball is seen above Brkini, Slovenia
+Northern Taurid fireballs can be seen all November long
+In close relations to the fungi fairy rings
+There are no fairies at play here.
+One of nature's great mysteries may now have an answer
+Two separate missions launched aboard a United Launch Alliance
+Atlas V rocket from Space Launch
+Complex-3 at Vandenberg Space Force Base in Lompoc
+California on Thursday morning.
+Heat shield that could land humans on Mars is hitching a ride to space
+A portion of the dwarf galaxy Wolf
+Infrared Array Camera (left) and the James Webb Space Telescope
+New Webb telescope image shows 'lonely' dwarf galaxy in striking detail
+International Space Station Cygnus spacecraft. (NASA)
+Cargo spacecraft docks with ISS after solar panel fails to deploy
+Bronze Age comb reveals an ancient frustration with head lice
+Astronomers spy the ghost of a star and cosmic cobwebs
+A space rock slammed into Mars on Christmas Eve. It revealed a hidden surprise
+Antarctic emperor penguins
+Antarctica's emperor penguins at risk of extinction due to the climate crisis
+Over 200 different species of rhododendrons can be found high up in the Hengduan mountains
+Known for its biological diversity
+Estimated 12,000 species of flowers grow on the mountains.
+Evolutionary mystery of harmonious flower meadow may be solved
+Ghostly figures emerge from Pillars of Creation in new Webb telescope image
+Microbes may have survived for millions of years beneath the Martian surface
+One of The rare Aye-Aye lemur that is only nocturnal
+This weird-looking primate's extra-long fingers give it an extra-gross talent
+Webb telescope shares unique peek inside the early universe
+ed dwarfs tend to be magnetically active, and erupt with intense
+Strip a nearby planets atmosphere over time, or make the surface inhospitable.
+The hunt for habitable planets may have just gotten far more narrow, new study finds
+The International Space Station (ISS)
+Roscosmos cosmonaut Pyotr Dubrov from the Soyuz MS-19 spacecraft
+International Space Station swerves to avoid Russian space debris
+The last solar eclipse of the year can be seen today
+Rare 300-foot whaleback boat discovered at the bottom of Lake Superior
+From Comet Halley, Orionid meteor shower is most visible this week
+James Webb Space Telescope captures new details of iconic 'Pillars of Creation'
+Mosquito insect sitting on skin
+Are you a mosquito magnet? This could be why
+A reconstruction of a Neanderthal father and his daughter.
+Ancient DNA reveals first Neanderthal family portrait
+The Black Death is still affecting the human immune system
+The heaviest bony fish in the world is a giant sunfish of 2744 kg
+Discovered in Portugal.
+Record-breaking bony fish weighing 3 tons found
+Torn deck plating of the V 1302 John Mahn that was damaged by the bomb that hit amidships
+World War II shipwreck still pollutes the North Sea's ocean floor 80 years later
+Your cat might also be secretly signaling
+Affection in the way they look at you, writes Emily Blackwell.
+Want to know if your cat loves you? Look out for these signs
+Astronaut James A. McDivitt after successful space flight.
+Astronaut James McDivitt, who led Gemini and Apollo missions, has died at 93
+Bright, powerful burst of gamma rays detected by multiple telescopes
+Elon Musk reverses course, says SpaceX will keep funding Ukraine Starlink service for free
+The 17th litter of cheetah cubs were born at the Smithsonian
+National Zoo and Conservation Biology Institute and can be watched live on their Cheetah Cub Cam.
+Cub cam offers a unique glimpse into the early days of a vulnerable species
+Here's what keeps Jerome Powell up at night and interest rates high
+Disney has bigger problems than Ron DeSantis
+'Britcoin' could arrive in the second half of the decade
+State of the Union, recession, crypto woes and more tech layoffs: What investors are watching
+The CEO of America's second-largest bank is preparing for possible US debt default
+FTX to politicians: Give us back our donations or we'll sue you
+The bear market could make a comeback
+Viva Technologie show at Parc des Expositions Porte de Versailles
+The new international event brings together 5,000 startups
+Companies to grow businesses
+All players in the digital transformation who shape the future of the internet
+A conversation about the post-pandemic workplace with Microsoft CEO Satya Nadella.
+RSVP HERE
+Employee mental health is a huge concern after such a brutal stretch
+How managers really feel about remote work
+Signs an employee may quit and what managers can do to prevent it
+closeup of a young man in an office holding a briefcase and a surgical mask in his hand
+Managers, buckle up. Your job will be harder than ever as we return to the office
+Nervous about socializing in the office again? Here's how to break the ice
+The millennials in sexless marriages
+How we work
+The entry-level workers earning six-figure salaries
+Does 'solo polyamory' mean having it all?
+Family Tree
+The parents who sever ties with their children
+The rising curiosity behind open relationships
+‘Situationships’: Why Gen Z are embracing the grey area
+The jobs employers just can't fill
+Why workers and employers are ghosting each other
+Future Planet
+The enormous heat pumps warming cities
+Montessori: The world's most influential school?
+Should you sing when suffering from a cold?
+The Health Gap
+Why aren't there better treatments for cystitis?
+Get the Facts
+Leprosy: the ancient disease scientists can't solve
+How an ancient Greek myth still shapes our minds
+Wise Words
+Why we need new words for life in the Anthropocene
+Microbes and Me
+Mike loses magic in 'tepid' sequel
+Cinema's ultimate scene-stealers
+EO thumbnail
+Knock at the Cabin is 'passably tense'
+The 1960s crime film that still shocks
+Including the third Ant-Man and Magic Mike films, Cocaine Bear and a Pamela Anderson documentary in which she tells her own story
+Nicholas Barber lists this month's unmissable releases.
+How gut bacteria are controlling your brain
+Bird flu outlook is ‘grim’ as new wave of the virus heads for Britain
+Warning comes as ornithologists call for the government
+Step up testing to monitor the impact of the deadly H5N1 strain
+Bird flu outlook is ‘grim’ as new wave of the virus heads for Britain
+Energy prices to soar again as Jeremy Hunt rejects pleas to halt rise
+Millions will see costs mount by another 40% in April
+Rebate scheme ends and chancellor lets cap go up to £3,000
+Energy prices to soar again as Jeremy Hunt rejects pleas to halt rise
+Disabled rail passengers face restrictions at one in 10 stations
+Stress led to more NHS staff absences than Covid, new figures show
+‘It’s soul destroying’: why so many NHS staff are off sick with burnout
+Stop UK mobile and broadband firms ‘lining their pockets’, urge consumer experts
+Care worker whistleblower outed by Home Office over exploitation claims
+Same-sex marriage row looms over Church of England synod
+Dominic Raab: more civil servants in bullying complaint than previously thought
+Nurses offer to call off strikes if Sunak matches Welsh pay offer
+What will it take to stop the rape and murder of women by men on probation?
+Debt, bad; work, good: ‘pub bore’ beliefs that seal a miserable fate for the poorest
+Who’s going to be triggered by Northanger Abbey? It’s hardly Game of Thrones
+The Observer view on the shameful premium that the poorest pay for their energy
+It’s so easy to cheat with technology that even judges are doing it
+Knock, knock… the energy companies are at the door – cartoon
+A lowly sergeant, but Happy Valley’s Catherine Cawood is top of the cops
+May I have a word about… rummaging around on the Antiques Roadshow
+Chelsea’s gamble on young guns looks like another shot in the dark by US owners
+Restless Pep’s lean and mean Manchester City go in search of tactical truth at Spurs
+England 23-29 Scotland: Six Nations player ratings from Twickenham
+Paquetá responds to Newcastle’s fast start and salvages point for West Ham
+Scotland’s Duhan van der Merwe stuns England to settle Calcutta Cup thriller
+Wales’ Alun Wyn Jones to miss Scotland clash as Ireland turn focus to France
+Championship roundup: Burnley beat Norwich, Birmingham stun Swansea
+Southampton fans turn on Nathan Jones as Brentford cruise to victory
+Jared O’Mara: ex-Labour MP found guilty of six counts of expenses fraud
+Five arrested after video circulates of attack on Surrey schoolgirl
+Brexit Northern Ireland protocol is lawful, supreme court rules
+Archie Battersbee’s death was an accident, coroner concludes
+Meet Jinx, the dog on a mission to protect Welsh bird colonies from rats
+Mary, Queen of Scots prison letters finally decoded
+David Carrick ‘no longer the big man’ after life sentences, says victim
+Mother of UK woman missing with newborn baby issues open letter
+Prevent review condemned for ‘anti-Muslim prejudice
+Surge in young people declaring disability in England and Wales
+Russia-Ukraine war live
+Sunak says ‘nothing off the table’ after Zelenskiy’s plea for fighter jets
+Volodymyr Zelenskiy makes appeal for jets in address to British parliament
+MH17: strong indications Putin signed off on supplying missile that hit plane
+Before and after satellite images show scale of earthquake destruction in Turkey
+Turkey and Syria earthquake death toll nears 12,000 as Erdoğan defends response
+Ukraine releases video appearing to show Russian troops beating own wounded officer
+Split loyalties: coming of age inside Putin's Russia – video
+Canada’s Justin Trudeau greets political opponent with awkward handshake
+How Volodymyr Zelenskiy spent his day in Britain
+Greek PM survives confidence vote but phone-tapping scandal rumbles on
+Greek government faces confidence vote over spying row
+Terrawatch: Santorini watchful as nearby volcano is monitored
+Britain treasures the Parthenon marbles
+Consider this: returned to Greece, could they be more valuable?
+Ex-MEP at heart of cash for influence scandal strikes plea bargain
+Funeral of Greece’s last king, Constantine II, takes place in Athens
+The Guardian view on European migration policy: a cruel, myopic shambles
+Greek court drops spying charges against refugee rescue activists
+Long-awaited trial of 24 aid workers accused of espionage starts in Lesbos
+Caroline Crouch killed by Greek husband because of his drug smuggling, father claims
+Stephen Fry calls for return of Parthenon marbles to Athens
+British Museum in talks with Greece over return of Parthenon marbles
+Greek MEP at centre of Qatar corruption inquiry has hearing postponed
+Pope Francis orders Parthenon marbles held by Vatican be returned to Greece
+A police stakeout, piles of cash, and a promise of reform: the week that shook Brussels
+Former European parliament vice-president Eva Kaili was arrested along with her boyfriend, Francesco Giorgi
+Greece: thousands march after death of Roma boy shot in police chase
+Greek MEP stripped of EU vice-president role amid Qatar scandal
+Police search European parliament offices as bribery inquiry grows
+Afghan refugee freed in Greece after two years of wrongful imprisonment
+Greece passes intelligence bill banning the sale of spyware
+Migrants face ‘unprecedented rise in violence’ in EU borders, report finds
+The fixed-price shopping basket: Greece’s answer to cost of living crisis
+Romany leaders appeal for calm after second day of protest violence in Greece
+Thousands take to streets in Greece in protest over 2008 shooting of teenager
+Violent protests in Greece after Romany boy shot by police
+No 10 rules out law change for return of Parthenon marbles
+Weather tracker: Storm Ariel brings heavy rain and lightning to Greece
+‘Destitution is almost inevitable’: Afghan refugees in Greece left homeless by failed system
+‘Red lights are flashing’: Athens tourism explosion threatens ancient sites
+Smyrna review – raw, shocking violence in epic take on Greek-Turkish conflict
+What happens when an oligarch takes on a prime minister? Look to Greece to find out
+Greek court acquits activists who hung banner at Acropolis in China protest
+US and Israel blame Iran after drone strikes oil tanker off Oman
+Man repatriates 19 antiquities after reading Guardian article";
+
+$titles = explode("\n", $text);
+
+// print_r($titles); die();
+
+
+include 'pythia.php';
+
+
+$posts = $wiki->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 @@
+<?php
+
+define("LOREM", [
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla sit amet venenatis urna. Mauris dictum justo diam, quis aliquet massa accumsan placerat. Vivamus eu dapibus erat. Fusce at pretium enim. Curabitur eget pulvinar lectus. Sed bibendum lectus nisi, eget elementum elit laoreet sed. Curabitur pretium dapibus magna sed rhoncus. Donec at velit eget metus posuere tincidunt. 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.'
+ ,
+ 'Fusce libero lorem, tristique id nunc vel, pellentesque consectetur lacus. Aenean elit elit, euismod at neque ac, dapibus efficitur lacus. Sed fermentum vehicula luctus. Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium. 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. Integer augue felis, ullamcorper in volutpat et, maximus eget erat.'
+ ,
+ 'Suspendisse turpis sapien, cursus non aliquam sed, accumsan sed arcu. Sed pellentesque ipsum metus. Maecenas placerat enim non massa laoreet convallis. Etiam quis dolor interdum, condimentum ipsum vitae, ullamcorper nulla. Nulla ornare volutpat sagittis. Morbi at sapien nibh. Curabitur nec quam placerat, placerat elit sit amet, tincidunt neque. Pellentesque nec maximus neque. Aenean maximus quam in sem aliquet, lobortis blandit sem lobortis. Aliquam id venenatis tortor, quis sollicitudin turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus suscipit turpis nec consequat euismod. Vivamus eleifend sapien quis mollis scelerisque. Cras et nunc sed turpis dictum porta. Cras vel scelerisque ex, ac volutpat dui. Donec eleifend porta est sit amet fringilla.'
+ ,
+ 'Nunc sed purus sed nibh lobortis luctus a vitae ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus.'
+ ,
+ 'Phasellus consequat purus eu porta sagittis. In hac habitasse platea dictumst. Maecenas lorem diam, scelerisque non neque euismod, lobortis congue massa. Nunc sed purus sed nibh lobortis luctus a vitae ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus. In pulvinar facilisis ornare. Etiam sit amet nisi accumsan, tincidunt nisi at, malesuada dolor. Donec consequat metus turpis, eu suscipit libero consequat ac. Praesent malesuada sagittis vestibulum. Aenean gravida in elit et ornare. Pellentesque non bibendum est. Cras augue dolor, euismod ut faucibus at, suscipit eu erat. Suspendisse interdum nisl eget nibh vehicula, et commodo urna rhoncus. Nunc sed faucibus libero. Nam et quam dolor. Suspendisse quis ante dapibus, porta est sed, accumsan risus. Etiam faucibus, augue id sollicitudin blandit, nisi nisl feugiat nibh, fringilla pharetra quam eros sed eros.'
+ ,
+ 'Praesent ullamcorper blandit tortor, non sollicitudin erat viverra ultrices. Quisque in vehicula leo. Proin ut urna quis quam vestibulum bibendum ut at odio. Duis ullamcorper leo et aliquet tempor. Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor. Maecenas viverra nunc ligula, id interdum ipsum dapibus quis. Vestibulum congue libero nisi, vitae dapibus turpis mollis at. Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in. Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus. Duis eget quam sodales, feugiat risus ut, feugiat turpis. Quisque sollicitudin risus tellus, eu gravida nisi commodo sit amet. Sed nec libero nec metus auctor mattis eu tristique diam. Aenean metus nisl, blandit nec mollis quis, luctus at nunc.'
+ ,
+ 'Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor. Maecenas viverra nunc ligula, id interdum ipsum dapibus quis. Vestibulum congue libero nisi, vitae dapibus turpis mollis at. Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in. Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus. Duis eget quam sodales'
+ ,
+ 'Duis ullamcorper leo et aliquet tempor. Pellentesque id orci aliquet, condimentum sapien nec, porttitor felis. Mauris fringilla tellus eu hendrerit tempor. Maecenas viverra nunc ligula, id interdum ipsum dapibus quis. Vestibulum congue libero nisi, vitae dapibus turpis mollis at. Vestibulum ante urna, consequat quis efficitur non, iaculis at metus. Curabitur sagittis turpis sed odio feugiat, rhoncus posuere neque rhoncus. Nunc dignissim commodo tortor, in lacinia nisi imperdiet in. Suspendisse cursus, metus pharetra faucibus mattis, orci leo efficitur enim, vel malesuada dui ante vitae lacus.'
+ ,
+ 'Eu suscipit libero consequat ac. Praesent malesuada sagittis vestibulum. Aenean gravida in elit et ornare. Pellentesque non bibendum est. Cras augue dolor, euismod ut faucibus at, suscipit eu erat. Suspendisse interdum nisl eget nibh vehicula, et commodo urna rhoncus. Nunc sed faucibus libero. Nam et quam dolor. Suspendisse quis ante dapibus, porta est sed, accumsan risus. Etiam faucibus, augue id sollicitudin blandit, nisi nisl feugiat nibh, fringilla pharetra quam eros sed eros.'
+ ,
+ 'Ut vitae justo eu velit aliquam fringilla in et magna. Ut suscipit varius nunc, ut accumsan massa sollicitudin sed. Suspendisse sit amet egestas libero. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec porta dui ut felis aliquet eleifend a eget metus. Nulla et dui ex. Suspendisse potenti. Quisque dictum tristique mi, ac commodo sem ullamcorper eget. Donec sagittis augue sit amet metus aliquet, ac blandit mi convallis. Nulla semper vel erat at sagittis. Nullam eu massa nibh. Integer nec sem consequat arcu cursus condimentum. Duis commodo, enim eget vestibulum aliquam, quam nibh interdum lacus, id feugiat lacus augue at magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras dictum id nunc sit amet efficitur.'
+ ,
+ 'Suspendisse potenti. Ut ullamcorper lectus dui, non dignissim est efficitur quis. Mauris a tempus eros, quis accumsan lacus. Aliquam commodo feugiat pretium. Aenean tincidunt sollicitudin sem sit amet elementum. In rutrum, lorem vel vestibulum ultricies, purus lectus varius nibh, sit amet vulputate risus elit et lectus. Quisque lectus tortor, varius sed tincidunt sit amet, cursus non risus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ac erat accumsan, faucibus lorem et, euismod ex. Fusce cursus nulla lacus, ac varius neque tempus a. In pulvinar facilisis ornare.'
+ ,
+ 'Mauris dui metus, lacinia sit amet faucibus ut, vehicula et urna. Vestibulum at sem sed turpis eleifend venenatis. Mauris euismod nisl nibh. Etiam gravida purus mi, varius sollicitudin nunc iaculis a. Nunc egestas venenatis risus, sed interdum velit tincidunt quis. Praesent mattis magna eget condimentum convallis. Nulla urna justo, pulvinar a sagittis nec, feugiat vel ex. Sed quis malesuada lorem.'
+ ,
+ 'Quisque lectus arcu, cursus vel pellentesque sit amet, faucibus vitae turpis. Vestibulum mauris augue, lacinia a mollis at, placerat ut eros. Suspendisse gravida tempor lacus, ac tincidunt lectus facilisis ut. Morbi lacinia dui at fermentum viverra. Integer dapibus lorem a ligula vestibulum dapibus. Suspendisse potenti. Maecenas tincidunt risus nec volutpat tincidunt. Vivamus tincidunt mauris nec nunc accumsan tristique. Maecenas mattis porttitor malesuada. Aliquam lobortis eu ligula et aliquet. Praesent ut leo tristique tellus commodo sagittis nec eget nibh. Ut ut lacus laoreet, semper turpis ac, dictum nulla. In tincidunt ut ipsum in accumsan. Nunc ac neque nec sapien pretium tempus. Maecenas consectetur ut libero quis vulputate. Donec consectetur viverra est vel consectetur. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur vel ullamcorper diam. Pellentesque posuere dui in metus rutrum, sed accumsan tellus rhoncus. Nam facilisis nibh hendrerit leo condimentum dictum. Maecenas in egestas nisi, at tristique nunc.'
+ ,
+ 'Nulla sit amet venenatis urna. Mauris dictum justo diam, quis aliquet massa accumsan placerat. Vivamus eu dapibus erat. Fusce at pretium enim. Curabitur eget pulvinar lectus. Sed bibendum lectus nisi, eget elementum elit laoreet sed. Donec at velit eget metus posuere tincidunt. 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.'
+ ,
+ 'Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium. 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.'
+ ,
+ 'Suspendisse turpis sapien, cursus non aliquam sed, accumsan sed arcu. Sed pellentesque ipsum metus. Maecenas placerat enim non massa laoreet convallis. Etiam quis dolor interdum, condimentum ipsum vitae, ullamcorper nulla. Nulla ornare volutpat sagittis. Morbi at sapien nibh. Curabitur nec quam placerat, placerat elit sit amet, tincidunt neque. Pellentesque nec maximus neque. Aenean maximus quam in sem aliquet, lobortis blandit sem lobortis. Aliquam id venenatis tortor, quis sollicitudin turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus suscipit turpis nec consequat euismod. Vivamus eleifend sapien quis mollis scelerisque. Cras et nunc sed turpis dictum porta. Cras vel scelerisque ex, ac volutpat dui. Donec eleifend porta est sit amet fringilla. Sed luctus condimentum magna id aliquet. Duis eget justo ut nunc condimentum tempus non at metus. Nullam imperdiet tempus odio at pretium. 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.'
+]);
+
+function some_lorem_html($min = 2, $max = 9) {
+
+ $lorem = LOREM;
+
+ shuffle($lorem);
+
+ $p = rand($min, $max);
+
+ $html = "";
+
+ for( $i = 0 ; $i < $p ; $i++ ) {
+ $html .= "<p>". $lorem[$i] ."</p>";
+ }
+
+ 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 "<pre>";
+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 "</pre>"; \ 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 @@
+<?php
+// init app
+// -----------------------------------------------------------------------------
+include 'pythia.php';
+
+// read request parameters
+// -----------------------------------------------------------------------------
+$post_id = intval($_GET['id']);
+
+
+// Get all data needed
+// (so you don't have to make multiple requests to the wiki-database)
+// -----------------------------------------------------------------------------
+
+$post = $wiki->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 ...
+// -----------------------------------------------------------------------------
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Wiki</title>
+
+ <?php Render::template("sections/common_libs.php", [ 'admin' => false ]); ?>
+
+<body>
+ <?php include HEAD; ?>
+
+ <div class="container-fluid main-content wiki">
+
+ <div class="top-bar">
+ <div class="breadcrumb">
+
+ <?php if($post['id'] != 0) : ?>
+ <?php // breadcrumbs
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/breadcrumbs.php", [
+ 'id' => $post['category_id'],
+ 'title' => $breadcrumbs[ $post['category_id'] ]['rec']['title'],
+ 'parents' => $breadcrumbs[ $post['category_id'] ]['parents']
+ ]); ////////////////////////////////////////////////////
+ ?>
+ <?php endif ?>
+
+ </div>
+
+ <div class="admin">
+ <?php include "sections/admin-dropdown.php"; ?>
+ </div>
+
+ </div><!-- /top-bar -->
+
+ <div class="row">
+
+ <div class="col-xl-2 col-lg-3 col-sm-4 side-bar">
+
+ <!-- <h3>Κατηγορίες</h3> -->
+ <?php // categories hierarchical menu
+ ////////////////////////////////////////////////////////////
+ Render::template("sections/categories_menu.php", [
+ 'categories' => $categories,
+ 'open_path' => (($post['id'] == 0)
+ ? [1,2,3] // post not exist; open main categories
+ : $category_path) // post exists: open category path
+ ]); ////////////////////////////////////////////////////
+ ?>
+
+ </div>
+
+ <div class="col-xl-10 col-lg-9 col-sm-8 content">
+
+ <!-- THE POST -->
+ <div class="content--wrapper post">
+
+ <!-- title -->
+ <h2>
+ <?=$post['title']?>
+ <?php if (UGMC(array(61,63), $user_id)) : ?>
+ <span>
+ <a href="/wiki/admin?action=editpost&id=<?=$post_id?>" class="btn btn-xs btn-warning">
+ <i class="fas fa-pencil-alt"></i>
+ </a>
+ </span>
+ <?php endif; ?>
+ </h2>
+
+ <!-- date -->
+ <p class="post--date">
+ Δημοσίευση: <?=Render::date_friendly($post['creation_date'])?>
+ <?php if ($post['update_date'] != $post['creation_date']) : ?>
+ — Τελευταία Ενημέρωση: <?=Render::date_friendly($post['update_date'])?>
+ <?php endif; ?>
+ </p>
+
+ <!-- intro -->
+ <?php if ($post['intro'] != "") : ?>
+ <p class="intro"><?=$post['intro']?></p>
+ <?php endif; ?>
+
+ <!-- body -->
+ <div class="article-body">
+ <?=$body_html?>
+ </div>
+
+ <?php if(isset($medias)) : ?>
+ <!-- medias / attachments -->
+ <div class="medias">
+ <h4>Συνημμένα:</h4>
+ <ol>
+ <?php foreach($medias as $file) : ?>
+ <?php if ($file->reference == 1) : ?>
+ <li>
+ <a href="<?=CDN?><?=$file->path?>"
+ data-type="<?=$file->type?>"
+ target="_blank">
+ <?=$file->title?>
+ </a>
+ </li>
+ <?php endif; ?>
+ <?php endforeach; ?>
+ </ol>
+ </div>
+ <?php endif; ?>
+
+ <?php if(isset($tags)) : ?>
+ <!-- tags -->
+ <div class="tags">
+ Επικέτες:
+ <?php foreach($tags as $tag) : ?>
+ <a href="/wiki/tag?id=<?=$tag->id?>"><?=$tag->name?></a>
+ <?php endforeach; ?>
+ </div>
+ <?php endif; ?>
+
+ </div>
+
+ </div><!-- /content (post) -->
+
+ </div>
+ </div><!-- /main-content -->
+
+
+ <!-- MODALS -->
+
+ <!-- product-modal -->
+ <div id="view-product" class="modal" tabindex="-1" >
+ <div class="modal-dialog modal-lg">
+ <div class="modal-content">
+
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">
+ <i class="glyphicon glyphicon-tag"></i> Στοιχεία Προϊόντος
+ </h4>
+ </div>
+ <div class="modal-body">
+
+ <div id="modal-loader" style="display: none; text-align: center;">
+ <img src="<?php echo RP ?>img/ajax-loader.gif">
+ </div>
+
+ <div id="dynamic-product"></div>
+
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Κλείσιμο</button>
+ </div>
+
+ </div>
+ </div>
+ </div><!-- /product-modal -->
+
+ <!-- store-modal -->
+ <div id="view-store" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="store-title" aria-hidden="true" style="display: none; z-index: 2000;">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">
+ <i class="glyphicon glyphicon-shopping-cart"></i> Πληροφορίες Καταστήματος
+ </h4>
+ </div>
+ <div class="modal-body">
+ <div id="mapCanvas" style="height: 300px"></div>
+ <div id="modal-loader-store" style="display: none; text-align: center;">
+ <img src="<?php echo RP ?>img/ajax-loader.gif">
+ </div>
+ <div id="dynamic-content"></div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Κλείσιμο</button>
+ </div>
+ </div>
+ </div>
+ </div><!-- /store-modal -->
+
+ <script src="/wiki/js/wiki.js"></script>
+ <script src="/wiki/js/post-features.js"></script>
+</body>
+</html>