From 1f04b83be337cc91a3fabcf4e574e2306f3d2eaa Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Sat, 30 Mar 2024 12:14:42 -0400 Subject: refactor --- db/rest/post/api_post.sql | 3 +- db/rest/post/api_post_insert.sql | 2 + db/rest/util/_api_validate_text.sql | 4 +- web/_controller/_index.php | 23 +++++ web/_controller/apps/error.php | 20 ++++ web/_controller/apps/home.php | 89 ++++++++++++++++ web/_controller/modal.php | 26 +++++ web/_controller/template.php | 22 ++++ web/_model/apps/error.php | 35 +++++++ web/_model/apps/home.php | 22 ++++ web/_model/cache.php | 37 +++++++ web/_model/format.php | 45 +++++++++ web/_model/main.php | 84 ++++++++++++++++ web/_model/request.php | 40 ++++++++ web/config/aesthetic.php | 56 +++++++++++ web/config/routes.php | 7 ++ web/core/_controller.php | 49 +++++++++ web/core/_model.php | 44 ++++++++ web/core/aesthetic.php | 55 ---------- web/core/controller.php | 55 ---------- web/core/database.php | 1 - web/core/error.php | 12 --- web/core/helper.php | 0 web/core/loader.php | 79 +++++++++++++-- web/core/main.php | 123 ---------------------- web/core/model.php | 29 ------ web/core/router.php | 196 ++++++++++++++++++++---------------- web/helper/error.php | 9 ++ web/helper/lang.php | 77 ++++++++++++++ web/index.php | 116 +++------------------ web/lang/en_US/api_lang.php | 26 +++++ web/lang/en_US/apps/home.php | 9 ++ web/lang/en_US/common_lang.php | 10 +- web/lang/en_US/error_lang.php | 2 +- web/lang/en_US/routes/home.php | 14 --- web/public/css/common.css | 57 +++++++++++ web/public/js/lib.js | 66 +++++++++++- web/public/js/modal.js | 64 ------------ web/public/js/post.js | 38 ------- web/public/js/routes/home.js | 0 web/public/js/shared/modal.js | 64 ++++++++++++ web/public/js/shared/post.js | 38 +++++++ web/routes/error/controller.php | 20 ---- web/routes/error/model.php | 31 ------ web/routes/error/views/error.php | 4 - web/routes/home/controller.php | 84 ---------------- web/routes/home/model.php | 19 ---- web/routes/home/views/main.php | 29 ------ web/views/apps/error/main.php | 6 ++ web/views/apps/home/main.php | 30 ++++++ web/views/footer.php | 2 + web/views/header.php | 14 ++- web/views/modal/new-post.php | 28 ------ web/views/modal/new_post.php | 46 +++++++++ web/views/template/comment.php | 2 + web/views/template/error.php | 12 +++ web/views/template/modal.php | 2 + web/views/template/pfp.php | 2 + web/views/template/post.php | 2 + web/views/template/toast.php | 19 ++++ 60 files changed, 1287 insertions(+), 813 deletions(-) create mode 100644 web/_controller/_index.php create mode 100644 web/_controller/apps/error.php create mode 100644 web/_controller/apps/home.php create mode 100644 web/_controller/modal.php create mode 100644 web/_controller/template.php create mode 100644 web/_model/apps/error.php create mode 100644 web/_model/apps/home.php create mode 100644 web/_model/cache.php create mode 100644 web/_model/format.php create mode 100644 web/_model/main.php create mode 100644 web/_model/request.php create mode 100644 web/config/aesthetic.php create mode 100644 web/config/routes.php create mode 100644 web/core/_controller.php create mode 100644 web/core/_model.php delete mode 100644 web/core/aesthetic.php delete mode 100644 web/core/controller.php delete mode 100644 web/core/error.php delete mode 100644 web/core/helper.php delete mode 100644 web/core/main.php delete mode 100644 web/core/model.php create mode 100644 web/helper/error.php create mode 100644 web/helper/lang.php create mode 100644 web/lang/en_US/api_lang.php create mode 100644 web/lang/en_US/apps/home.php delete mode 100644 web/lang/en_US/routes/home.php delete mode 100644 web/public/js/modal.js delete mode 100644 web/public/js/post.js create mode 100644 web/public/js/routes/home.js create mode 100644 web/public/js/shared/modal.js create mode 100644 web/public/js/shared/post.js delete mode 100644 web/routes/error/controller.php delete mode 100644 web/routes/error/model.php delete mode 100644 web/routes/error/views/error.php delete mode 100644 web/routes/home/controller.php delete mode 100644 web/routes/home/model.php delete mode 100644 web/routes/home/views/main.php create mode 100644 web/views/apps/error/main.php create mode 100644 web/views/apps/home/main.php delete mode 100644 web/views/modal/new-post.php create mode 100644 web/views/modal/new_post.php create mode 100644 web/views/template/error.php create mode 100644 web/views/template/toast.php diff --git a/db/rest/post/api_post.sql b/db/rest/post/api_post.sql index a91d9d2..b49289c 100644 --- a/db/rest/post/api_post.sql +++ b/db/rest/post/api_post.sql @@ -5,7 +5,8 @@ CREATE VIEW api.post AS p.content, p.date FROM - admin.post p; + admin.post p + ORDER BY id DESC; GRANT SELECT ON TABLE api.post TO rest_anon, rest_user; diff --git a/db/rest/post/api_post_insert.sql b/db/rest/post/api_post_insert.sql index 9eb200c..02b9d8d 100644 --- a/db/rest/post/api_post_insert.sql +++ b/db/rest/post/api_post_insert.sql @@ -32,6 +32,8 @@ GRANT INSERT ON TABLE api.post TO rest_user; GRANT INSERT ON TABLE admin.post TO rest_user; +GRANT UPDATE ON TABLE sys.post_id_seq + TO rest_user; CREATE TRIGGER api_post_insert_trgr INSTEAD OF INSERT diff --git a/db/rest/util/_api_validate_text.sql b/db/rest/util/_api_validate_text.sql index 2a3764c..e4a6a7b 100644 --- a/db/rest/util/_api_validate_text.sql +++ b/db/rest/util/_api_validate_text.sql @@ -29,7 +29,7 @@ BEGIN IF _min IS NOT NULL AND _length < _min THEN PERFORM _api.raise( - _msg => 'api_text_min', + _msg => 'api_min_value', _detail => _column, _hint => _min || '' ); @@ -37,7 +37,7 @@ BEGIN IF _max IS NOT NULL AND _length > _max THEN PERFORM _api.raise( - _msg => 'api_text_max', + _msg => 'api_max_value', _detail => _column, _hint => _max || '' ); diff --git a/web/_controller/_index.php b/web/_controller/_index.php new file mode 100644 index 0000000..fdf9440 --- /dev/null +++ b/web/_controller/_index.php @@ -0,0 +1,23 @@ +main->session) { + $this->redirect('/home'); + } else { + $this->redirect('/login'); + } + } + +} + +?> diff --git a/web/_controller/apps/error.php b/web/_controller/apps/error.php new file mode 100644 index 0000000..5ce9ec4 --- /dev/null +++ b/web/_controller/apps/error.php @@ -0,0 +1,20 @@ +error_model = $this->load->model('apps/error'); + } + + public function index() { + parent::index(); + $data = $this->error_model->get_data(); + $this->view('header', $data); + $this->view('apps/error/main', $data); + } + +} + +?> diff --git a/web/_controller/apps/home.php b/web/_controller/apps/home.php new file mode 100644 index 0000000..25c8c4e --- /dev/null +++ b/web/_controller/apps/home.php @@ -0,0 +1,89 @@ +home_model = $this->load->model('apps/home'); + $this->request_model = $this->load->model('request'); + $this->cache_model = $this->load->model('cache'); + } + + public function index(): void { + parent::index(); + $data = $this->home_model->get_data(); + $this->view('header', $data); + $this->view('apps/home/main', $data); + } + + public function posts(): void { + $page = $this->request_model->get_int('page', 0); + $page_size = 20; + $offset = $page * $page_size; + + $user = $this->main->user(); + + $query = $this->db; + + if ($user) { + $query = $query->select('p.*, l.post_id IS NOT NULL as liked'); + } else { + $query = $query->select('p.*, FALSE as liked'); + } + + $query = $query->from('api.post p'); + + if ($user) { + $query = $query->join('admin.like l', 'p.id = l.post_id') + ->where('l.user_id')->eq($user['id']) + ->or()->where('l.user_id IS NULL'); + } + + $posts = $query->limit($page_size) + ->offset($offset) + ->rows(); + + $users = $this->cache_model->get_users($posts); + + foreach ($posts as $post) { + $data = array(); + $data['user'] = $users[$post['user_id']]; + $data['post'] = $post; + $this->view('template/post', $data); + } + } + + public function comments(): void { + $page = $this->request_model->get_int('page', 0); + $id = $this->request_model->get_int('id'); + $page_size = 20; + $offset = $page * $page_size; + + $comments = $this->db + ->select('*') + ->from('admin.comment') + ->limit($page_size) + ->offset($offset) + ->rows(); + + $users = $this->cache_model->get_users($comments); + + foreach ($comments as $comment) { + $data = array(); + $data['user'] = $users[$comment['user_id']]; + $data['comment'] = $comment; + $this->view('template/comment', $data); + } + } + +} + +?> diff --git a/web/_controller/modal.php b/web/_controller/modal.php new file mode 100644 index 0000000..9ae4ca8 --- /dev/null +++ b/web/_controller/modal.php @@ -0,0 +1,26 @@ +view('template/modal', $data); + } + + public function new_post(): void { + $this->modal('new_post'); + } +} + +?> + diff --git a/web/_controller/template.php b/web/_controller/template.php new file mode 100644 index 0000000..7a8cdf8 --- /dev/null +++ b/web/_controller/template.php @@ -0,0 +1,22 @@ +request_model = $this->load->model('request'); + } + + public function toast(): void { + $data = array( + 'msg' => $this->request_model->get_str('msg', FALSE), + 'detail' => $this->request_model->get_str('detail', FALSE), + 'hint' => $this->request_model->get_str('hint', FALSE) + ); + $this->view('template/toast', $data); + } + +} + diff --git a/web/_model/apps/error.php b/web/_model/apps/error.php new file mode 100644 index 0000000..ad72b28 --- /dev/null +++ b/web/_model/apps/error.php @@ -0,0 +1,35 @@ +get_msg($data); + return $data; + } +} +?> diff --git a/web/_model/apps/home.php b/web/_model/apps/home.php new file mode 100644 index 0000000..82fbf26 --- /dev/null +++ b/web/_model/apps/home.php @@ -0,0 +1,22 @@ +db + ->select('*') + ->from('admin.post') + ->limit(20) + ->rows(); + } + + public function get_data(): array { + $data = parent::get_data(); + $data['title'] = lang('title'); + $data['posts'] = $this->get_posts(); + return $data; + } +} diff --git a/web/_model/cache.php b/web/_model/cache.php new file mode 100644 index 0000000..6cf9924 --- /dev/null +++ b/web/_model/cache.php @@ -0,0 +1,37 @@ +users = array(); + } + + /** + * Gets a array of users + */ + public function get_users($objs) { + $ids = array(); + foreach ($objs as $obj) { + $id = $obj['user_id']; + if (!array_key_exists($id, $this->users)) { + array_push($ids, intval($id)); + } + } + if (!empty($ids)) { + $result = $this->main->db + ->select('*') + ->from('api.user') + ->where_in('id', $ids) + ->rows(); + foreach ($result as $user) { + $id = $user['id']; + $this->users[$id] = $user; + } + } + return $this->users; + } + +} diff --git a/web/_model/format.php b/web/_model/format.php new file mode 100644 index 0000000..d8c7480 --- /dev/null +++ b/web/_model/format.php @@ -0,0 +1,45 @@ +db = new DatabaseHelper(); + /// load the current session + if (array_key_exists('jwt', $_SESSION)) { + $this->get_session($_SESSION['jwt']); + } else { + $this->session = NULL; + }; + /// init other vars + $this->users = array(); + } + + /** + * Loads current session + * @param string $jwt - the user provided JWT + */ + private function get_session($jwt) { + $query = $this->db + ->select("_api.verify_jwt('" . $jwt . "') AS user_id;"); + $result = $query->row(); + $user_id = $result['user_id']; + if ($user_id) { + $this->session = array( + 'id' => $user_id, + 'jwt' => $jwt + ); + } + } + + /** + * Loads a css html link + * @param string $path - the path to the css file + */ + public function link_css($path) { + return ''; + } + + /** + * Loads a js html link + * @param string $path - the path to the js file + */ + public function link_js($path) { + return ''; + } + + /** + * Gets the current user + */ + public function user() { + if ($this->session) { + return $this->db + ->select('*') + ->from('api.user') + ->where('id') + ->eq($this->session['id']) + ->row(); + } else { + return NULL; + } + } + +} + +?> diff --git a/web/_model/request.php b/web/_model/request.php new file mode 100644 index 0000000..4cce07a --- /dev/null +++ b/web/_model/request.php @@ -0,0 +1,40 @@ +config = array( + '_common' => array( + 'js' => [ + 'js/jquery-3.7.1.min.js', + 'js/lib.js', + 'js/shared/modal.js', + ], + 'css' => [ + 'css/common.css' + ], + ), + 'error' => array( + 'css' => [ + 'css/error.css' + ], + ), + 'home' => array( + 'js' => [ + 'js/shared/post.js', + 'js/routes/home.js', + ], + 'css' => [ + 'css/home.css', + 'css/post.css' + ], + ), + ); + } + + function get_files($route) { + $js_files = $this->config['_common']['js']; + $css_files = $this->config['_common']['css']; + + if (array_key_exists($route, $this->config)) { + $config = $this->config[$route]; + if (array_key_exists('js', $config)) { + $js_files = array_merge($js_files, $config['js']); + } + if (array_key_exists('css', $config)) { + $css_files = array_merge($css_files, $config['css']); + } + } + + return array( + 'js_files' => $js_files, + 'css_files' => $css_files, + ); + } + +} diff --git a/web/config/routes.php b/web/config/routes.php new file mode 100644 index 0000000..78df332 --- /dev/null +++ b/web/config/routes.php @@ -0,0 +1,7 @@ +load = $load; + $this->main = $this->load->model('main'); + $this->db = $this->main->db; + + $info = $this->main->info; + $lang = $info['lang']; + $this->load->lang($lang); + $app = $info['app']; + if ($app) { + $this->load->app_lang($lang, $app); + } + } + + public function index() {} + + public function redirect($link) { + header('Location: '. $link, true, 301); + die(); + } + + protected function view($__name, $data = array()) { + $__root = $GLOBALS['webroot']; + $__path = $__root . '/views/' . $__name . '.php'; + if (is_file($__path)) { + extract($data); + require($__path); + return; + } + } + +} +?> diff --git a/web/core/_model.php b/web/core/_model.php new file mode 100644 index 0000000..936fab4 --- /dev/null +++ b/web/core/_model.php @@ -0,0 +1,44 @@ +load = $load; + $this->main = $this->load->model('main'); + $this->db = $this->main->db; + $this->config = new Aesthetic(); + } + + /** + * @returns the base model data + */ + public function get_data(): array { + $data = array(); + $data['self'] = $this->main->user(); + + $info = $this->main->info; + $app = $info['app']; + + if ($app) { + $files = $this->config->get_files($app); + $data = array_merge($data, $files); + } else { + $files = $this->config->get_files(); + $data = array_merge($data, $files); + } + + return $data; + } +} diff --git a/web/core/aesthetic.php b/web/core/aesthetic.php deleted file mode 100644 index 1180ad1..0000000 --- a/web/core/aesthetic.php +++ /dev/null @@ -1,55 +0,0 @@ -config = array( - '_common' => array( - 'js' => [ - 'js/jquery-3.7.1.min.js', - 'js/lib.js', - 'js/modal.js', - ], - 'css' => [ - 'css/common.css' - ], - ), - 'error' => array( - 'css' => [ - 'css/error.css' - ], - ), - 'home' => array( - 'js' => [ - 'js/post.js', - ], - 'css' => [ - 'css/home.css', - 'css/post.css' - ], - ), - ); - } - - function get_files($route) { - $js_files = $this->config['_common']['js']; - $css_files = $this->config['_common']['css']; - - if (array_key_exists($route, $this->config)) { - $config = $this->config[$route]; - if (array_key_exists('js', $config)) { - $js_files = array_merge($js_files, $config['js']); - } - if (array_key_exists('css', $config)) { - $css_files = array_merge($css_files, $config['css']); - } - } - - return array( - 'js_files' => $js_files, - 'css_files' => $css_files, - ); - } - -} diff --git a/web/core/controller.php b/web/core/controller.php deleted file mode 100644 index 946b460..0000000 --- a/web/core/controller.php +++ /dev/null @@ -1,55 +0,0 @@ -main = $GLOBALS['__vars']['main']; - $this->load = $GLOBALS['__vars']['load']; - $this->db = $this->main->db; - - $info = $this->main->info; - $lang_code = $info['lang']; - $route_name = $info['route']; - $this->load->lang($lang_code); - $this->load->route_lang($lang_code, $route_name); - } - - public function index() {} - - protected function view($__name, $data = array()) { - $__root = $GLOBALS['webroot']; - $__path = $__root . '/views/' . $__name . '.php'; - if (is_file($__path)) { - extract($data); - require($__path); - return; - } - } - - protected function app_view($__name, $data = array()) { - $__root = $GLOBALS['webroot']; - $__route = $this->main->info['route']; - $__path = $__root . '/routes/' . $__route . '/views/' . $__name . '.php'; - if (is_file($__path)) { - extract($data); - require($__path); - return; - } - } - - protected function modal($title, $content, $data = array()) { - $data['title'] = $title; - $data['content'] = $content; - $this->view('template/modal', $data); - } - -} -?> diff --git a/web/core/database.php b/web/core/database.php index b3a597b..4b44e86 100644 --- a/web/core/database.php +++ b/web/core/database.php @@ -170,4 +170,3 @@ class DatabaseHelper { } } -?> diff --git a/web/core/error.php b/web/core/error.php deleted file mode 100644 index 2e02cb1..0000000 --- a/web/core/error.php +++ /dev/null @@ -1,12 +0,0 @@ - - - - <?=$code . ' - ' . $msg?> - - -
-

