From a979c73924f16c515a70e929aaad4e3c41aa6bec Mon Sep 17 00:00:00 2001 From: bckelley <> Date: Thu, 25 Jan 2024 09:52:59 -0600 Subject: [PATCH] init commit --- .gitignore | 2 + .htaccess | 5 + .todo | 0 LICENSE | 447 +++++++++++++++++++++++++++++++++++ app/.htaccess | 1 + app/bootstrap.php | 25 ++ app/config/config.php | 21 ++ app/controllers/Pages.php | 53 +++++ app/controllers/Posts.php | 222 +++++++++++++++++ app/controllers/Users.php | 204 ++++++++++++++++ app/helpers/sessions.php | 57 +++++ app/helpers/url.php | 12 + app/libraries/Controller.php | 43 ++++ app/libraries/Core.php | 68 ++++++ app/libraries/Database.php | 123 ++++++++++ app/models/Post.php | 117 +++++++++ app/models/User.php | 99 ++++++++ app/views/inc/footer.php | 9 + app/views/inc/header.php | 24 ++ app/views/inc/navbar.php | 53 +++++ app/views/pages/about.php | 9 + app/views/pages/index.php | 8 + app/views/posts/add.php | 28 +++ app/views/posts/edit.php | 28 +++ app/views/posts/index.php | 23 ++ app/views/posts/show.php | 21 ++ app/views/users/login.php | 35 +++ app/views/users/register.php | 42 ++++ public/.htaccess | 8 + public/css/style.css | 0 public/index.php | 5 + public/js/main.js | 0 readme.md | 78 ++++++ 33 files changed, 1870 insertions(+) create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 .todo create mode 100644 LICENSE create mode 100644 app/.htaccess create mode 100644 app/bootstrap.php create mode 100644 app/config/config.php create mode 100644 app/controllers/Pages.php create mode 100644 app/controllers/Posts.php create mode 100644 app/controllers/Users.php create mode 100644 app/helpers/sessions.php create mode 100644 app/helpers/url.php create mode 100644 app/libraries/Controller.php create mode 100644 app/libraries/Core.php create mode 100644 app/libraries/Database.php create mode 100644 app/models/Post.php create mode 100644 app/models/User.php create mode 100644 app/views/inc/footer.php create mode 100644 app/views/inc/header.php create mode 100644 app/views/inc/navbar.php create mode 100644 app/views/pages/about.php create mode 100644 app/views/pages/index.php create mode 100644 app/views/posts/add.php create mode 100644 app/views/posts/edit.php create mode 100644 app/views/posts/index.php create mode 100644 app/views/posts/show.php create mode 100644 app/views/users/login.php create mode 100644 app/views/users/register.php create mode 100644 public/.htaccess create mode 100644 public/css/style.css create mode 100644 public/index.php create mode 100644 public/js/main.js create mode 100644 readme.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32f8c12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.git +.vscode diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..25a878e --- /dev/null +++ b/.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine on + RewriteRule ^$ public/ [L] + RewriteRule (.*) public/$1 [L] + diff --git a/.todo b/.todo new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b1e5465 --- /dev/null +++ b/LICENSE @@ -0,0 +1,447 @@ +Attribution-NonCommercial-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International Public License +("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-NC-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution, NonCommercial, and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + l. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + m. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + n. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only by individuals, + non-profit organizations, and small businesses + (not qualifying as major corporations); and + + b. produce, reproduce, and Share Adapted Material for + NonCommercial purposes only by individuals, non-profit + organizations, and small businesses + (not qualifying as major corporations). + + 5. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 6. Term. The term of this Public License is specified in Section + 6(a). + + 7. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 8. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 9. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Restrictions on Major Corporations. + + 1. The use of the Licensed Material by major corporations is + strictly prohibited under this license. Major corporations + are defined as entities with a significant market presence, + substantial financial resources, and a broad customer base. + + c. Share Back Requirement. + + 1. All users are required to share any Adapted Material back + to the original source, prominently mentioning the original + creator and providing a URI or hyperlink to the original + Licensed Material to the extent reasonably practicable. + + d. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-NC-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..45552cb --- /dev/null +++ b/app/.htaccess @@ -0,0 +1 @@ +Options -Indexes \ No newline at end of file diff --git a/app/bootstrap.php b/app/bootstrap.php new file mode 100644 index 0000000..78b2a24 --- /dev/null +++ b/app/bootstrap.php @@ -0,0 +1,25 @@ + 'Home', + 'description' => '', + ]; + + // Load the view with the specified data + $this->view('pages/index', $data); + } + + /** + * About Method + * + * Handles the about us page of the application. + */ + public function about(){ + // Data for the view + $data = [ + 'title' => 'About Us', + 'description' => '', + 'version' => APP_VERSION, + ]; + + // Load the view with the specified data + $this->view('pages/about', $data); + } +} diff --git a/app/controllers/Posts.php b/app/controllers/Posts.php new file mode 100644 index 0000000..2aa80e4 --- /dev/null +++ b/app/controllers/Posts.php @@ -0,0 +1,222 @@ +postModel = $this->model('Post'); + $this->userModel = $this->model('User'); + } + + /** + * Index Method + * + * Displays a list of posts. + */ + public function index() { + // Get all posts + $posts = $this->postModel->getPosts(); + + // Data for the view + $data = [ + 'posts' => $posts, + ]; + + // Load the view with the specified data + $this->view('/posts/index', $data); + } + + /** + * Show Method + * + * Displays a single post. + * + * @param int $id Post ID + */ + public function show($id) { + // Get post and user information + $post = $this->postModel->getPost($id); + $user = $this->userModel->getUserById($post->user_id); + + // Data for the view + $data = [ + 'post' => $post, + 'user' => $user, + ]; + + // Load the view with the specified data + $this->view('/posts/show', $data); + } + + /** + * Add Method + * + * Adds a new post. + */ + public function add() { + // Check if the form is submitted + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Sanitize input data + $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS); + + // Extract form data + $data = [ + 'title' => trim($_POST['title']), + 'summary' => trim($_POST['summary']), + 'body' => trim($_POST['body']), + 'userId' => $_SESSION['userId'], + 'titleErr' => '', + 'summaryErr' => '', + 'bodyErr' => '', + ]; + + // Validate form data + if (empty($data['title'])) { + $data['titleErr'] = 'Please enter a title.'; + } + + if (empty($data['summary'])) { + $data['summaryErr'] = 'Please enter a summary.'; + } + + if (empty($data['body'])) { + $data['bodyErr'] = 'Please enter a body.'; + } + + // Check for errors + if (empty($data['titleErr']) && empty($data['summaryErr']) && empty($data['bodyErr'])) { + // Add post to the database + if ($this->postModel->addPost($data)) { + message('post_message', 'Your post has been added.', 'alert alert-success'); + redirect('/posts/index'); + } else { + die('Something went wrong.'); + } + } else { + // Load the view with the specified data and errors + $this->view('/posts/add', $data); + } + } else { + // Display the form + $data = [ + 'title' => '', + 'summary' => '', + 'body' => '', + ]; + + // Load the view with the specified data + $this->view('/posts/add', $data); + } + } + + /** + * Edit Method + * + * Edits an existing post. + * + * @param int $id Post ID + */ + public function edit($id) { + // Check if the form is submitted + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Sanitize input data + $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS); + + // Extract form data + $data = [ + 'id' => $id, + 'title' => trim($_POST['title']), + 'summary' => trim($_POST['summary']), + 'body' => trim($_POST['body']), + 'userId' => $_SESSION['userId'], + 'titleErr' => '', + 'summaryErr' => '', + 'bodyErr' => '', + ]; + + // Validate form data + if (empty($data['title'])) { + $data['titleErr'] = 'Please enter a title.'; + } + + if (empty($data['summary'])) { + $data['summaryErr'] = 'Please enter a summary.'; + } + + if (empty($data['body'])) { + $data['bodyErr'] = 'Please enter a body.'; + } + + // Check for errors + if (empty($data['titleErr']) && empty($data['summaryErr']) && empty($data['bodyErr'])) { + // Update post in the database + if ($this->postModel->updatePost($data)) { + message('post_message', 'Your post has been updated.', 'alert alert-success'); + redirect('/posts/index'); + } else { + die('Something went wrong.'); + } + } else { + // Load the view with the specified data and errors + $this->view('/posts/edit', $data); + } + } else { + // Display the form with existing post data + $post = $this->postModel->getPost($id); + + // Check if the user is authorized to edit the post + if ($post->user_id != $_SESSION['userId']) { + redirect('/posts/index'); + } + + // Data for the view + $data = [ + 'id' => $id, + 'title' => $post->title, + 'summary' => $post->summary, + 'body' => $post->body, + ]; + + // Load the view with the specified data + $this->view('/posts/edit', $data); + } + } + + /** + * Delete Method + * + * Deletes a post. + * + * @param int $id Post ID + */ + public function delete($id) { + // Check if the form is submitted + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Delete post from the database + if ($this->postModel->deletePost($id)) { + message('post_message', 'Your post has been removed.'); + redirect('/posts/index'); + } else { + die('Something went wrong'); + } + } else { + // Redirect to the posts index page if form is not submitted + redirect('/posts/index'); + } + } +} diff --git a/app/controllers/Users.php b/app/controllers/Users.php new file mode 100644 index 0000000..d86a1d9 --- /dev/null +++ b/app/controllers/Users.php @@ -0,0 +1,204 @@ +userModel = $this->model('User'); + } + + /** + * Register Method + * + * Handles user registration. + */ + public function register() { + // Check if the form is submitted + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Sanitize input data + $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS); + + // Extract form data + $data = [ + 'username' => trim($_POST['username']), + 'email' => trim($_POST['email']), + 'password' => trim($_POST['password']), + 'again' => trim($_POST['again']), + 'usernameErr' => '', + 'emailErr' => '', + 'passErr' => '', + 'againErr' => '', + ]; + + // Validate form data + if (empty($data['email'])) { + $data['emailErr'] = 'Please provide an email.'; + } else { + if ($this->userModel->findUserByEmail($data['email'])) { + $data['emailErr'] = 'Email exists.'; + } + } + + if (empty($data['username'])) { + $data['usernameErr'] = 'Please provide a valid username.'; + } + + if (empty($data['password'])) { + $data['passErr'] = 'Please provide a valid password.'; + } elseif (strlen($data['password']) < 6) { + $data['passErr'] = 'Please provide a password 6 or more characters in length.'; + } + + if (empty($data['again'])) { + $data['againErr'] = 'Please confirm your password.'; + } else { + if ($data['password'] != $data['again']) { + $data['passErr'] = 'Passwords do not match.'; + } + } + + // Check for errors + if (empty($data['usernameErr']) && empty($data['emailErr']) && + empty($data['passErr']) && empty($data['againErr'])) { + + // Hash the password + $data['password'] = password_hash($data['password'], PASSWORD_BCRYPT); + + // Register the user + if ($this->userModel->register($data)) { + message('reg_success', 'You are now registered and can login.', 'alert alert-success'); + redirect('/users/login'); + } else { + die('Something went wrong.'); + } + } else { + // Load the view with the specified data and errors + $this->view('users/register', $data); + } + + } else { + // Display the registration form + $data = [ + 'username' => '', + 'email' => '', + 'password' => '', + 'again' => '', + 'usernameErr' => '', + 'emailErr' => '', + 'passErr' => '', + 'againErr' => '', + ]; + + // Load the view with the specified data + $this->view('users/register', $data); + } + } + + /** + * Login Method + * + * Handles user login. + */ + public function login() { + // Check if the form is submitted + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Sanitize input data + $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS); + + // Extract form data + $data = [ + 'email' => trim($_POST['email']), + 'password' => trim($_POST['password']), + 'emailErr' => '', + 'passErr' => '', + ]; + + // Validate form data + if (empty($data['email'])) { + $data['emailErr'] = 'Please provide an email.'; + } + + if (empty($data['password'])) { + $data['passErr'] = 'Please provide a valid password.'; + } elseif (strlen($data['password']) < 6) { + $data['passErr'] = 'Please provide a password 6 or more characters in length.'; + } + + // Check if the user exists + if ($this->userModel->findUserByEmail($data['email'])) { + + } else { + $data['emailErr'] = 'No user found.'; + } + + // Check for errors + if (empty($data['emailErr']) && empty($data['passErr'])) { + // Attempt to log in the user + $loggedIn = $this->userModel->login($data['email'], $data['password']); + + if ($loggedIn) { + $this->createUserSession($loggedIn); + } else { + $data['passErr'] = 'Password was incorrect.'; + } + } else { + // Load the view with the specified data and errors + $this->view('users/login', $data); + } + + } else { + // Display the login form + $data = [ + // 'username' => '', + 'email' => '', + 'password' => '', + // 'usernameErr' => '', + 'emailErr' => '', + 'passErr' => '', + ]; + + // Load the view with the specified data + $this->view('users/login', $data); + } + } + + /** + * createUserSession Method + * + * Creates a user session upon successful login. + * + * @param object $user User object + */ + public function createUserSession($user) { + // Set session variables + $_SESSION['userId'] = $user->id; + $_SESSION['username'] = $user->name; + $_SESSION['userEmail'] = $user->email; + + // Redirect to the posts index page + redirect('/posts/index'); + } + + /** + * Logout Method + * + * Logs out the user. + */ + public function logout() { + // Unset session variables + unset($_SESSION['userId']); + unset($_SESSION['username']); + unset($_SESSION['userEmail']); + + // Redirect to the login page + redirect('/users/login'); + } +} diff --git a/app/helpers/sessions.php b/app/helpers/sessions.php new file mode 100644 index 0000000..3a9b00f --- /dev/null +++ b/app/helpers/sessions.php @@ -0,0 +1,57 @@ +' . $_SESSION[$name] . ''; + + // Clear the session variables + unset($_SESSION[$name]); + unset($_SESSION[$name . '_class']); + } + } +} + +/** + * Is Logged In Function + * + * Check if a user is logged in. + * + * @return bool Returns true if the user is logged in, false otherwise. + */ +function isLoggedIn() { + if (isset($_SESSION['userId'])) { + return true; + } else { + return false; + } +} diff --git a/app/helpers/url.php b/app/helpers/url.php new file mode 100644 index 0000000..b2c6449 --- /dev/null +++ b/app/helpers/url.php @@ -0,0 +1,12 @@ +getUrl(); + + // Look in controllers for the first value + if (!empty($url[0])) { + if (file_exists('../app/controllers/' . ucwords($url[0]) . '.php')) { + // If exists, set as controller + $this->currentController = ucwords($url[0]); + // Unset 0 Index + unset($url[0]); + } + } + + // Require the controller + require_once '../app/controllers/' . $this->currentController . '.php'; + + // Instantiate the controller class + $this->currentController = new $this->currentController; + + // Check for the second part of the URL + if (isset($url[1])) { + // Check to see if the method exists in the controller + if (method_exists($this->currentController, $url[1])) { + $this->currentMethod = $url[1]; + // Unset 1 index + unset($url[1]); + } + } + + // Get params + $this->params = $url ? array_values($url) : []; + + // Call a callback with an array of params + call_user_func_array([$this->currentController, $this->currentMethod], $this->params); + } + + /** + * Get URL Method + * + * Gets the URL and prepares it for processing. + * + * @return array An array representing the URL + */ + public function getUrl() { + if (isset($_GET['url'])) { + $url = rtrim($_GET['url'], '/'); + $url = filter_var($url, FILTER_SANITIZE_URL); + $url = explode('/', $url); + return $url; + } + } +} diff --git a/app/libraries/Database.php b/app/libraries/Database.php new file mode 100644 index 0000000..aeffc61 --- /dev/null +++ b/app/libraries/Database.php @@ -0,0 +1,123 @@ +host . ';dbname=' . $this->dbname; + $options = array( + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + + // Create PDO instance + try { + $this->dbh = new PDO($dsn, $this->user, $this->pass, $options); + } catch (PDOException $e) { + $this->error = $e->getMessage(); + echo $this->error; + } + } + + /** + * Query Method + * + * Prepares a statement with the provided SQL query. + * + * @param string $sql The SQL query + */ + public function query($sql) { + $this->stmt = $this->dbh->prepare($sql); + } + + /** + * Bind Method + * + * Binds a value to a parameter in the prepared statement. + * + * @param mixed $param The parameter to bind + * @param mixed $value The value to bind + * @param string $type The data type (default: null) + */ + public function bind($param, $value, $type = null) { + if (is_null($type)) { + switch (true) { + case is_int($value): + $type = PDO::PARAM_INT; + break; + case is_bool($value): + $type = PDO::PARAM_BOOL; + break; + case is_null($value): + $type = PDO::PARAM_NULL; + break; + default: + $type = PDO::PARAM_STR; + } + } + + $this->stmt->bindValue($param, $value, $type); + } + + /** + * Execute Method + * + * Executes the prepared statement. + * + * @return bool Returns true on success, false on failure + */ + public function execute() { + return $this->stmt->execute(); + } + + /** + * ResultSet Method + * + * Gets the result set as an array of objects. + * + * @return array An array of objects representing the result set + */ + public function resultSet() { + $this->execute(); + return $this->stmt->fetchAll(PDO::FETCH_OBJ); + } + + /** + * Single Method + * + * Gets a single record as an object. + * + * @return object An object representing a single record + */ + public function single() { + $this->execute(); + return $this->stmt->fetch(PDO::FETCH_OBJ); + } + + /** + * RowCount Method + * + * Gets the row count of the result set. + * + * @return int The number of rows affected by the last SQL statement + */ + public function rowCount() { + return $this->stmt->rowCount(); + } +} diff --git a/app/models/Post.php b/app/models/Post.php new file mode 100644 index 0000000..d401f5b --- /dev/null +++ b/app/models/Post.php @@ -0,0 +1,117 @@ +db = new Database; + } + + /** + * GetPosts Method + * + * Fetches all posts from the database. + * + * @return array An array of post objects + */ + public function getPosts() { + $this->db->query('SELECT *, posts.id as postId, + users.id as userId, + posts.created_at as postCreated, + users.created_at as userCreated + FROM posts INNER JOIN users ON posts.user_id = users.id ORDER BY posts.created_at DESC'); + + $res = $this->db->resultSet(); + + return $res; + } + + /** + * GetPost Method + * + * Fetches a single post by its ID. + * + * @param int $id The ID of the post + * @return object An object representing a single post + */ + public function getPost($id) { + $this->db->query('SELECT * FROM posts WHERE id = :id'); + $this->db->bind(':id', $id); + + $row = $this->db->single(); + + return $row; + } + + /** + * AddPost Method + * + * Inserts a new post into the database. + * + * @param array $data An associative array containing post data + * @return bool Returns true on success, false on failure + */ + public function addPost($data) { + $this->db->query('INSERT INTO posts (user_id, title, summary, body) VALUES(:userId, :title, :summary, :body)'); + + $this->db->bind(':userId', $data['userId']); + $this->db->bind(':title', $data['title']); + $this->db->bind(':summary', $data['summary']); + $this->db->bind(':body', $data['body']); + + if ($this->db->execute()) { + return true; + } else { + return false; + } + } + + /** + * UpdatePost Method + * + * Updates an existing post in the database. + * + * @param array $data An associative array containing post data + * @return bool Returns true on success, false on failure + */ + public function updatePost($data) { + $this->db->query('UPDATE posts SET title=:title, summary=:summary, body=:body WHERE id=:id'); + + $this->db->bind(':id', $data['id']); + $this->db->bind(':title', $data['title']); + $this->db->bind(':summary', $data['summary']); + $this->db->bind(':body', $data['body']); + + if ($this->db->execute()) { + return true; + } else { + return false; + } + } + + /** + * DeletePost Method + * + * Deletes a post from the database by its ID. + * + * @param int $id The ID of the post + * @return bool Returns true on success, false on failure + */ + public function deletePost($id) { + $this->db->query('DELETE FROM posts WHERE id = :id'); + $this->db->bind(':id', $id); + + if ($this->db->execute()) { + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/app/models/User.php b/app/models/User.php new file mode 100644 index 0000000..1fed2d4 --- /dev/null +++ b/app/models/User.php @@ -0,0 +1,99 @@ +db = new Database; + } + + /** + * GetUserById Method + * + * Fetches a user by their ID. + * + * @param int $id The ID of the user + * @return object An object representing the user + */ + public function getUserById($id) { + $this->db->query('SELECT * FROM users WHERE id = :id'); + $this->db->bind(':id', $id); + + $row = $this->db->single(); + + return $row; + } + + /** + * FindUserByEmail Method + * + * Checks if a user with the given email exists. + * + * @param string $email The email of the user + * @return bool Returns true if the user exists, false otherwise + */ + public function findUserByEmail($email) { + $this->db->query('SELECT * FROM users WHERE email = :email'); + $this->db->bind(':email', $email); + + $row = $this->db->single(); + + if ($this->db->rowCount() > 0) { + return true; + } else { + return false; + } + } + + /** + * Register Method + * + * Registers a new user in the database. + * + * @param array $data An associative array containing user data + * @return bool Returns true on success, false on failure + */ + public function register($data) { + $this->db->query('INSERT INTO users (name, email, password) VALUES(:name, :email, :password)'); + + $this->db->bind(':name', $data['username']); + $this->db->bind(':email', $data['email']); + $this->db->bind(':password', $data['password']); + + if ($this->db->execute()) { + return true; + } else { + return false; + } + } + + /** + * Login Method + * + * Performs user login based on email and password. + * + * @param string $email The email of the user + * @param string $password The password of the user + * @return mixed Returns the user object on success, false on failure + */ + public function login($email, $password) { + $this->db->query('SELECT * FROM users WHERE email = :email'); + $this->db->bind(':email', $email); + + $row = $this->db->single(); + + $hashedPassword = $row->password; + if (password_verify($password, $hashedPassword)) { + return $row; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/app/views/inc/footer.php b/app/views/inc/footer.php new file mode 100644 index 0000000..bfbaafa --- /dev/null +++ b/app/views/inc/footer.php @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/views/inc/header.php b/app/views/inc/header.php new file mode 100644 index 0000000..dc82a6b --- /dev/null +++ b/app/views/inc/header.php @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + <?php echo SITENAME; ?> + + + + + + + + + +
\ No newline at end of file diff --git a/app/views/inc/navbar.php b/app/views/inc/navbar.php new file mode 100644 index 0000000..8df4b8c --- /dev/null +++ b/app/views/inc/navbar.php @@ -0,0 +1,53 @@ + \ No newline at end of file diff --git a/app/views/pages/about.php b/app/views/pages/about.php new file mode 100644 index 0000000..46748f8 --- /dev/null +++ b/app/views/pages/about.php @@ -0,0 +1,9 @@ + +
+
+

+

+

Version:

+
+
+ diff --git a/app/views/pages/index.php b/app/views/pages/index.php new file mode 100644 index 0000000..ac81bc1 --- /dev/null +++ b/app/views/pages/index.php @@ -0,0 +1,8 @@ + +
+
+

+

+
+
+ diff --git a/app/views/posts/add.php b/app/views/posts/add.php new file mode 100644 index 0000000..853e1d7 --- /dev/null +++ b/app/views/posts/add.php @@ -0,0 +1,28 @@ + + Back +
+

Add Post

+

Create a new post.

+
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + \ No newline at end of file diff --git a/app/views/posts/edit.php b/app/views/posts/edit.php new file mode 100644 index 0000000..52afec8 --- /dev/null +++ b/app/views/posts/edit.php @@ -0,0 +1,28 @@ + + Back +
+

Edit Post

+

Create a new post.

+
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + \ No newline at end of file diff --git a/app/views/posts/index.php b/app/views/posts/index.php new file mode 100644 index 0000000..437139f --- /dev/null +++ b/app/views/posts/index.php @@ -0,0 +1,23 @@ + + +
+
+

Posts

+
+ +
+ +
+

title ?>

+
+ written by: name ?> on postCreated ?> +
+

summary ?>

+ Read More +
+ + diff --git a/app/views/posts/show.php b/app/views/posts/show.php new file mode 100644 index 0000000..68a7181 --- /dev/null +++ b/app/views/posts/show.php @@ -0,0 +1,21 @@ + + + Back +
+

title ?>

+
+ Written by name ?> on created_at ?>
+
+ +

body ?>

+ + user_id == $_SESSION['userId']) : ?> +
+ Edit Post + +
+ +
+ + + \ No newline at end of file diff --git a/app/views/users/login.php b/app/views/users/login.php new file mode 100644 index 0000000..6cb5154 --- /dev/null +++ b/app/views/users/login.php @@ -0,0 +1,35 @@ + + +
+
+
+ +

Login to your Account

+

Fill in your information to continue.

+
+
+ + + +
+ +
+ + + +
+ +
+
+ +
+
+ Register +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/app/views/users/register.php b/app/views/users/register.php new file mode 100644 index 0000000..8dd6747 --- /dev/null +++ b/app/views/users/register.php @@ -0,0 +1,42 @@ + + +
+
+
+

Create an Account

+

Fill out to Register

+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..41d6ab0 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,8 @@ + + Options -Multiviews + RewriteEngine On + RewriteBase / + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] + \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..db6d1a4 --- /dev/null +++ b/public/index.php @@ -0,0 +1,5 @@ +