-
-
- - diff --git a/web/core/helper.php b/web/core/helper.php deleted file mode 100644 index e69de29..0000000 diff --git a/web/core/loader.php b/web/core/loader.php index 4d4526c..2091533 100644 --- a/web/core/loader.php +++ b/web/core/loader.php @@ -1,16 +1,79 @@ loaded = array(); + } + + /** + * Loads a $type of object from a $dir with a given $name + * @param string $name - the name of the object to load + * @param string $dir - the directory theese objects are stored in + * @param string $type - the type of the object + */ + private function load_type($name, $dir, $type): object|NULL { + $path = $dir . '/' . $name . '.php'; + if (array_key_exists($path, $this->loaded)) { + return $this->loaded[$path]; + } + + if (!file_exists($path)) { + return NULL; + } + + $parts = explode('/', $name); + $part = end($parts); + $class = ucfirst($part) . '_' . $type; + require($path); + + $ref = NULL; + try { + $ref = new ReflectionClass($class); + } catch (Exception $_e) {} + + if ($ref === NULL) { + return NULL; + } + + $obj = $ref->newInstance($this); + $this->loaded[$path] = $obj; + + return $obj; + } + + /** + * Loads a model + * @param string $name - the name of the model to load + */ + public function model($name): object|NULL { + $root = $GLOBALS['webroot']; + $dir = $root . '/_model'; + return $this->load_type($name, $dir, 'model'); + } + + /** + * Loads a controller + * @param string $name - the name of the controller to load + */ + public function controller($name): Controller|NULL { + $root = $GLOBALS['webroot']; + $dir = $root . '/_controller'; + return $this->load_type($name, $dir, 'controller'); + } + /** * Loads the given common lang - * @param lang_code - the language code + * @param string $lang_code 0 the language code */ - public function lang($lang_code) { + public function lang($lang_code): void { $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/'; $lang = $GLOBALS['lang']; if ($handle = opendir($dir)) { while (false !== ($entry = readdir($handle))) { - if ($entry === '.' || $entry === '..' || $entry === 'routes') { + if ($entry === '.' || $entry === '..' || $entry === 'apps') { continue; } $path = $dir . $entry; @@ -21,12 +84,12 @@ class Loader { } /** - * Loads a given route specific lang - * @param lang_coed - the language code - * #param name - the name of the route + * Loads a given app specific lang + * @param string $lang_code - the language code + * @param string $name - the name of the app */ - public function route_lang($lang_code, $name) { - $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/routes/'; + public function app_lang($lang_code, $name): void { + $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/apps/'; $file = $dir . $name . '.php'; if (file_exists($file)) { $lang = $GLOBALS['lang']; diff --git a/web/core/main.php b/web/core/main.php deleted file mode 100644 index c3c65dd..0000000 --- a/web/core/main.php +++ /dev/null @@ -1,123 +0,0 @@ -info = NULL; - $this->db = new DatabaseHelper(); - $this->users = array(); - $_SESSION['jwt'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicmVzdF91c2VyIiwidXNlcl9pZCI6MSwiZXhwIjoxNzExODUxMDUzfQ.FUcFO44SWV--YtVOy7NftTF8OeeOYGZDaDHigygQxsY'; - if (array_key_exists('jwt', $_SESSION)) { - $this->get_session($_SESSION['jwt']); - } else { - $this->user_id = NULL; - }; - } - - private function get_session($jwt) { - $query = $this->db - ->select("_api.verify_jwt('" . $jwt . "') AS user_id;"); - $result = $query->row(); - $user_id = $result['user_id']; - if ($user_id) { - $this->user_id = $user_id; - } - } - - public function link_css($path) { - return ''; - } - - public function link_js($path) { - return ''; - } - - public function user() { - if ($this->user_id) { - return $this->db - ->select('*') - ->from('api.user') - ->where('id') - ->eq($this->user_id) - ->row(); - } else { - return NULL; - } - } - - public function get_num($key, $default = NULL) { - if (!array_key_exists($key, $_GET)) { - if ($default !== NULL) { - return $default; - } else { - error_page(400, lang('error_400')); - } - } else { - $val = $_GET[$key]; - $val = intval($val); - if ($val < 0) { - return 0; - } else { - return $val; - } - } - } - - public function get_users($objs) { - $ids = array(); - foreach ($objs as $obj) { - $id = $obj['user_id']; - if (!array_key_exists($id, $this->users)) { - array_push($ids, intval($id)); - } - } - if (!empty($ids)) { - $result = $this->db - ->select('*') - ->from('api.user') - ->where_in('id', $ids) - ->rows(); - foreach ($result as $user) { - $id = $user['id']; - $this->users[$id] = $user; - } - } - return $this->users; - } - - public function display_name($user) { - $name = ''; - if ($user['first_name']) { - $name .= $user['first_name']; - } - if ($user['middle_name']) { - if ($name != '') { - $name .= ' '; - } - $name .= $user['middle_name']; - } - if ($user['last_name']) { - if ($name != '') { - $name .= ' '; - } - $name .= $user['last_name']; - } - if ($name == '') { - $name = '@' . $user['username']; - } - return $name; - } - - public function display_date($date) { - return $date; - } - -} - -?> diff --git a/web/core/model.php b/web/core/model.php deleted file mode 100644 index 039b138..0000000 --- a/web/core/model.php +++ /dev/null @@ -1,29 +0,0 @@ -main = $GLOBALS['__vars']['main']; - $this->load = $GLOBALS['__vars']['load']; - $this->db = $this->main->db; - $this->config = new Aesthetic(); - } - - public function get_data() { - $data = array(); - $route = $this->main->info['route']; - $files = $this->config->get_files($route); - $data = array_merge($data, $files); - $data['self'] = $this->main->user(); - return $data; - } -} -?> diff --git a/web/core/router.php b/web/core/router.php index 6ee28a9..72c7674 100644 --- a/web/core/router.php +++ b/web/core/router.php @@ -1,127 +1,147 @@ newInstance(); - $controller_ref = new ReflectionClass($controller_cls); - $controller = $controller_ref->newInstance($model); + // the main model + private $main; - return $controller; + /** + * Creates a router + * @param Loader $load - the main laoder object + */ + function __construct($load) { + $this->load = $load; + $this->main = $this->load->model('main'); } - function __construct($main, $load) { - - $routes = array( - 'home' => array( - 'slugs' => ['', 'home'], - 'model' => 'HomeModel', - 'controller' => 'HomeController', - ), - ); - - $this->routes = array(); - foreach ($routes as $name => $route) { - foreach ($route['slugs'] as $slug) { - $this->routes[$slug] = $route; - $this->routes[$slug]['name'] = $name; - } + /** + * @param string $path - the current request path + * Gets the current route + * @return array + */ + private function get_req_route($path): array { + // trim the path + $path = trim($path); + // remove first '/' + $path = substr($path, 1); + // get path parts + $parts = explode('/', $path); + + $len = count($parts); + + // get route info + $route = array(); + // e.g. / + if ($path === '') { + $route = array( + 'route' => '', + 'slug' => 'index', + ); + // e.g. /home /login + } else if ($len === 1) { + $route = array( + 'route' => $parts[0], + 'slug' => 'index', + ); + // e.g. /home/posts + } else { + $route = array ( + 'route' => implode('/', array_slice($parts, 0, -1)), + 'slug' => end($parts) + ); + }; + + $route['app'] = $route['route']; + $routes = $GLOBALS['routes']; + if (array_key_exists($route['route'], $routes)) { + $route['route'] = $routes[$route['route']]; } - $this->main = $main; - $this->load = $load; + return $route; } - function get_info() { - $uri = parse_url($_SERVER['REQUEST_URI']); + /** + * Gets the curret request info + * @return array + */ + private function get_req(): array { $method = $_SERVER['REQUEST_METHOD']; - $parts = explode('/', $uri['path']); - $slug = sizeof($parts) > 1 ? $parts[1] : ''; - $path = sizeof($parts) > 2 ? $parts[2] : 'index'; - - if (sizeof($parts) > 3) { - return NULL; - } - - return array( - 'method' => $method, - 'uri' => $uri, + $uri = parse_url($_SERVER['REQUEST_URI']); + $path = $uri['path']; - 'slug' => $slug, - 'path' => $path + return array_merge( + array( + 'uri' => $uri, + 'method' => $method, + 'lang' => $this->get_lang(), + ), + $this->get_req_route($path), ); } - function handle_error($code) { - $route = array( - 'name' => 'error', - 'model' => 'ErrorModel', - 'controller' => 'ErrorController' - ); - $this->main->info = array( - 'slug' => 'error', - 'lang' => 'en_US', - 'route' => 'error' - ); - $controller = $this->load_route($route); - $_GET['code'] = $code; - http_response_code($code); - $controller->index(); + /** + * Gets the current language + * @return string + */ + private function get_lang(): string { + return 'en_US'; } - public function handle_request() { - $request = $this->get_info(); - - if ($request === NULL) { - $this->handle_error(404); - return; + /** + * Handles a router error code + * @param int $code - the http error code + * @param bool $recursed + */ + private function handle_error($code, $recursed): void { + if ($recursed) { + die($code . ' (recursed)'); } - $slug = $request['slug']; - if (!array_key_exists($slug, $this->routes)) { - $this->handle_error(404); - return; - } + $this->main->info['slug'] = 'index'; + $this->main->info['app'] = 'error'; + $this->main->info['route'] = 'apps/error'; + $req = $this->main->info; + $_GET['code'] = $code; - $route = $this->routes[$slug]; - $this->main->info = array( - 'lang' => 'en_US', - 'slug' => $slug, - 'route' => $route['name'], - ); + $this->handle_req($req, TRUE); + } - $controller = $this->load_route($route); + /** + * @param array $req + * @param bool $recursed + */ + private function handle_req($req, $recursed = FALSE): void { + $controller = $this->load->controller($req['route']); - $path = $request['path']; - $ref = NULL; + if ($controller === NULL) { + $this->handle_error(404, $recursed); + return; + } + $ref = NULL; try { - $ref = new ReflectionMethod($controller, $path); + $ref = new ReflectionMethod($controller, $req['slug']); } catch (Exception $_e) {} if ($ref === NULL || !$ref->isPublic()) { - $this->handle_error(404); + $this->handle_error(404, $recursed); return; } $ref->invoke($controller); + } + /** + * Handels the incomming reuqest + */ + public function handle_request(): void { + $req = $this->get_req(); + $this->main->info = $req; + $this->handle_req($req); } } diff --git a/web/helper/error.php b/web/helper/error.php new file mode 100644 index 0000000..6fcaddd --- /dev/null +++ b/web/helper/error.php @@ -0,0 +1,9 @@ + $attr) { + echo $key . '="' . $attr . '" '; + } + echo '> '; + if ($icon) { + echo ''; + if ($content) { + echo $content; + } + echo ''; + } + if ($text) { + echo '' . $text . ''; + } + if ($click) { + echo ''; + } else { + echo ''; + } +} diff --git a/web/index.php b/web/index.php index 1032b7f..9c2d239 100644 --- a/web/index.php +++ b/web/index.php @@ -2,114 +2,26 @@ session_start(); -$lang = array(); -$__vars = array(); $webroot = dirname(__FILE__); -function error_page($code, $msg) { - $root = $GLOBALS['webroot']; - error_reporting(E_ERROR | E_PARSE); - http_response_code($code); - require($root . '/core/error.php'); - die(); -} +// load all the helper files +require($webroot . '/helper/error.php'); +require($webroot . '/helper/lang.php'); -function lang($key, $default = NULL, $sub = NULL) { - $lang = $GLOBALS['lang']; - if(array_key_exists($key, $lang)) { - if ($sub) { - return sprintf($lang[$key], ...$sub); - } else { - return $lang[$key]; - } - } else if ($default !== NULL) { - return $default; - } else { - return $key; - } -} +// load all the config files +require($webroot . '/config/aesthetic.php'); +require($webroot . '/config/routes.php'); -function ilang($key, - $class = NULL, - $id = NULL, - $href = NULL, - $click = NULL, - $attrs = array(), - $sub = NULL, - $button = FALSE, -) { - $text = lang($key . "_text", FALSE, sub: $sub); - $tip = lang($key . "_tip", FALSE); - $icon = lang($key . "_icon", FALSE); - $content = lang($key . "_content", FALSE); - - if ($click || $button) { - echo ''; - } else { - echo ''; - } -} +// load all core files +require($webroot . '/core/_controller.php'); +require($webroot . '/core/_model.php'); +require($webroot . '/core/database.php'); +require($webroot . '/core/loader.php'); +require($webroot . '/core/router.php'); function __init() { - - $root = $GLOBALS['webroot']; - - // load all core files - require($root . '/core/database.php'); - require($root . '/core/aesthetic.php'); - require($root . '/core/controller.php'); - require($root . '/core/model.php'); - require($root . '/core/loader.php'); - require($root . '/core/main.php'); - require($root . '/core/router.php'); - - $main = new MainModel(); $load = new Loader(); - $router = new Router($main, $load); - - $GLOBALS['__vars']['main'] = $main; - $GLOBALS['__vars']['load'] = $load; - $GLOBALS['__vars']['router'] = $router; - + $router = new Router($load); $router->handle_request(); }; @@ -118,5 +30,3 @@ if (!file_exists('/status/ready')) { } __init(); - -?> diff --git a/web/lang/en_US/api_lang.php b/web/lang/en_US/api_lang.php new file mode 100644 index 0000000..129147c --- /dev/null +++ b/web/lang/en_US/api_lang.php @@ -0,0 +1,26 @@ + diff --git a/web/lang/en_US/apps/home.php b/web/lang/en_US/apps/home.php new file mode 100644 index 0000000..a30eb88 --- /dev/null +++ b/web/lang/en_US/apps/home.php @@ -0,0 +1,9 @@ + diff --git a/web/lang/en_US/common_lang.php b/web/lang/en_US/common_lang.php index 3ce2fdb..7e214b5 100644 --- a/web/lang/en_US/common_lang.php +++ b/web/lang/en_US/common_lang.php @@ -1,4 +1,4 @@ - diff --git a/web/public/css/common.css b/web/public/css/common.css index 05f429f..84aabfc 100644 --- a/web/public/css/common.css +++ b/web/public/css/common.css @@ -52,6 +52,8 @@ body { } header { + top: 0; + position: sticky; height: 3.5rem; background-color: var(--primary); display: flex; @@ -145,6 +147,10 @@ input:focus { align-items: center; } +.nav { + position: sticky; +} + .nav-right { flex: 1; justify-content: flex-end; @@ -333,6 +339,30 @@ input:focus { border-radius: .5rem; display: flex; flex-direction: column; + animation: fadeIn .1s, slideInModal .1s linear; +} + +@keyframes slideInModal { + 0% { + animation-timing-function: ease-in; + transform: translate(-50%, -60%); + } +} + +@keyframes slideIn { + 0% { + animation-timing-function: ease-out; + transform: translate(0, -50%); + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } } .modal>form { @@ -402,3 +432,30 @@ button[type="submit"]:hover { background-color: var(--logo); } +#toast-container { + position: fixed; + top: 4rem; + left: 100%; + transform: translateX(-110%); + margin-top: 1rem; + z-index: 10000; +} + +.toast { + padding: .75rem; + margin: .5rem; + border-radius: .5rem; + min-width: 15rem; + font-family: sfpro; + animation: fadeIn .1s, slideIn .25s linear; + display: flex; + justify-content: space-between; +} + +.toast.error { + background-color: var(--error); +} + +.toast.success { + background-color: var(--success); +} diff --git a/web/public/js/lib.js b/web/public/js/lib.js index d822b02..7647208 100644 --- a/web/public/js/lib.js +++ b/web/public/js/lib.js @@ -1,4 +1,8 @@ +/// +/// document ready functions +/// + let ready = false; $(function() { @@ -15,9 +19,14 @@ var r$ = function(callback) { } } +/// +/// dom observer +/// checks for elements on the DOM now and added later +/// + function observe(containerSelector, elementSelector, callback) { - r$(() => { + r$(() => { $(containerSelector + ' ' + elementSelector).each(function (_, e) { let me = $(e); callback(me); @@ -37,9 +46,64 @@ function observe(containerSelector, elementSelector, callback) { }; var target = $(containerSelector)[0]; + + if (!target) { + console.warn('[observe] didnt find container: ', containerSelector); + return; + } + var config = { childList: true, subtree: true }; var MutationObserver = window.MutationObserver; var observer = new MutationObserver(onMutationsObserved); observer.observe(target, config); }); } + +/// +/// ajax setup +/// + +let ajaxHeaders = {}; +ajaxHeaders['Content-Type'] = 'application/json'; +if (jwtStr) { + ajaxHeaders['Authorization'] = 'Bearer ' + jwtStr +} + +$.ajaxSetup({ + headers: ajaxHeaders +}) + +/// +/// ajax error handle +/// + +var errorToast = (xhr) => { + let data = xhr.responseJSON; + let msg = data.message; + let detail = data.details; + let hint = data.hint; + + let query = '?msg=' + msg; + if (detail) { + query += '&detail=' + detail; + } + if (hint) { + query += '&hint=' + hint; + } + let url = '/template/toast' + query; + $.get(url, function (data) { + $('#toast-container').prepend(data); + }) +} + +observe('#toast-container', '.action-close-toast', function(el) { + el.on('click', function() { + el.parent().remove(); + }); + + setTimeout(function() { + el.parent().remove(); + }, 5000); +}); + + diff --git a/web/public/js/modal.js b/web/public/js/modal.js deleted file mode 100644 index 792cd85..0000000 --- a/web/public/js/modal.js +++ /dev/null @@ -1,64 +0,0 @@ -$(document).on('click', ".modal-close", (o) => { - $(o.target).closest('.modal-container').remove(); -}); - -const initDrag = (header, modal, container) => { - let drag = false; - - let mouseX, mouseY, modalX, modalY; - - const onStart = (e) => { - e = e || window.event; - e.preventDefault(); - mouseX = e.clientX; - mouseY = e.clientY; - drag = true; - }; - - const onDrag = (e) => { - e = e || window.event; - e.preventDefault(); - if (!drag) { - return; - } - modalX = mouseX - e.clientX; - modalY = mouseY - e.clientY; - mouseX = e.clientX; - mouseY = e.clientY; - - let posX = (modal.offsetLeft - modalX), - posY = (modal.offsetTop - modalY); - - let minX = modal.offsetWidth / 2, - minY = modal.offsetHeight / 2; - - let maxX = container.offsetWidth - minX, - maxY = container.offsetHeight - minY; - - posX = Math.max(minX, Math.min(maxX, posX)); - posY = Math.max(minY, Math.min(maxY, posY)); - - posX = posX / container.offsetWidth * 100; - posY = posY / container.offsetHeight * 100; - - modal.style.left = posX + "%"; - modal.style.top = posY + "%"; - }; - - const onEnd = () => { - drag = false; - }; - - header.onmousedown = onStart; - container.onmousemove = onDrag; - container.onmouseup = onEnd; -}; - -observe('body', '.modal-header', function(el) { - let header = $(el); - let modal = header.closest('.modal'); - let container = header.closest('.modal-container'); - initDrag( - header[0], modal[0], container[0] - ); -}); diff --git a/web/public/js/post.js b/web/public/js/post.js deleted file mode 100644 index 736fa2b..0000000 --- a/web/public/js/post.js +++ /dev/null @@ -1,38 +0,0 @@ -observe('.post', '.action-load-comments', function(me) { - me.on('click', function() { - let page = me.attr('page'); - if (!page) { - page = '1'; - } - let newPage = Number(page) + 1; - let id = me.attr('postId'); - me.attr('page', newPage + ''); - let url = '/home/comments?page=' + page + '&id=' + id; - $.get(url, function (data) { - if (data === '') { - me.remove(); - } else { - $(me).prepend(data); - } - }); - }); -}); - -observe('#main-content', '#action-load-posts', function(me) { - me.on('click', function () { - let page = me.attr('page'); - if (!page) { - page = '1'; - } - let newPage = Number(page) + 1; - me.attr('page', newPage + ''); - let url = '/home/posts?page=' + page; - $.get(url, function (data) { - if (data === '') { - me.remove(); - } else { - $('#post-container').append(data); - } - }); - }); -}); diff --git a/web/public/js/routes/home.js b/web/public/js/routes/home.js new file mode 100644 index 0000000..e69de29 diff --git a/web/public/js/shared/modal.js b/web/public/js/shared/modal.js new file mode 100644 index 0000000..792cd85 --- /dev/null +++ b/web/public/js/shared/modal.js @@ -0,0 +1,64 @@ +$(document).on('click', ".modal-close", (o) => { + $(o.target).closest('.modal-container').remove(); +}); + +const initDrag = (header, modal, container) => { + let drag = false; + + let mouseX, mouseY, modalX, modalY; + + const onStart = (e) => { + e = e || window.event; + e.preventDefault(); + mouseX = e.clientX; + mouseY = e.clientY; + drag = true; + }; + + const onDrag = (e) => { + e = e || window.event; + e.preventDefault(); + if (!drag) { + return; + } + modalX = mouseX - e.clientX; + modalY = mouseY - e.clientY; + mouseX = e.clientX; + mouseY = e.clientY; + + let posX = (modal.offsetLeft - modalX), + posY = (modal.offsetTop - modalY); + + let minX = modal.offsetWidth / 2, + minY = modal.offsetHeight / 2; + + let maxX = container.offsetWidth - minX, + maxY = container.offsetHeight - minY; + + posX = Math.max(minX, Math.min(maxX, posX)); + posY = Math.max(minY, Math.min(maxY, posY)); + + posX = posX / container.offsetWidth * 100; + posY = posY / container.offsetHeight * 100; + + modal.style.left = posX + "%"; + modal.style.top = posY + "%"; + }; + + const onEnd = () => { + drag = false; + }; + + header.onmousedown = onStart; + container.onmousemove = onDrag; + container.onmouseup = onEnd; +}; + +observe('body', '.modal-header', function(el) { + let header = $(el); + let modal = header.closest('.modal'); + let container = header.closest('.modal-container'); + initDrag( + header[0], modal[0], container[0] + ); +}); diff --git a/web/public/js/shared/post.js b/web/public/js/shared/post.js new file mode 100644 index 0000000..afbeaf0 --- /dev/null +++ b/web/public/js/shared/post.js @@ -0,0 +1,38 @@ +observe('#main-content', '.action-load-comments', function(me) { + me.on('click', function() { + let page = me.attr('page'); + if (!page) { + page = '1'; + } + let newPage = Number(page) + 1; + let id = me.attr('postId'); + me.attr('page', newPage + ''); + let url = '/home/comments?page=' + page + '&id=' + id; + $.get(url, function (data) { + if (data === '') { + me.remove(); + } else { + $(me).prepend(data); + } + }); + }); +}); + +observe('#main-content', '#action-load-posts', function(me) { + me.on('click', function () { + let page = me.attr('page'); + if (!page) { + page = '1'; + } + let newPage = Number(page) + 1; + me.attr('page', newPage + ''); + let url = '/home/posts?page=' + page; + $.get(url, function (data) { + if (data === '') { + me.remove(); + } else { + $('#post-container').append(data); + } + }); + }); +}); diff --git a/web/routes/error/controller.php b/web/routes/error/controller.php deleted file mode 100644 index 3cb2345..0000000 --- a/web/routes/error/controller.php +++ /dev/null @@ -1,20 +0,0 @@ -model = $model; - } - - public function index() { - parent::index(); - $data = $this->model->get_data(); - $this->view('header', $data); - $this->app_view('error', $data); - } - -} - -?> diff --git a/web/routes/error/model.php b/web/routes/error/model.php deleted file mode 100644 index a30fccc..0000000 --- a/web/routes/error/model.php +++ /dev/null @@ -1,31 +0,0 @@ -get_msg($data); - return $data; - } -} -?> diff --git a/web/routes/error/views/error.php b/web/routes/error/views/error.php deleted file mode 100644 index 3b926bc..0000000 --- a/web/routes/error/views/error.php +++ /dev/null @@ -1,4 +0,0 @@ -
-

- -
diff --git a/web/routes/home/controller.php b/web/routes/home/controller.php deleted file mode 100644 index 775e43a..0000000 --- a/web/routes/home/controller.php +++ /dev/null @@ -1,84 +0,0 @@ -model = $model; - } - - public function index() { - parent::index(); - $data = $this->model->get_data(); - $this->view('header', $data); - $this->app_view('main', $data); - } - - public function posts() { - $page = $this->main->get_num('page', 0); - $page_size = 20; - $offset = $page * $page_size; - - $user = $this->main->user(); - - $query = $this->db; - - if ($user) { - $query = $query->select('p.*, l.post_id IS NOT NULL as liked'); - } else { - $query = $query->select('p.*, FALSE as liked'); - } - - $query = $query->from('admin.post p'); - - if ($user) { - $query = $query->join('admin.like l', 'p.id = l.post_id') - ->where('l.user_id')->eq($user['id']) - ->or()->where('l.user_id IS NULL'); - } - - $posts = $query->limit($page_size) - ->offset($offset) - ->rows(); - - $users = $this->main->get_users($posts); - - foreach ($posts as $post) { - $data = array(); - $data['user'] = $users[$post['user_id']]; - $data['post'] = $post; - $this->view('template/post', $data); - } - } - - public function comments() { - $page = $this->main->get_num('page', 0); - $id = $this->main->get_num('id'); - $page_size = 20; - $offset = $page * $page_size; - - $comments = $this->db - ->select('*') - ->from('admin.comment') - ->limit($page_size) - ->offset($offset) - ->rows(); - - $users = $this->main->get_users($comments); - - foreach ($comments as $comment) { - $data = array(); - $data['user'] = $users[$comment['user_id']]; - $data['comment'] = $comment; - $this->view('template/comment', $data); - } - } - - public function new_post_modal() { - $this->modal(lang('new_post_modal_title'), 'new-post'); - } - -} - -?> diff --git a/web/routes/home/model.php b/web/routes/home/model.php deleted file mode 100644 index 44a8549..0000000 --- a/web/routes/home/model.php +++ /dev/null @@ -1,19 +0,0 @@ -db - ->select('*') - ->from('admin.post') - ->limit(20) - ->rows(); - } - - public function get_data() { - $data = parent::get_data(); - $data['title'] = lang('title'); - $data['posts'] = $this->get_posts(); - return $data; - } -} -?> diff --git a/web/routes/home/views/main.php b/web/routes/home/views/main.php deleted file mode 100644 index bf0087b..0000000 --- a/web/routes/home/views/main.php +++ /dev/null @@ -1,29 +0,0 @@ - -
- -
-
- view('template/pfp', array('user' => $self))?> - - - -
- -
- -
- posts()?> -
- -
diff --git a/web/views/apps/error/main.php b/web/views/apps/error/main.php new file mode 100644 index 0000000..81051bd --- /dev/null +++ b/web/views/apps/error/main.php @@ -0,0 +1,6 @@ + + +
+

+ +
diff --git a/web/views/apps/home/main.php b/web/views/apps/home/main.php new file mode 100644 index 0000000..b1c1efc --- /dev/null +++ b/web/views/apps/home/main.php @@ -0,0 +1,30 @@ + + +
+ +
+
+ view('template/pfp', array('user' => $self))?> + + + +
+ +
+ +
+ posts()?> +
+ +
diff --git a/web/views/footer.php b/web/views/footer.php index 6cbe21b..1266b9a 100644 --- a/web/views/footer.php +++ b/web/views/footer.php @@ -1,2 +1,4 @@ + + diff --git a/web/views/header.php b/web/views/header.php index 183f4f4..891e27e 100644 --- a/web/views/header.php +++ b/web/views/header.php @@ -1,10 +1,18 @@ - + + main->user(); ?> + main->link_js($js); @@ -71,4 +79,6 @@ menu.toggleClass('visible'); }); - + +
+
diff --git a/web/views/modal/new-post.php b/web/views/modal/new-post.php deleted file mode 100644 index 7215862..0000000 --- a/web/views/modal/new-post.php +++ /dev/null @@ -1,28 +0,0 @@ -main->user(); -?> -
-
-
- view('template/pfp', array('user' => $user))?> -
- - -
-
- -
- -
diff --git a/web/views/modal/new_post.php b/web/views/modal/new_post.php new file mode 100644 index 0000000..82243fb --- /dev/null +++ b/web/views/modal/new_post.php @@ -0,0 +1,46 @@ + + +main->user(); +?> +
+
+
+ view('template/pfp', array('user' => $user))?> +
+ + +
+
+ +
+ +
+ diff --git a/web/views/template/comment.php b/web/views/template/comment.php index ef7a081..943f232 100644 --- a/web/views/template/comment.php +++ b/web/views/template/comment.php @@ -1,3 +1,5 @@ + +
view('template/pfp', array('user' => $user))?>
diff --git a/web/views/template/error.php b/web/views/template/error.php new file mode 100644 index 0000000..2e02cb1 --- /dev/null +++ b/web/views/template/error.php @@ -0,0 +1,12 @@ + + + + <?=$code . ' - ' . $msg?> + + +
+

+
+
+ + diff --git a/web/views/template/modal.php b/web/views/template/modal.php index 4f47400..e3ce6fe 100644 --- a/web/views/template/modal.php +++ b/web/views/template/modal.php @@ -1,3 +1,5 @@ + +