├── .gitignore
├── .htaccess
├── .scrutinizer.yml
├── LICENSE
├── README.md
├── _installation
├── 01 database.sql
├── 02 tables.sql
├── 03 triggers.sql
└── 04 users.sql
├── app
├── .htaccess
├── backups
│ └── .gitignore
├── config
│ ├── config.php
│ └── javascript.php
├── controllers
│ ├── AdminController.php
│ ├── CommentsController.php
│ ├── DownloadsController.php
│ ├── ErrorsController.php
│ ├── FilesController.php
│ ├── LoginController.php
│ ├── NewsFeedController.php
│ ├── PostsController.php
│ ├── TodoController.php
│ └── UserController.php
├── core
│ ├── App.php
│ ├── Component.php
│ ├── Config.php
│ ├── Controller.php
│ ├── Cookie.php
│ ├── Email.php
│ ├── Encryption.php
│ ├── Environment.php
│ ├── Handler.php
│ ├── Logger.php
│ ├── Redirector.php
│ ├── Request.php
│ ├── Response.php
│ ├── Session.php
│ ├── View.php
│ └── components
│ │ ├── AuthComponent.php
│ │ └── SecurityComponent.php
├── logs
│ └── log.txt
├── models
│ ├── Admin.php
│ ├── Comment.php
│ ├── Database.php
│ ├── File.php
│ ├── Login.php
│ ├── Model.php
│ ├── NewsFeed.php
│ ├── Pagination.php
│ ├── Permission.php
│ ├── Post.php
│ ├── Todo.php
│ ├── Uploader.php
│ ├── User.php
│ └── Validation.php
├── uploads
│ └── .gitignore
├── utility
│ └── Utility.php
└── views
│ ├── admin
│ ├── backups.php
│ └── users
│ │ ├── index.php
│ │ ├── users.php
│ │ └── viewUser.php
│ ├── alerts
│ ├── errors.php
│ └── success.php
│ ├── bugs
│ └── index.php
│ ├── dashboard
│ ├── index.php
│ └── updates.php
│ ├── errors
│ ├── 400.php
│ ├── 401.php
│ ├── 403.php
│ ├── 404.php
│ └── 500.php
│ ├── files
│ ├── files.php
│ └── index.php
│ ├── layout
│ ├── default
│ │ ├── footer.php
│ │ ├── header.php
│ │ └── navigation.php
│ ├── errors
│ │ ├── footer.php
│ │ └── header.php
│ ├── login
│ │ ├── footer.php
│ │ └── header.php
│ └── todo
│ │ ├── footer.php
│ │ └── header.php
│ ├── login
│ ├── index.php
│ ├── passwordUpdated.php
│ ├── updatePassword.php
│ └── userVerified.php
│ ├── newsfeed
│ ├── index.php
│ ├── newsfeed.php
│ └── updateForm.php
│ ├── pagination
│ ├── comments.php
│ └── default.php
│ ├── posts
│ ├── commentUpdateForm.php
│ ├── comments.php
│ ├── index.php
│ ├── newPost.php
│ ├── post.php
│ ├── postUpdateForm.php
│ ├── posts.php
│ └── viewPost.php
│ ├── todo
│ └── index.php
│ └── user
│ └── profile.php
├── composer.json
└── public
├── .htaccess
├── css
├── bootstrap.min.css
├── font-awesome.min.css
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ └── fontawesome-webfont.woff
└── sb-admin-2.css
├── img
├── backgrounds
│ └── background.png
├── icons
│ ├── favicon.ico
│ ├── favicon.png
│ ├── icon114.png
│ ├── icon120.png
│ ├── icon144.png
│ ├── icon152.png
│ ├── icon57.png
│ ├── icon72.png
│ └── icon76.png
└── profile_pictures
│ ├── author.jpg
│ └── default.png
├── index.php
└── js
├── bootstrap.min.js
├── jquery.min.js
├── main.js
└── sb-admin-2.js
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | # Disable server signature
2 | ServerSignature Off
3 |
4 |
5 | RewriteEngine on
6 | RewriteRule ^$ public/ [L]
7 | RewriteRule (.*) public/$1 [L]
8 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | checks:
2 | php:
3 | code_rating: true
4 | duplication: true
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Omar El Gabry
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/_installation/01 database.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- Create database
3 | --
4 |
5 | CREATE DATABASE IF NOT EXISTS miniphp_68dozftn CHARACTER SET utf8 COLLATE utf8_general_ci
6 |
--------------------------------------------------------------------------------
/_installation/02 tables.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- Table structure for table `users`
3 | --
4 |
5 | CREATE TABLE `users` (
6 | `id` int(11) NOT NULL AUTO_INCREMENT,
7 | `session_id` varchar(48) DEFAULT NULL,
8 | `cookie_token` varchar(128) DEFAULT NULL,
9 | `name` varchar(48) NOT NULL,
10 | `role` varchar(16) NOT NULL DEFAULT 'user',
11 | `hashed_password` varchar(128) NOT NULL,
12 | `email` varchar(64) NOT NULL,
13 | `is_email_activated` tinyint(1) NOT NULL DEFAULT '0',
14 | `email_token` varchar(48) DEFAULT NULL,
15 | `email_last_verification` int(11) DEFAULT NULL COMMENT 'unix timestamp',
16 | `pending_email` varchar(64) DEFAULT NULL COMMENT 'temporary email that will be used when user updates his current one',
17 | `pending_email_token` varchar(48) DEFAULT NULL,
18 | `profile_picture` varchar(48) NOT NULL DEFAULT 'default.png' COMMENT 'The base name for the image. Its not always unique because of default.jpg',
19 | PRIMARY KEY (`id`),
20 | UNIQUE KEY `email` (`email`)
21 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
22 |
23 | --
24 | -- Table structure for table `failed_logins`
25 | --
26 |
27 | CREATE TABLE `failed_logins` (
28 | `id` int(11) NOT NULL AUTO_INCREMENT,
29 | `user_email` varchar(64) NOT NULL COMMENT 'It doesnt reference email in table users, this will prevent even unregistered users as well',
30 | `last_failed_login` int(11) DEFAULT NULL COMMENT 'unix timestamp of last failed login',
31 | `failed_login_attempts` int(11) NOT NULL DEFAULT '0',
32 | PRIMARY KEY (`id`),
33 | UNIQUE KEY `user_email` (`user_email`)
34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
35 |
36 | --
37 | -- Table structure for table `ip_failed_logins`
38 | --
39 |
40 | CREATE TABLE `ip_failed_logins` (
41 | `ip` varchar(48) NOT NULL,
42 | `user_email` varchar(64) NOT NULL COMMENT 'It doesnt reference email in table users',
43 | PRIMARY KEY (`ip`,`user_email`)
44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
45 |
46 | --
47 | -- Table structure for table `blocked_ips`
48 | --
49 |
50 | CREATE TABLE `blocked_ips` (
51 | `ip` varchar(48) NOT NULL,
52 | PRIMARY KEY (`ip`)
53 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
54 |
55 | --
56 | -- Table structure for table `forgotten_passwords`
57 | --
58 |
59 | CREATE TABLE `forgotten_passwords` (
60 | `id` int(11) NOT NULL AUTO_INCREMENT,
61 | `user_id` int(11) NOT NULL,
62 | `password_token` varchar(48) DEFAULT NULL,
63 | `password_last_reset` int(11) DEFAULT NULL COMMENT 'unix timestamp of last password reset request',
64 | `forgotten_password_attempts` int(11) NOT NULL DEFAULT '0',
65 | PRIMARY KEY (`id`),
66 | UNIQUE KEY `forgotten_passwords_user` (`user_id`),
67 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
68 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
69 |
70 |
71 | --
72 | -- Table structure for table `posts`
73 | --
74 |
75 | CREATE TABLE `posts` (
76 | `id` int(11) NOT NULL AUTO_INCREMENT,
77 | `user_id` int(11) NOT NULL,
78 | `title` varchar(128) NOT NULL,
79 | `content` text NOT NULL,
80 | `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
81 | PRIMARY KEY (`id`),
82 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
83 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
84 |
85 | --
86 | -- Table structure for table `comments`
87 | --
88 |
89 | CREATE TABLE `comments` (
90 | `id` int(11) NOT NULL AUTO_INCREMENT,
91 | `user_id` int(11) NOT NULL,
92 | `post_id` int(11) NOT NULL,
93 | `content` varchar(512) NOT NULL,
94 | `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
95 | PRIMARY KEY (`id`),
96 | FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
97 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
98 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
99 |
100 | --
101 | -- Table structure for table `newsfeed`
102 | --
103 |
104 | CREATE TABLE `newsfeed` (
105 | `id` int(11) NOT NULL AUTO_INCREMENT,
106 | `user_id` int(11) NOT NULL,
107 | `content` varchar(512) NOT NULL,
108 | `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
109 | PRIMARY KEY (`id`),
110 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
111 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
112 |
113 | --
114 | -- Table structure for table `files`
115 | --
116 |
117 | CREATE TABLE `files` (
118 | `id` int(11) NOT NULL AUTO_INCREMENT,
119 | `user_id` int(11) NOT NULL,
120 | `filename` varchar(48) NOT NULL COMMENT 'original file name',
121 | `hashed_filename` varchar(48) NOT NULL COMMENT 'The hashed file name generated from hash(filename . extension)',
122 | `extension` varchar(8) NOT NULL COMMENT 'The file extension',
123 | `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
124 | PRIMARY KEY (`id`),
125 | UNIQUE KEY `hashed_filename` (`hashed_filename`),
126 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
127 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
128 |
129 | --
130 | -- Table structure for table `notifications`
131 | --
132 |
133 | CREATE TABLE `notifications` (
134 | `user_id` int(11) NOT NULL,
135 | `target` varchar(16) NOT NULL COMMENT 'Represents the target of the notification, like files, posts, ...etc',
136 | `count` int(11) NOT NULL DEFAULT '0',
137 | PRIMARY KEY (`user_id`,`target`),
138 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
139 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
140 |
141 | --
142 | -- Table structure for table `todo`
143 | --
144 |
145 | CREATE TABLE `todo` (
146 | `id` int(11) NOT NULL AUTO_INCREMENT,
147 | `user_id` int(11) NOT NULL,
148 | `content` varchar(512) NOT NULL,
149 | PRIMARY KEY (`id`),
150 | FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
151 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
--------------------------------------------------------------------------------
/_installation/03 triggers.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- Triggers `users`
3 | --
4 | DROP TRIGGER IF EXISTS `initialize_notifications`;
5 | DELIMITER //
6 | CREATE TRIGGER `initialize_notifications` AFTER INSERT ON `users`
7 | FOR EACH ROW INSERT INTO notifications (user_id, target, count) VALUES (NEW.id, "newsfeed", 0), (NEW.id, "files", 0), (NEW.id, "posts", 0)
8 | //
9 | DELIMITER ;
10 |
11 | --
12 | -- Triggers `posts`
13 | --
14 | DROP TRIGGER IF EXISTS `increment_notifications_posts`;
15 | DELIMITER //
16 | CREATE TRIGGER `increment_notifications_posts` AFTER INSERT ON `posts`
17 | FOR EACH ROW UPDATE notifications SET count = count+1
18 |
19 | WHERE notifications.user_id != NEW.user_id
20 | AND notifications.target = "posts"
21 | //
22 | DELIMITER ;
23 |
24 | --
25 | -- Triggers `newsfeed`
26 | --
27 | DROP TRIGGER IF EXISTS `increment_notifications_newsfeed`;
28 | DELIMITER //
29 | CREATE TRIGGER `increment_notifications_newsfeed` AFTER INSERT ON `newsfeed`
30 | FOR EACH ROW UPDATE notifications SET count = count+1
31 |
32 | WHERE notifications.user_id != NEW.user_id
33 | AND notifications.target = "newsfeed"
34 | //
35 | DELIMITER ;
36 |
37 | --
38 | -- Triggers `files`
39 | --
40 | DROP TRIGGER IF EXISTS `increment_notifications_files`;
41 | DELIMITER //
42 | CREATE TRIGGER `increment_notifications_files` AFTER INSERT ON `files`
43 | FOR EACH ROW UPDATE notifications SET count = count+1
44 |
45 | WHERE notifications.user_id != NEW.user_id
46 | AND notifications.target = "files"
47 | //
48 | DELIMITER ;
49 |
50 |
--------------------------------------------------------------------------------
/_installation/04 users.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- Add initial admin and normal user
3 | --
4 |
5 | INSERT INTO `miniphp_68dozftn`.`users` (`id`, `session_id`, `cookie_token`, `name`, `role`, `hashed_password`, `email`, `is_email_activated`, `email_token`, `email_last_verification`, `profile_picture`) VALUES ('1', NULL, NULL, 'Anna Collier', 'admin', '$2y$10$oomRp.tNyq2sG/3YE3jtMO3lyCzBwI3dxWxEsz956a7Cherfp7h4K', 'admin@demo.com', '1', NULL, NULL, 'default.png'),
6 | ('2', NULL, NULL, 'Evans Fuller', 'user', '$2y$10$CUiHm3w/waT3xlY0mJBm/uYNbOQnHcXYaZifekyUGxJhCxDdjWsV6', 'user@demo.com', '1', NULL, NULL, 'default.png');
7 |
--------------------------------------------------------------------------------
/app/.htaccess:
--------------------------------------------------------------------------------
1 | deny from all
--------------------------------------------------------------------------------
/app/backups/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
--------------------------------------------------------------------------------
/app/config/config.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | return array(
12 |
13 | /**
14 | * Configuration for: Database Connection
15 | * Define database constants to establish a connection.
16 | *
17 | * It's important to set the charset for the database
18 | *
19 | * In the real world, you will have a database user with limited privileges(usually only CRUD).
20 | *
21 | */
22 | "DB_HOST" =>"localhost",
23 | "DB_NAME" => "miniphp_68dozftn",
24 | "DB_USER" => "root",
25 | "DB_PASS" => "",
26 | 'DB_CHARSET' => 'utf8',
27 |
28 | /**
29 | * Configuration for: Paths
30 | * Paths from views directory
31 | */
32 | "VIEWS_PATH" => APP . "views/",
33 | "ERRORS_PATH" => APP . "views/errors/",
34 | "LOGIN_PATH" => APP . "views/login/",
35 | "ADMIN_VIEWS_PATH" => APP . "views/admin/",
36 |
37 | /**
38 | * Configuration for: Cookies
39 | *
40 | * COOKIE_RUNTIME: How long should a cookie be valid by seconds.
41 | * - 1209600 means 2 weeks
42 | * - 604800 means 1 week
43 | * COOKIE_DOMAIN: The domain where the cookie is valid for.
44 | * COOKIE_DOMAIN mightn't work with "localhost", ".localhost", "127.0.0.1", or ".127.0.0.1". If so, leave it as empty string, false or null.
45 | * @see http://stackoverflow.com/questions/1134290/cookies-on-localhost-with-explicit-domain
46 | * @see http://php.net/manual/en/function.setcookie.php#73107
47 | *
48 | * COOKIE_PATH: The path where the cookie is valid for. If set to '/', the cookie will be available within the entire COOKIE_DOMAIN.
49 | * COOKIE_SECURE: If the cookie will be transferred through secured connection(SSL). It's highly recommended to set it to true if you have secured connection
50 | * COOKIE_HTTP: If set to true, Cookies that can't be accessed by JS - Highly recommended!
51 | * COOKIE_SECRET_KEY: A random value to make the cookie more secure.
52 | *
53 | */
54 | "COOKIE_EXPIRY" => 1209600,
55 | "SESSION_COOKIE_EXPIRY" => 604800,
56 | "COOKIE_DOMAIN" => '',
57 | "COOKIE_PATH" => '/',
58 | "COOKIE_SECURE" => false,
59 | "COOKIE_HTTP" => true,
60 | "COOKIE_SECRET_KEY" => "af&70-GF^!a{f64r5@g38l]#kQ4B+43%",
61 |
62 | /**
63 | * Configuration for: Encryption Keys
64 | *
65 | */
66 | "ENCRYPTION_KEY" => "3¥‹a0cd@!$251Êìcef08%&",
67 | "HMAC_SALT" => "a8C7n7^Ed0%8Qfd9K4m6d$86Dab",
68 | "HASH_KEY" => "z4D8Mp7Jm5cH",
69 |
70 | /**
71 | * Configuration for: Email server credentials
72 | * Emails are sent using SMTP, Don't use built-in mail() function in PHP.
73 | *
74 | */
75 | "EMAIL_SMTP_DEBUG" => 2,
76 | "EMAIL_SMTP_AUTH" => true,
77 | "EMAIL_SMTP_SECURE" => "ssl",
78 | "EMAIL_SMTP_HOST" => "YOURSMTPHOST",
79 | "EMAIL_SMTP_PORT" => 465,
80 | "EMAIL_SMTP_USERNAME" => "YOURUSERNAME",
81 | "EMAIL_SMTP_PASSWORD" => "YOURPASSWORD",
82 | "EMAIL_FROM" => "info@YOURDOMAIN.com",
83 | "EMAIL_FROM_NAME" => "mini PHP",
84 | "EMAIL_REPLY_TO" => "no-reply@YOURDOMAIN.com",
85 | "ADMIN_EMAIL" => "YOUREMAIL",
86 |
87 | /**
88 | * Configuration for: Email Verification
89 | *
90 | * EMAIL_EMAIL_VERIFICATION_URL: Full URL must be provided
91 | *
92 | */
93 | "EMAIL_EMAIL_VERIFICATION" => "1",
94 | "EMAIL_EMAIL_VERIFICATION_URL" => PUBLIC_ROOT . "Login/verifyUser",
95 | "EMAIL_EMAIL_VERIFICATION_SUBJECT" => "[IMP] Please verify your account",
96 |
97 | /**
98 | * Configuration for: Revoke email change
99 | *
100 | * EMAIL_REVOKE_EMAIL_URL: Full URL must be provided
101 | *
102 | */
103 | "EMAIL_REVOKE_EMAIL" => "2",
104 | "EMAIL_REVOKE_EMAIL_URL" => PUBLIC_ROOT . "User/revokeEmail",
105 | "EMAIL_REVOKE_EMAIL_SUBJECT" => "[IMP] Your email has been changed",
106 |
107 | /**
108 | * Configuration for: Confirm pending(updated) email
109 | *
110 | * EMAIL_UPDATE_EMAIL_URL: Full URL must be provided
111 | *
112 | */
113 | "EMAIL_UPDATE_EMAIL" => "3",
114 | "EMAIL_UPDATE_EMAIL_URL" => PUBLIC_ROOT . "User/updateEmail",
115 | "EMAIL_UPDATE_EMAIL_SUBJECT" => "[IMP] Please confirm your new email",
116 |
117 | /**
118 | * Configuration for: Reset Password
119 | *
120 | * EMAIL_PASSWORD_RESET_URL: Full URL must be provided
121 | *
122 | */
123 | "EMAIL_PASSWORD_RESET" => "4",
124 | "EMAIL_PASSWORD_RESET_URL" => PUBLIC_ROOT . "Login/resetPassword",
125 | "EMAIL_PASSWORD_RESET_SUBJECT" => "[IMP] Reset your password",
126 |
127 | /**
128 | * Configuration for: Report Bug, Feature, or Enhancement
129 | */
130 | "EMAIL_REPORT_BUG" => "5",
131 | "EMAIL_REPORT_BUG_SUBJECT" => "Request",
132 |
133 | /**
134 | * Configuration for: Hashing strength
135 | *
136 | * It defines the strength of the password hashing/salting. "10" is the default value by PHP.
137 | * @see http://php.net/manual/en/function.password-hash.php
138 | *
139 | */
140 | "HASH_COST_FACTOR" => "10",
141 |
142 | /**
143 | * Configuration for: Pagination
144 | *
145 | */
146 | "PAGINATION_DEFAULT_LIMIT" => 10
147 |
148 | );
149 |
--------------------------------------------------------------------------------
/app/config/javascript.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | return array(
12 |
13 | 'root' => PUBLIC_ROOT, /* public root used in ajax calls and redirection from client-side */
14 | 'fileSizeOverflow' => 10485760 /* max file size, this is important to avoid overflow in files with big size */
15 | );
16 |
--------------------------------------------------------------------------------
/app/controllers/AdminController.php:
--------------------------------------------------------------------------------
1 |
8 | *
9 | */
10 |
11 | class AdminController extends Controller {
12 |
13 | /**
14 | * A method that will be triggered before calling action method.
15 | * Any changes here will reflect then on Controller::triggerComponents() method
16 | *
17 | */
18 | public function beforeAction(){
19 |
20 | parent::beforeAction();
21 |
22 | $action = $this->request->param('action');
23 | $actions = ['getUsers', 'updateUserInfo', 'deleteUser'];
24 |
25 | // define the action methods that needs to be triggered only through POST & Ajax request.
26 | $this->Security->requireAjax($actions);
27 | $this->Security->requirePost($actions);
28 |
29 | // You need to explicitly define the form fields that you expect to be returned in POST request,
30 | // if form field wasn't defined, this will detected as form tampering attempt.
31 | switch($action){
32 | case "getUsers":
33 | $this->Security->config("form", [ 'fields' => ['name', 'email', 'role', 'page']]);
34 | break;
35 | case "updateUserInfo":
36 | $this->Security->config("form", [ 'fields' => ['user_id', 'name', 'password', 'role']]);
37 | break;
38 | case "deleteUser":
39 | $this->Security->config("form", [ 'fields' => ['user_id']]);
40 | break;
41 | case "updateBackup":
42 | case "restoreBackup":
43 | $this->Security->config("validateCsrfToken", true);
44 | break;
45 | }
46 | }
47 |
48 | /**
49 | * show all users
50 | *
51 | */
52 | public function users(){
53 |
54 | Config::setJsConfig('curPage', "users");
55 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('ADMIN_VIEWS_PATH') . 'users/index.php');
56 | }
57 |
58 | /**
59 | * get users by name, email & role
60 | *
61 | */
62 | public function getUsers(){
63 |
64 | $name = $this->request->data("name");
65 | $email = $this->request->data("email");
66 | $role = $this->request->data("role");
67 | $pageNum = $this->request->data("page");
68 |
69 | $usersData = $this->admin->getUsers($name, $email, $role, $pageNum);
70 |
71 | if(!$usersData){
72 | $this->view->renderErrors($this->admin->errors());
73 | } else{
74 |
75 | $usersHTML = $this->view->render(Config::get('ADMIN_VIEWS_PATH') . 'users/users.php', array("users" => $usersData["users"]));
76 | $paginationHTML = $this->view->render(Config::get('VIEWS_PATH') . 'pagination/default.php', array("pagination" => $usersData["pagination"]));
77 | $this->view->renderJson(array("data" => ["users" => $usersHTML, "pagination" => $paginationHTML]));
78 | }
79 | }
80 |
81 | /**
82 | * view a user
83 | *
84 | * @param integer|string $userId
85 | */
86 | public function viewUser($userId = 0){
87 |
88 | $userId = Encryption::decryptId($userId);
89 |
90 | if(!$this->user->exists($userId)){
91 | return $this->error(404);
92 | }
93 |
94 | Config::setJsConfig('curPage', "users");
95 | Config::setJsConfig('userId', Encryption::encryptId($userId));
96 |
97 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('ADMIN_VIEWS_PATH') . 'users/viewUser.php', array("userId" => $userId));
98 | }
99 |
100 | /**
101 | * update user profile info(name, password, role)
102 | *
103 | */
104 | public function updateUserInfo(){
105 |
106 | $userId = Encryption::decryptId($this->request->data("user_id"));
107 | $name = $this->request->data("name");
108 | $password = $this->request->data("password");
109 | $role = $this->request->data("role");
110 |
111 | if(!$this->user->exists($userId)){
112 | return $this->error(404);
113 | }
114 |
115 | $result = $this->admin->updateUserInfo($userId, Session::getUserId(), $name, $password, $role);
116 |
117 | if(!$result){
118 | $this->view->renderErrors($this->admin->errors());
119 | }else{
120 | $this->view->renderSuccess("Profile has been updated.");
121 | }
122 | }
123 |
124 | /**
125 | * delete a user
126 | *
127 | */
128 | public function deleteUser(){
129 |
130 | $userId = Encryption::decryptIdWithDash($this->request->data("user_id"));
131 |
132 | if(!$this->user->exists($userId)){
133 | return $this->error(404);
134 | }
135 |
136 | $this->admin->deleteUser(Session::getUserId(), $userId);
137 | $this->view->renderJson(array("success" => true));
138 | }
139 |
140 | /**
141 | * view backups if exist
142 | *
143 | */
144 | public function backups(){
145 |
146 | Config::setJsConfig('curPage', "backups");
147 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('ADMIN_VIEWS_PATH') . 'backups.php');
148 | }
149 |
150 | /**
151 | * update backup
152 | *
153 | */
154 | public function updateBackup(){
155 |
156 | $this->admin->updateBackup();
157 |
158 | Session::set('backup-success', "Backup has been updated");
159 | return $this->redirector->root("Admin/Backups");
160 | }
161 |
162 | /**
163 | * restore backup
164 | *
165 | */
166 | public function restoreBackup(){
167 |
168 | $result = $this->admin->restoreBackup();
169 |
170 | if(!$result){
171 | Session::set('backup-errors', $this->admin->errors());
172 | return $this->redirector->root("Admin/Backups");
173 | }else{
174 | Session::set('backup-success', "Backup has been restored successfully");
175 | return $this->redirector->root("Admin/Backups");
176 | }
177 | }
178 |
179 | /**
180 | * Is user authorized for admin controller & requested action method?
181 | *
182 | * @return bool
183 | */
184 | public function isAuthorized(){
185 |
186 | $role = Session::getUserRole();
187 | if(isset($role) && $role === "admin"){
188 | return true;
189 | }
190 | return false;
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/app/controllers/CommentsController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class CommentsController extends Controller{
11 |
12 | public function beforeAction(){
13 |
14 | parent::beforeAction();
15 |
16 | $action = $this->request->param('action');
17 | $actions = ['getAll', 'create', 'getUpdateForm', 'update', 'getById', 'delete'];
18 | $this->Security->requireAjax($actions);
19 | $this->Security->requirePost($actions);
20 |
21 | switch($action){
22 | case "getAll":
23 | $this->Security->config("form", [ 'fields' => ['post_id', 'page', 'comments_created']]);
24 | break;
25 | case "create":
26 | $this->Security->config("form", [ 'fields' => ['post_id', 'content']]);
27 | break;
28 | case "getUpdateForm":
29 | $this->Security->config("form", [ 'fields' => ['comment_id']]);
30 | break;
31 | case "update":
32 | $this->Security->config("form", [ 'fields' => ['comment_id', 'content']]);
33 | break;
34 | case "getById":
35 | case "delete":
36 | $this->Security->config("form", [ 'fields' => ['comment_id']]);
37 | break;
38 | }
39 | }
40 |
41 | /**
42 | * get all comments
43 | *
44 | */
45 | public function getAll(){
46 |
47 | $postId = Encryption::decryptId($this->request->data("post_id"));
48 |
49 | $pageNum = $this->request->data("page");
50 | $commentsCreated = (int)$this->request->data("comments_created");
51 |
52 | $commentsData = $this->comment->getAll($postId, $pageNum, $commentsCreated);
53 |
54 | $commentsHTML = $this->view->render(Config::get('VIEWS_PATH') . 'posts/comments.php', array("comments" => $commentsData["comments"]));
55 | $paginationHTML = $this->view->render(Config::get('VIEWS_PATH') . 'pagination/comments.php', array("pagination" => $commentsData["pagination"]));
56 |
57 | $this->view->renderJson(array("data" => ["comments" => $commentsHTML, "pagination" => $paginationHTML]));
58 | }
59 |
60 | public function create(){
61 |
62 | $postId = Encryption::decryptId($this->request->data("post_id"));
63 |
64 | $content = $this->request->data("content");
65 |
66 | $comment = $this->comment->create(Session::getUserId(), $postId, $content);
67 |
68 | if(!$comment){
69 | $this->view->renderErrors($this->comment->errors());
70 | }else{
71 |
72 | $html = $this->view->render(Config::get('VIEWS_PATH') . 'posts/comments.php', array("comments" => $comment));
73 | $this->view->renderJson(array("data" => $html));
74 | }
75 | }
76 |
77 | /**
78 | * whenever the user hits 'edit' button,
79 | * a request will be sent to get the update form of that comment,
80 | * so that the user can 'update' or even 'cancel' the edit request.
81 | *
82 | */
83 | public function getUpdateForm(){
84 |
85 | $commentId = Encryption::decryptIdWithDash($this->request->data("comment_id"));
86 |
87 | if(!$this->comment->exists($commentId)){
88 | return $this->error(404);
89 | }
90 |
91 | $comment = $this->comment->getById($commentId);
92 |
93 | $commentsHTML = $this->view->render(Config::get('VIEWS_PATH') . 'posts/commentUpdateForm.php', array("comment" => $comment[0]));
94 | $this->view->renderJson(array("data" => $commentsHTML));
95 | }
96 |
97 | /**
98 | * update comment
99 | *
100 | */
101 | public function update(){
102 |
103 | $commentId = Encryption::decryptIdWithDash($this->request->data("comment_id"));
104 | $content = $this->request->data("content");
105 |
106 | if(!$this->comment->exists($commentId)){
107 | return $this->error(404);
108 | }
109 |
110 | $comment = $this->comment->update($commentId, $content);
111 |
112 | if(!$comment){
113 | $this->view->renderErrors($this->comment->errors());
114 | }else{
115 |
116 | $html = $this->view->render(Config::get('VIEWS_PATH') . 'posts/comments.php', array("comments" => $comment));
117 | $this->view->renderJson(array("data" => $html));
118 | }
119 | }
120 |
121 | /**
122 | * get comment by Id
123 | *
124 | */
125 | public function getById(){
126 |
127 | $commentId = Encryption::decryptIdWithDash($this->request->data("comment_id"));
128 |
129 | if(!$this->comment->exists($commentId)){
130 | return $this->error(404);
131 | }
132 |
133 | $comment = $this->comment->getById($commentId);
134 |
135 | $commentsHTML = $this->view->render(Config::get('VIEWS_PATH') . 'posts/comments.php', array("comments" => $comment));
136 | $this->view->renderJson(array("data" => $commentsHTML));
137 | }
138 |
139 | public function delete(){
140 |
141 | $commentId = Encryption::decryptIdWithDash($this->request->data("comment_id"));
142 |
143 | if(!$this->comment->exists($commentId)){
144 | return $this->error(404);
145 | }
146 |
147 | $this->comment->deleteById($commentId);
148 |
149 | $this->view->renderJson(array("success" => true));
150 | }
151 |
152 | public function isAuthorized(){
153 |
154 | $action = $this->request->param('action');
155 | $role = Session::getUserRole();
156 | $resource = "comments";
157 |
158 | // only for admins
159 | Permission::allow('admin', $resource, ['*']);
160 |
161 | // only for normal users
162 | Permission::allow('user', $resource, ['getAll', 'getById', 'create']);
163 | Permission::allow('user', $resource, ['update', 'delete', 'getUpdateForm'], 'owner');
164 |
165 | $commentId = $this->request->data("comment_id");
166 | if(!empty($commentId)){
167 | $commentId = Encryption::decryptIdWithDash($commentId);
168 | }
169 |
170 | $config = [
171 | "user_id" => Session::getUserId(),
172 | "table" => "comments",
173 | "id" => $commentId];
174 |
175 | return Permission::check($role, $resource, $action, $config);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/app/controllers/DownloadsController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | class DownloadsController extends Controller {
10 |
11 |
12 | public function beforeAction(){
13 |
14 | parent::beforeAction();
15 |
16 | $actions = ['download', 'users'];
17 | $this->Security->requireGet($actions);
18 |
19 | // if you want to add csrf_token in the URL of file download
20 | // So, it will be something like this: http://localhost/miniPHP/downloads/download/f850749b62bf3badfb6c0?csrf_token=21eb0f2c6b4fddce8a7f3
21 | // $this->Security->config("validateCsrfToken", true);
22 | }
23 |
24 | /**
25 | * download a file provided by it's hashed name
26 | * the url should be something like: http://localhost/miniPHP/downloads/download/f850749b62bf3ba57b6380b67c6f3096bcdfb6c0
27 | *
28 | * @param string $hashedFileName
29 | */
30 | public function download($hashedFileName = ''){
31 |
32 | $fullPath = APP . "uploads/" ;
33 | $file = $this->file->getByHashedName($hashedFileName);
34 |
35 | if(empty($file)){
36 | return $this->error(404);
37 | }
38 |
39 | $fullPath .= $hashedFileName . "." . $file["extension"];
40 | $file["basename"] = $file["filename"] . "." . $file["extension"];
41 |
42 | if(!Uploader::isFileExists($fullPath)){
43 | return $this->error(404);
44 | }
45 |
46 | $this->response->download($fullPath, ["basename" => $file["basename"], "extension" => $file["extension"]]);
47 | }
48 |
49 | /**
50 | * download users data as csv file
51 | *
52 | */
53 | public function users(){
54 |
55 | $data = $this->admin->getUsersData();
56 | $this->response->csv(["cols" => $data["cols"], "rows" => $data["rows"]], ["filename" => $data["filename"]]);
57 | }
58 |
59 | public function isAuthorized(){
60 |
61 | $action = $this->request->param('action');
62 | $role = Session::getUserRole();
63 | $resource = "downloads";
64 |
65 | //only for admin
66 | Permission::allow('admin', $resource, "*");
67 |
68 | //only for normal users
69 | Permission::allow('user', $resource, "download");
70 |
71 | return Permission::check($role, $resource, $action);
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/controllers/ErrorsController.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 |
15 | class ErrorsController extends Controller{
16 |
17 | /**
18 | * Initialization method.
19 | *
20 | */
21 | public function initialize(){
22 | }
23 |
24 | public function NotFound(){
25 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/errors/", Config::get('ERRORS_PATH') . "404.php");
26 | }
27 |
28 | public function Unauthenticated(){
29 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/errors/", Config::get('ERRORS_PATH') . "401.php");
30 | }
31 |
32 | public function Unauthorized(){
33 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/errors/", Config::get('ERRORS_PATH') . "403.php");
34 | }
35 |
36 | public function BadRequest(){
37 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/errors/", Config::get('ERRORS_PATH') . "400.php");
38 | }
39 |
40 | public function System(){
41 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/errors/", Config::get('ERRORS_PATH') . "500.php");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/controllers/FilesController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class FilesController extends Controller {
11 |
12 | public function beforeAction(){
13 |
14 | parent::beforeAction();
15 |
16 | Config::setJsConfig('curPage', "files");
17 |
18 | $action = $this->request->param('action');
19 | $actions = ['create', 'delete'];
20 | $this->Security->requireAjax($actions);
21 | $this->Security->requirePost($actions);
22 |
23 | switch($action){
24 | case "create":
25 | $this->Security->config("form", [ 'fields' => ['file']]);
26 | break;
27 | case "delete":
28 | $this->Security->config("form", [ 'fields' => ['file_id']]);
29 | break;
30 | }
31 | }
32 |
33 | public function index(){
34 |
35 | // clear all notifications whenever you hit 'files' in the navigation bar
36 | $this->user->clearNotifications(Session::getUserId(), $this->file->table);
37 |
38 | $pageNum = $this->request->query("page");
39 |
40 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'files/index.php', ['pageNum' => $pageNum]);
41 | }
42 |
43 | public function create(){
44 |
45 | $fileData = $this->request->data("file");
46 |
47 | $file = $this->file->create(Session::getUserId(), $fileData);
48 |
49 | if(!$file){
50 | $this->view->renderErrors($this->file->errors());
51 | }else{
52 |
53 | $fileHTML = $this->view->render(Config::get('VIEWS_PATH') . 'files/files.php', array("files" => $file));
54 | $this->view->renderJson(array("data" => $fileHTML));
55 | }
56 | }
57 |
58 | public function delete(){
59 |
60 | $fileId = Encryption::decryptIdWithDash($this->request->data("file_id"));
61 |
62 | if(!$this->file->exists($fileId)){
63 | return $this->error(404);
64 | }
65 |
66 | $this->file->deleteById($fileId);
67 |
68 | $this->view->renderJson(array("success" => true));
69 | }
70 |
71 | public function isAuthorized(){
72 |
73 | $action = $this->request->param('action');
74 | $role = Session::getUserRole();
75 | $resource = "files";
76 |
77 | // only for admins
78 | Permission::allow('admin', $resource, ['*']);
79 |
80 | // only for normal users
81 | Permission::allow('user', $resource, ['index', 'create']);
82 | Permission::allow('user', $resource, ['delete'], 'owner');
83 |
84 | $fileId = $this->request->data("file_id");
85 | if(!empty($fileId)){
86 | $fileId = Encryption::decryptIdWithDash($fileId);
87 | }
88 |
89 | $config = [
90 | "user_id" => Session::getUserId(),
91 | "table" => "files",
92 | "id" => $fileId
93 | ];
94 |
95 | return Permission::check($role, $resource, $action, $config);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/controllers/NewsFeedController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class NewsFeedController extends Controller{
11 |
12 | public function beforeAction(){
13 |
14 | parent::beforeAction();
15 |
16 | Config::setJsConfig('curPage', "newsfeed");
17 |
18 | $action = $this->request->param('action');
19 | $actions = ['create', 'getUpdateForm', 'update', 'getById', 'delete'];
20 | $this->Security->requirePost($actions);
21 |
22 | switch($action){
23 | case "create":
24 | $this->Security->config("form", [ 'fields' => ['content']]);
25 | break;
26 | case "getUpdateForm":
27 | $this->Security->config("form", [ 'fields' => ['newsfeed_id']]);
28 | break;
29 | case "update":
30 | $this->Security->config("form", [ 'fields' => ['newsfeed_id', 'content']]);
31 | break;
32 | case "getById":
33 | case "delete":
34 | $this->Security->config("form", [ 'fields' => ['newsfeed_id']]);
35 | break;
36 | }
37 | }
38 |
39 | public function index(){
40 |
41 | $this->user->clearNotifications(Session::getUserId(), $this->newsfeed->table);
42 |
43 | $pageNum = $this->request->query("page");
44 |
45 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'newsfeed/index.php', ['pageNum' => $pageNum]);
46 | }
47 |
48 | public function create(){
49 |
50 | $content = $this->request->data("content");
51 |
52 | $newsfeed = $this->newsfeed->create(Session::getUserId(), $content);
53 |
54 | if(!$newsfeed){
55 |
56 | Session::set('newsfeed-errors', $this->newsfeed->errors());
57 | return $this->redirector->root("NewsFeed");
58 |
59 | }else{
60 |
61 | return $this->redirector->root("NewsFeed");
62 | }
63 | }
64 |
65 | public function getUpdateForm(){
66 |
67 | $newsfeedId = Encryption::decryptIdWithDash($this->request->data("newsfeed_id"));
68 |
69 | if(!$this->newsfeed->exists($newsfeedId)){
70 | return $this->error(404);
71 | }
72 |
73 | $newsfeed = $this->newsfeed->getById($newsfeedId);
74 |
75 | $html = $this->view->render(Config::get('VIEWS_PATH') . 'newsfeed/updateForm.php', array("newsfeed" => $newsfeed[0]));
76 | $this->view->renderJson(array("data" => $html));
77 | }
78 |
79 | public function update(){
80 |
81 | // Remember? each news feed has an id that looks like this: feed-51b2cfa
82 | $newsfeedId = Encryption::decryptIdWithDash($this->request->data("newsfeed_id"));
83 | $content = $this->request->data("content");
84 |
85 | if(!$this->newsfeed->exists($newsfeedId)){
86 | return $this->error(404);
87 | }
88 |
89 | $newsfeed = $this->newsfeed->update($newsfeedId, $content);
90 | if(!$newsfeed){
91 | $this->view->renderErrors($this->newsfeed->errors());
92 | }else{
93 |
94 | $html = $this->view->render(Config::get('VIEWS_PATH') . 'newsfeed/newsfeed.php', array("newsfeed" => $newsfeed));
95 | $this->view->renderJson(array("data" => $html));
96 | }
97 | }
98 |
99 | public function getById(){
100 |
101 | $newsfeedId = Encryption::decryptIdWithDash($this->request->data("newsfeed_id"));
102 |
103 | if(!$this->newsfeed->exists($newsfeedId)){
104 | return $this->error(404);
105 | }
106 |
107 | $newsfeed = $this->newsfeed->getById($newsfeedId);
108 |
109 | $html = $this->view->render(Config::get('VIEWS_PATH') . 'newsfeed/newsfeed.php', array("newsfeed" => $newsfeed));
110 | $this->view->renderJson(array("data" => $html));
111 | }
112 |
113 | public function delete(){
114 |
115 | $newsfeedId = Encryption::decryptIdWithDash($this->request->data("newsfeed_id"));
116 |
117 | $this->newsfeed->deleteById($newsfeedId);
118 | $this->view->renderJson(array("success" => true));
119 | }
120 |
121 | public function isAuthorized(){
122 |
123 | $action = $this->request->param('action');
124 | $role = Session::getUserRole();
125 | $resource = "newsfeed";
126 |
127 | // only for admins
128 | Permission::allow('admin', $resource, ['*']);
129 |
130 | // only for normal users
131 | Permission::allow('user', $resource, ['index', 'getById', 'create']);
132 | Permission::allow('user', $resource, ['update', 'delete', 'getUpdateForm'], 'owner');
133 |
134 | $newsfeedId = $this->request->data("newsfeed_id");
135 | if(!empty($newsfeedId)){
136 | $newsfeedId = Encryption::decryptIdWithDash($newsfeedId);
137 | }
138 |
139 | $config = [
140 | "user_id" => Session::getUserId(),
141 | "table" => "newsfeed",
142 | "id" => $newsfeedId];
143 |
144 | return Permission::check($role, $resource, $action, $config);
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/app/controllers/PostsController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class PostsController extends Controller{
11 |
12 |
13 | public function beforeAction(){
14 |
15 | parent::beforeAction();
16 |
17 | Config::setJsConfig('curPage', "posts");
18 |
19 | $action = $this->request->param('action');
20 | $actions = ['create', 'update'];
21 | $this->Security->requirePost($actions);
22 |
23 | switch($action){
24 | case "create":
25 | $this->Security->config("form", [ 'fields' => ['title', 'content']]);
26 | break;
27 | case "update":
28 | $this->Security->config("form", [ 'fields' => ['post_id', 'title', 'content']]);
29 | break;
30 | case "delete":
31 | $this->Security->config("validateCsrfToken", true);
32 | $this->Security->config("form", [ 'fields' => ['post_id']]);
33 | break;
34 | }
35 | }
36 |
37 | /**
38 | * show posts page
39 | *
40 | */
41 | public function index(){
42 |
43 | // clear all notifications
44 | $this->user->clearNotifications(Session::getUserId(), $this->post->table);
45 |
46 | $pageNum = $this->request->query("page");
47 |
48 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'posts/index.php', ['pageNum' => $pageNum]);
49 | }
50 |
51 | /**
52 | * view a post
53 | *
54 | * @param integer|string $postId
55 | */
56 | public function view($postId = 0){
57 |
58 | $postId = Encryption::decryptId($postId);
59 |
60 | if(!$this->post->exists($postId)){
61 | return $this->error(404);
62 | }
63 |
64 | Config::setJsConfig('curPage', ["posts", "comments"]);
65 | Config::setJsConfig('postId', Encryption::encryptId($postId));
66 |
67 | $action = $this->request->query('action');
68 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'posts/viewPost.php', ["action"=> $action, "postId" => $postId]);
69 | }
70 |
71 | /**
72 | * show new post form
73 | */
74 | public function newPost(){
75 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'posts/newPost.php');
76 | }
77 |
78 | /**
79 | * creates a new post
80 | *
81 | */
82 | public function create(){
83 |
84 | $title = $this->request->data("title");
85 | $content = $this->request->data("content");
86 |
87 | $result = $this->post->create(Session::getUserId(), $title, $content);
88 |
89 | if(!$result){
90 | Session::set('posts-errors', $this->post->errors());
91 | }else{
92 | Session::set('posts-success', "Post has been created");
93 | }
94 |
95 | return $this->redirector->root("Posts/newPost");
96 | }
97 |
98 | /**
99 | * update a post
100 | *
101 | */
102 | public function update(){
103 |
104 | $postId = $this->request->data("post_id");
105 | $title = $this->request->data("title");
106 | $content = $this->request->data("content");
107 |
108 | $postId = Encryption::decryptId($postId);
109 |
110 | if(!$this->post->exists($postId)){
111 | return $this->error(404);
112 | }
113 |
114 | $post = $this->post->update($postId, $title, $content);
115 |
116 | if(!$post){
117 |
118 | Session::set('posts-errors', $this->post->errors());
119 | return $this->redirector->root("Posts/View/" . urlencode(Encryption::encryptId($postId)) . "?action=update");
120 |
121 | }else{
122 | return $this->redirector->root("Posts/View/" . urlencode(Encryption::encryptId($postId)));
123 | }
124 | }
125 |
126 | public function delete($postId = 0){
127 |
128 | $postId = Encryption::decryptId($postId);
129 |
130 | if(!$this->post->exists($postId)){
131 | return $this->error(404);
132 | }
133 |
134 | $this->post->deleteById($postId);
135 |
136 | return $this->redirector->root("Posts");
137 | }
138 |
139 | public function isAuthorized(){
140 |
141 | $action = $this->request->param('action');
142 | $role = Session::getUserRole();
143 | $resource = "posts";
144 |
145 | // only for admins
146 | Permission::allow('admin', $resource, ['*']);
147 |
148 | // only for normal users
149 | Permission::allow('user', $resource, ['index', 'view', 'newPost', 'create']);
150 | Permission::allow('user', $resource, ['update', 'delete'], 'owner');
151 |
152 | $postId = ($action === "delete")? $this->request->param("args")[0]: $this->request->data("post_id");
153 | if(!empty($postId)){
154 | $postId = Encryption::decryptId($postId);
155 | }
156 |
157 | $config = [
158 | "user_id" => Session::getUserId(),
159 | "table" => "posts",
160 | "id" => $postId
161 | ];
162 |
163 | return Permission::check($role, $resource, $action, $config);
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/app/controllers/TodoController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class TodoController extends Controller{
11 |
12 | // override this method to perform any logic before calling action method as explained above
13 | public function beforeAction(){
14 |
15 | parent::beforeAction();
16 |
17 | // define the actions in this Controller
18 | $action = $this->request->param('action');
19 |
20 | // restrict the request to action methods
21 | // $this->Security->requireAjax(['create', 'delete']);
22 | $this->Security->requirePost(['create', 'delete']);
23 |
24 | // define the expected form fields for every action if exist
25 | switch($action){
26 | case "create":
27 | // you can exclude form fields if you don't care if they were sent with form fields or not
28 | $this->Security->config("form", [ 'fields' => ['content']]);
29 | break;
30 | case "delete":
31 | // If you want to disable validation for form tampering
32 | // $this->Security->config("validateForm", false);
33 | $this->Security->config("form", [ 'fields' => ['todo_id']]);
34 | break;
35 | }
36 | }
37 |
38 | public function index(){
39 |
40 | // display todo list
41 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/todo/", Config::get('VIEWS_PATH') . 'todo/index.php');
42 | }
43 |
44 | public function create(){
45 |
46 | $content = $this->request->data("content");
47 | $todo = $this->todo->create(Session::getUserId(), $content);
48 |
49 | if(!$todo){
50 |
51 | // in case of normal post request
52 | Session::set('errors', $this->todo->errors());
53 | return $this->redirector->root("Todo");
54 |
55 | // in case of ajax
56 | // $this->view->renderErrors($this->todo->errors());
57 |
58 | }else{
59 |
60 | // in case of normal post request
61 | Session::set('success', "Todo has been created");
62 | return $this->redirector->root("Todo");
63 |
64 | // in case of ajax
65 | // $this->view->renderJson(array("success" => "Todo has been created"));
66 | }
67 | }
68 |
69 | public function delete(){
70 |
71 | $todoId = Encryption::decryptIdWithDash($this->request->data("todo_id"));
72 | $this->todo->delete($todoId);
73 |
74 | // in case of normal post request
75 | Session::set('success', "Todo has been deleted");
76 | return $this->redirector->root("Todo");
77 |
78 | // in case of ajax
79 | // $this->view->renderJson(array("success" => "Todo has been deleted"));
80 | }
81 |
82 | public function isAuthorized(){
83 |
84 | $action = $this->request->param('action');
85 | $role = Session::getUserRole();
86 | $resource = "todo";
87 |
88 | // only for admins
89 | Permission::allow('admin', $resource, ['*']);
90 |
91 | // only for normal users
92 | Permission::allow('user', $resource, ['delete'], 'owner');
93 |
94 | $todoId = $this->request->data("todo_id");
95 |
96 | if(!empty($todoId)){
97 | $todoId = Encryption::decryptIdWithDash($todoId);
98 | }
99 |
100 | $config = [
101 | "user_id" => Session::getUserId(),
102 | "table" => "todo",
103 | "id" => $todoId];
104 |
105 | return Permission::check($role, $resource, $action, $config);
106 | }
107 | }
--------------------------------------------------------------------------------
/app/controllers/UserController.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class UserController extends Controller{
11 |
12 | public function beforeAction(){
13 |
14 | parent::beforeAction();
15 |
16 | $action = $this->request->param('action');
17 | $actions = ['updateProfileInfo', 'updateProfilePicture', 'reportBug'];
18 | $this->Security->requirePost($actions);
19 |
20 | switch($action){
21 | case "updateProfileInfo":
22 | $this->Security->config("form", [ 'fields' => ['name', 'password', 'email', 'confirm_email']]);
23 | break;
24 | case "updateProfilePicture":
25 | $this->Security->config("form", [ 'fields' => ['file']]);
26 | break;
27 | case "reportBug":
28 | $this->Security->config("form", [ 'fields' => ['subject', 'label', 'message']]);
29 | break;
30 | }
31 | }
32 |
33 | /**
34 | * show dashboard page
35 | *
36 | */
37 | public function index(){
38 |
39 | Config::setJsConfig('curPage', "dashboard");
40 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'dashboard/index.php');
41 | }
42 |
43 | public function profile(){
44 |
45 | Config::setJsConfig('curPage', "profile");
46 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'user/profile.php');
47 | }
48 |
49 | public function updateProfileInfo(){
50 |
51 | $name = $this->request->data("name");
52 | $password = $this->request->data("password");
53 | $email = $this->request->data("email");
54 | $confirmEmail = $this->request->data("confirm_email");
55 |
56 | $result = $this->user->updateProfileInfo(Session::getUserId(), $name, $password, $email, $confirmEmail);
57 |
58 | if(!$result){
59 |
60 | Session::set('profile-info-errors', $this->user->errors());
61 |
62 | }else{
63 |
64 | $message = "Your Profile has been updated. ";
65 | $message .= $result["emailUpdated"]? "Please check your new email to confirm the changes, or your current email to revoke the changes": "";
66 |
67 | Session::set('profile-info-success', $message);
68 | }
69 |
70 | return $this->redirector->root("User/Profile");
71 | }
72 |
73 | public function updateProfilePicture(){
74 |
75 | $fileData = $this->request->data("file");
76 | $image = $this->user->updateProfilePicture(Session::getUserId(), $fileData);
77 |
78 | if(!$image){
79 | Session::set('profile-picture-errors', $this->user->errors());
80 | }
81 |
82 | return $this->redirector->root("User/Profile");
83 | }
84 |
85 | /**
86 | * revoke email updates
87 | *
88 | * You must be logged in with your current email
89 | */
90 | public function revokeEmail(){
91 |
92 | $userId = $this->request->query("id");
93 | $userId = empty($userId)? null: Encryption::decryptId($this->request->query("id"));
94 | $token = $this->request->query("token");
95 |
96 | $result = $this->user->revokeEmail($userId, $token);
97 |
98 | if(!$result){
99 | return $this->error(404);
100 | }else{
101 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'user/profile.php', ["emailUpdates" => ["success" => "Your email updates has been revoked successfully."]]);
102 | }
103 | }
104 |
105 | /**
106 | * confirm on email updates
107 | *
108 | * You must be logged in with your current email
109 | */
110 | public function updateEmail(){
111 |
112 | $userId = $this->request->query("id");
113 | $userId = empty($userId)? null: Encryption::decryptId($this->request->query("id"));
114 | $token = $this->request->query("token");
115 |
116 | $result = $this->user->updateEmail($userId, $token);
117 | $errors = $this->user->errors();
118 |
119 | if(!$result && empty($errors)){
120 | return $this->error(404);
121 | }else if(!$result && !empty($errors)){
122 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'user/profile.php', ["emailUpdates" => ["errors" => $this->user->errors()]]);
123 | }else{
124 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'user/profile.php',
125 | ["emailUpdates" => ["success" => "Your email updates has been updated successfully."]]);
126 | }
127 | }
128 |
129 | /**
130 | * users can report bugs, features, or enhancement
131 | * - Bug is an error you encountered
132 | * - Feature is a new functionality you suggest to add
133 | * - Enhancement is an existing feature, but you want to improve
134 | *
135 | */
136 | public function bugs(){
137 | Config::setJsConfig('curPage', "bugs");
138 | $this->view->renderWithLayouts(Config::get('VIEWS_PATH') . "layout/default/", Config::get('VIEWS_PATH') . 'bugs/index.php');
139 | }
140 |
141 | /**
142 | * send email to admin for reporting any bugs, features, or enhancement
143 | *
144 | */
145 | public function reportBug(){
146 |
147 | $subject = $this->request->data("subject");
148 | $label = $this->request->data("label");
149 | $message = $this->request->data("message");
150 |
151 | $result = $this->user->reportBug(Session::getUserId(), $subject, $label, $message);
152 |
153 | if(!$result){
154 | Session::set('report-bug-errors', $this->user->errors());
155 | }else{
156 | Session::set('report-bug-success', "Email has been sent successfully, We will consider your report.");
157 | }
158 |
159 | return $this->redirector->root("User/Bugs");
160 | }
161 |
162 | public function isAuthorized(){
163 | return true;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/app/core/Component.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class Component{
14 |
15 | /**
16 | * controller
17 | *
18 | * @var Controller
19 | */
20 | protected $controller;
21 |
22 | /**
23 | * request
24 | *
25 | * @var Request
26 | */
27 | protected $request;
28 |
29 | /**
30 | * Default configurations data
31 | *
32 | * @var array
33 | */
34 | protected $config = [];
35 |
36 |
37 | /**
38 | * Constructor
39 | *
40 | * @param Controller $controller
41 | * @param array $config user-provided config
42 | */
43 | public function __construct(Controller $controller, array $config = []){
44 | $this->controller = $controller;
45 | $this->request = $controller->request;
46 | $this->config = array_merge($this->config, $config);
47 | }
48 |
49 | /**
50 | * set and get configurations data
51 | *
52 | * @param string $key
53 | * @param mixed $value
54 | * @return mixed
55 | */
56 | public function config($key, $value = null){
57 |
58 | // set
59 | if($value !== null){
60 | $this->config = array_merge($this->config, [$key => $value]);
61 | return $this;
62 | }
63 |
64 | // get
65 | return array_key_exists($key, $this->config)? $this->config[$key]: null;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/core/Config.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | class Config{
12 |
13 | /**
14 | * Array of configurations
15 | *
16 | * @var array
17 | */
18 | private static $config = [];
19 |
20 | /**
21 | * Prefixes used to load specific configurations.
22 | *
23 | * @var array
24 | */
25 | private static $prefix = [
26 | 'default' => 'config',
27 | 'js' => 'javascript'
28 | ];
29 |
30 | /**
31 | * Get default configuration value(s)
32 | *
33 | * @param $key string
34 | * @return string|array|null
35 | */
36 | public static function get($key){
37 | return self::_get($key, self::$prefix['default']);
38 | }
39 |
40 | /**
41 | * Set or add a default configuration value
42 | *
43 | * @param $key string
44 | */
45 | public static function set($key, $value){
46 | self::_set($key, $value, self::$prefix['default']);
47 | }
48 |
49 | /**
50 | * Get javascript configuration value(s)
51 | *
52 | * @param $key string
53 | * @return string|array|null
54 | */
55 | public static function getJsConfig($key = ""){
56 | return self::_get($key, self::$prefix['js']);
57 | }
58 |
59 | /**
60 | * Set or add a javascript configuration value
61 | *
62 | * @param string $key
63 | * @param mixed $value
64 | */
65 | public static function setJsConfig($key, $value){
66 | self::_set($key, $value, self::$prefix['js']);
67 | }
68 |
69 | /**
70 | * Get a configuration value(s)
71 | *
72 | * @param $key string
73 | * @param $source string
74 | * @return string|null
75 | * @throws Exception if configuration file doesn't exist
76 | */
77 | private static function _get($key, $source){
78 |
79 | if (!isset(self::$config[$source])) {
80 |
81 | $config_file = APP . 'config/' . $source . '.php';
82 |
83 | if (!file_exists($config_file)) {
84 | throw new Exception("Configuration file " . $source . " doesn't exist");
85 | }
86 |
87 | self::$config[$source] = require $config_file . "";
88 | }
89 |
90 | if(empty($key)){
91 | return self::$config[$source];
92 | } else if(isset(self::$config[$source][$key])){
93 | return self::$config[$source][$key];
94 | }
95 |
96 | return null;
97 | }
98 |
99 | /**
100 | * Set or adds a configuration value
101 | *
102 | * @param $key string
103 | * @param $value string
104 | * @param $source string
105 | */
106 | private static function _set($key, $value, $source){
107 |
108 | // load configurations if not already loaded
109 | if (!isset(self::$config[$source])) {
110 | self::_get($key, $source);
111 | }
112 |
113 | if($key && $source){
114 | self::$config[$source][$key] = $value;
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/core/Cookie.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class Cookie{
11 |
12 | /**
13 | * @access public
14 | * @var string User ID
15 | */
16 | private static $userId = null;
17 |
18 | /**
19 | * @access public
20 | * @var string Cookie Token
21 | */
22 | private static $token = null;
23 |
24 | /**
25 | * @access public
26 | * @var string Hashed Token = hash(User ID . ":" . Token . Cookie Secret)
27 | */
28 | private static $hashedCookie = null;
29 |
30 | /**
31 | * This is the constructor for Cookie object.
32 | *
33 | * @access private
34 | */
35 | private function __construct() {}
36 |
37 | /**
38 | * Getters for $userId
39 | *
40 | * @access public
41 | * @static static method
42 | * @return string User ID
43 | */
44 | public static function getUserId(){
45 | return (int)self::$userId;
46 | }
47 |
48 | /**
49 | * Extract and validate cookie
50 | *
51 | * @access public
52 | * @static static method
53 | * @return bool
54 | */
55 | public static function isCookieValid(){
56 |
57 | // "auth" or "remember me" cookie
58 | if(empty($_COOKIE['auth'])) {
59 | return false;
60 | }
61 |
62 | // check the count before using explode
63 | $cookie_auth = explode(':', $_COOKIE['auth']);
64 | if(count ($cookie_auth) !== 3){
65 | self::remove();
66 | return false;
67 | }
68 |
69 | list ($encryptedUserId, self::$token, self::$hashedCookie) = $cookie_auth;
70 |
71 | // Remember? $hashedCookie was generated from the original user Id, NOT from the encrypted one.
72 | self::$userId = Encryption::decrypt($encryptedUserId);
73 |
74 | if (self::$hashedCookie === hash('sha256', self::$userId . ':' . self::$token . Config::get('COOKIE_SECRET_KEY')) && !empty(self::$token) && !empty(self::$userId)) {
75 |
76 | $database = Database::openConnection();
77 | $query = "SELECT id, cookie_token FROM users WHERE id = :id AND cookie_token = :cookie_token LIMIT 1";
78 | $database->prepare($query);
79 | $database->bindValue(':id', self::$userId);
80 | $database->bindValue(':cookie_token', self::$token);
81 | $database->execute();
82 |
83 | $isValid = $database->countRows() === 1? true: false;
84 |
85 | }else{
86 |
87 | $isValid = false;
88 | }
89 |
90 | if(!$isValid){
91 |
92 | Logger::log("COOKIE", self::$userId . " is trying to login using invalid cookie: " . self::$token, __FILE__, __LINE__);
93 | self::remove(self::$userId);
94 | }
95 |
96 | return $isValid;
97 | }
98 |
99 | /**
100 | * Remove cookie from the database of a user(if exists),
101 | * and also from the browser.
102 | *
103 | * @static static method
104 | * @param string $userId
105 | *
106 | */
107 | public static function remove($userId = null){
108 |
109 | if(!empty($userId)){
110 |
111 | $database = Database::openConnection();
112 | $query = "UPDATE users SET cookie_token = NULL WHERE id = :id";
113 | $database->prepare($query);
114 | $database->bindValue(":id", $userId);
115 | $result = $database->execute();
116 |
117 | if(!$result) {
118 | Logger::log("COOKIE", "Couldn't remove cookie from the database for user ID: " . $userId, __FILE__, __LINE__);
119 | }
120 | }
121 |
122 | self::$userId = self::$token = self::$hashedCookie = null;
123 |
124 | // How to kill/delete a cookie in a browser?
125 | setcookie('auth', false, time() - (3600 * 3650), Config::get('COOKIE_PATH'), Config::get('COOKIE_DOMAIN'), Config::get('COOKIE_SECURE'), Config::get('COOKIE_HTTP'));
126 | }
127 |
128 | /**
129 | * Reset Cookie,
130 | * resetting is done by updating the database,
131 | * and resetting the "auth" cookie in the browser
132 | *
133 | * @static static method
134 | * @param string $userId
135 | */
136 | public static function reset($userId){
137 |
138 | self::$userId = $userId;
139 | self::$token = hash('sha256', mt_rand());
140 | $database = Database::openConnection();
141 |
142 | $query = "UPDATE users SET cookie_token = :cookie_token WHERE id = :id";
143 | $database->prepare($query);
144 |
145 | // generate random hash for cookie token (64 char string)
146 | $database->bindValue(":cookie_token", self::$token);
147 | $database->bindValue(":id", self::$userId);
148 | $result = $database->execute();
149 |
150 | if(!$result) {
151 | Logger::log("COOKIE", "Couldn't remove cookie from the database for user ID: " . $userId, __FILE__, __LINE__);
152 | }
153 |
154 | // generate cookie string(remember me)
155 | // Don't expose the original user id in the cookie, Encrypt It!
156 | $cookieFirstPart = Encryption::encrypt(self::$userId) . ':' . self::$token ;
157 |
158 | // $hashedCookie generated from the original user Id, NOT from the encrypted one.
159 | self::$hashedCookie = hash('sha256', self::$userId . ':' . self::$token . Config::get('COOKIE_SECRET_KEY'));
160 | $authCookie = $cookieFirstPart . ':' . self::$hashedCookie;
161 |
162 | setcookie('auth', $authCookie, time() + Config::get('COOKIE_EXPIRY'), Config::get('COOKIE_PATH'), Config::get('COOKIE_DOMAIN'), Config::get('COOKIE_SECURE'), Config::get('COOKIE_HTTP'));
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/app/core/Environment.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | class Environment{
12 |
13 | /**
14 | * Constructor
15 | *
16 | */
17 | private function __construct(){}
18 |
19 | /**
20 | * Gets an environment variable from $_SERVER, $_ENV, or using getenv()
21 | *
22 | * @param $key string
23 | * @return string|null
24 | */
25 | public static function get($key){
26 |
27 | $val = null;
28 | if (isset($_SERVER[$key])) {
29 | $val = $_SERVER[$key];
30 | } elseif (isset($_ENV[$key])) {
31 | $val = $_ENV[$key];
32 | } elseif (getenv($key) !== false) {
33 | $val = getenv($key);
34 | }
35 |
36 | return $val;
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/app/core/Handler.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 |
13 | class Handler{
14 |
15 | /**
16 | * Constructor
17 | *
18 | */
19 | private function __construct(){}
20 |
21 | /**
22 | * Register the error and exception handlers.
23 | * Must be called at the beginning of your application
24 | *
25 | * @return void
26 | */
27 | public static function register(){
28 |
29 | // turn off all error reporting as well,
30 | // because we will take care of it
31 | error_reporting(0);
32 |
33 | set_error_handler(__CLASS__ . "::handleError");
34 | set_exception_handler(__CLASS__ .'::handleException');
35 | register_shutdown_function(__CLASS__ ."::handleFatalError" );
36 | }
37 |
38 | /**
39 | * Handle fatal errors
40 | *
41 | * @return void
42 | */
43 | public static function handleFatalError(){
44 |
45 | if (PHP_SAPI === 'cli') { return; }
46 | $error = error_get_last();
47 |
48 | if (!is_array($error)) { return; }
49 |
50 | $fatals = [E_USER_ERROR, E_ERROR, E_PARSE];
51 |
52 | if (!in_array($error['type'], $fatals, true)) {
53 | return;
54 | }
55 |
56 | // self::handleError($error['type'], $error['message'], $error['file'], $error['line'], null);
57 | self::handleException(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
58 | }
59 |
60 | /**
61 | * Handle errors
62 | *
63 | * @return void
64 | * @throws ErrorException
65 | */
66 | public static function handleError($errno, $errmsg, $filename, $linenum, $vars){
67 | throw new ErrorException($errmsg, 0, $errno, $filename, $linenum);
68 | }
69 |
70 | /**
71 | * Handle & log exceptions
72 | *
73 | * @param Throwable $e
74 | * @return void
75 | * @see http://php.net/manual/en/function.set-exception-handler.php
76 | */
77 | public static function handleException($e) {
78 | Logger::Log(get_class($e), $e->getMessage(), $e->getFile(), $e->getLine());
79 | self::render($e)->send();
80 | }
81 |
82 | /**
83 | * display system error page as result of an error or exception
84 | *
85 | * @param Throwable $e
86 | * @return Response
87 | */
88 | private static function render($e){
89 |
90 | if($e->getCode() === 400){
91 | return (new ErrorsController())->error(400);
92 | }
93 |
94 | return (new ErrorsController())->error(500);
95 | }
96 |
97 | /**
98 | * Map an error code to error text
99 | *
100 | * @param int $errno
101 | * @return string error text
102 | */
103 | private static function errorType($errno){
104 |
105 | // define an assoc array of error string
106 | $errortype = array (
107 | E_ERROR => 'Error',
108 | E_WARNING => 'Warning',
109 | E_PARSE => 'Parsing Error',
110 | E_NOTICE => 'Notice',
111 | E_CORE_ERROR => 'Core Error',
112 | E_CORE_WARNING => 'Core Warning',
113 | E_COMPILE_ERROR => 'Compile Error',
114 | E_COMPILE_WARNING => 'Compile Warning',
115 | E_USER_ERROR => 'User Error',
116 | E_USER_WARNING => 'User Warning',
117 | E_USER_NOTICE => 'User Notice',
118 | E_STRICT => 'Runtime Notice',
119 | E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
120 | );
121 |
122 | return $errortype[$errno];
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/app/core/Logger.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 |
12 | class Logger{
13 |
14 | /**
15 | * Constructor
16 | *
17 | */
18 | private function __construct(){}
19 |
20 | /**
21 | * log
22 | *
23 | * @access public
24 | * @static static method
25 | * @param string $header
26 | * @param string $message
27 | * @param string $filename
28 | * @param string $linenum
29 | */
30 | public static function log($header="", $message="", $filename="", $linenum=""){
31 |
32 | $logfile = APP . "logs/log.txt";
33 | $date = date("d/m/Y G:i:s");
34 | $err = $date." | ".$filename." | ".$linenum." | ".$header. "\n";
35 |
36 | $message = is_array($message)? implode("\n", $message): $message;
37 | $err .= $message . "\n*******************************************************************\n\n";
38 |
39 | // log/write error to log file
40 | error_log($err, 3, $logfile);
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/core/Redirector.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 |
12 | class Redirector{
13 |
14 | /**
15 | * Constructor
16 | *
17 | */
18 | public function __construct(){
19 | }
20 |
21 | /**
22 | * Redirect to the given location
23 | *
24 | * @param string $location
25 | */
26 | public function to($location, $query = ""){
27 |
28 | if(!empty($query)){
29 | $query = '?' . http_build_query((array)$query, null, '&');
30 | }
31 |
32 | $response = new Response('', 302, ["Location" => $location . $query]);
33 | return $response;
34 | }
35 |
36 | /**
37 | * Redirect to the given location from the root
38 | *
39 | * @param string $location
40 | */
41 | public function root($location = "", $query = ""){
42 | return $this->to(PUBLIC_ROOT . $location, $query);
43 | }
44 |
45 | /**
46 | * Redirect to the dashboard
47 | */
48 | public function dashboard(){
49 | return $this->to(PUBLIC_ROOT . "User");
50 | }
51 |
52 | /**
53 | * Redirect to the login page
54 | * $redirect_url is to send the user back to where he/she came from after login
55 | *
56 | * @param string|null $redirect_url
57 | */
58 | public function login($redirect_url = null){
59 | if(!empty($redirect_url)){
60 | return $this->to(PUBLIC_ROOT . "?redirect=" . urlencode($redirect_url));
61 | }else{
62 | return $this->to(PUBLIC_ROOT);
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/core/components/AuthComponent.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 |
12 | class AuthComponent extends Component{
13 |
14 | /**
15 | * Default configurations
16 | *
17 | * @var array
18 | */
19 | protected $config = [
20 | 'authenticate' => [],
21 | 'authorize' => []
22 | ];
23 |
24 | /**
25 | * Auth startup
26 | * All authentication and authorization checking are done in this method
27 | *
28 | */
29 | public function startup(){
30 |
31 | // authenticate
32 | if(!empty($this->config["authenticate"])){
33 | if(!$this->authenticate()){
34 | return $this->unauthenticated();
35 | }
36 | }
37 |
38 | // authorize
39 | if(!empty($this->config["authorize"])){
40 | if(!$this->authorize()){
41 | return $this->unauthorized();
42 | }
43 | }
44 | }
45 |
46 | /**
47 | * Handles unauthenticated access attempt.
48 | *
49 | */
50 | public function unauthenticated(){
51 |
52 | $this->controller->login->logOut(Session::getUserId());
53 |
54 | if($this->request->isAjax()) {
55 | return $this->controller->error(401);
56 | }else{
57 | $redirect = $this->controller->request->isGet()? $this->controller->request->uri(): "";
58 | return $this->controller->redirector->login($redirect);
59 | }
60 | }
61 |
62 | /**
63 | * Handles unauthorized access attempt.
64 | *
65 | */
66 | public function unauthorized(){
67 | return $this->controller->error(403);
68 | }
69 |
70 | /**
71 | * authenticate the user using the defined methods in $config
72 | *
73 | * @return boolean
74 | */
75 | public function authenticate(){
76 | return $this->check($this->config["authenticate"], "authenticate");
77 | }
78 |
79 | /**
80 | * authorize the user using the defined methods in $config
81 | *
82 | * @return boolean
83 | */
84 | public function authorize(){
85 | return $this->check($this->config["authorize"], "authorize");
86 | }
87 |
88 | /**
89 | * check for authentication or authorization
90 | *
91 | * @param array $config
92 | * @param string $type
93 | * @throws Exception if $config is empty or method doesn't exists
94 | * @return boolean
95 | */
96 | private function check($config, $type){
97 |
98 | if (empty($config)) {
99 | throw new Exception($type . ' methods arent initialized yet in config');
100 | }
101 |
102 | $auth = Utility::normalize($config);
103 |
104 | foreach($auth as $method => $config){
105 |
106 | $method = "_" . ucfirst($method) . ucfirst($type);
107 |
108 | if (!method_exists(__CLASS__, $method)) {
109 | throw new Exception('Auth Method doesnt exists: ' . $method);
110 | }
111 |
112 | if($this->{$method}($config) === false){
113 | return false;
114 | }
115 | }
116 | return true;
117 | }
118 |
119 | /**
120 | * Is user is already logged in via session or cookie?
121 | *
122 | * @return boolean
123 | */
124 | public function isLoggedIn(){
125 |
126 | if(Session::getIsLoggedIn() === true){
127 | return true;
128 | }
129 |
130 | if(Cookie::isCookieValid()){
131 | return true;
132 | }
133 |
134 | return false;
135 | }
136 |
137 | /**
138 | * Is user authorized for the requested Controller & Action method?
139 | *
140 | * @param array $config configuration data
141 | * @throws Exception if isAuthorized method doesn't exists in the controller class
142 | * @return boolean
143 | */
144 | private function _ControllerAuthorize($config){
145 |
146 | if (!method_exists($this->controller, 'isAuthorized')) {
147 | throw new Exception(sprintf('%s does not implement an isAuthorized() method.', get_class($this->controller)));
148 | }
149 | return (bool)$this->controller->isAuthorized();
150 | }
151 |
152 | /**
153 | * Is user authenticated?
154 | * It checks for:
155 | * - concurrent session
156 | * - user credentials in session & cookies
157 | * - cookies theft and manipulations
158 | * - session Hijacking and fixation.
159 | *
160 | * @param array $config configuration data
161 | * @return boolean
162 | */
163 | private function _UserAuthenticate($config){
164 |
165 | if($this->concurentSession()){
166 | return false;
167 | }
168 |
169 | if(!$this->loggedIn()){
170 | return false;
171 | }
172 |
173 | return true;
174 | }
175 |
176 | /**
177 | * Checks if user is logged in or not.
178 | * It uses Session and Cookies to validate the current user.
179 | *
180 | * @access public
181 | * @static static method
182 | * @return boolean
183 | *
184 | */
185 | private function loggedIn(){
186 |
187 | if (Session::isSessionValid($this->request->clientIp(), $this->request->userAgent())) {
188 | return true;
189 | }
190 |
191 | if (Cookie::isCookieValid()) {
192 |
193 | // get role from user class, because cookies don't store roles
194 | $role = $this->controller->user->getProfileInfo(Cookie::getUserId())["role"];
195 | Session::reset(["user_id" => Cookie::getUserId(), "role" => $role, "ip" => $this->request->clientIp(), "user_agent" => $this->request->userAgent()]);
196 |
197 | // reset cookie, Cookie token is usable only once
198 | Cookie::reset(Session::getUserId());
199 |
200 | return true;
201 | }
202 |
203 | return false;
204 | }
205 |
206 | private function concurentSession(){
207 | return Session::isConcurrentSessionExists();
208 | }
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/app/logs/log.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OmarElgabry/miniPHP/7ddf0c9db29b936418c1992e3d94cdf4918a1e93/app/logs/log.txt
--------------------------------------------------------------------------------
/app/models/Comment.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | class Comment extends Model{
10 |
11 | /**
12 | * get all comments of a post
13 | *
14 | * @access public
15 | * @param array $postId
16 | * @param integer $pageNum
17 | * @param integer $commentsCreated
18 | * @return array Associative array of the comments, and Pagination Object(View More).
19 | *
20 | */
21 | public function getAll($postId, $pageNum = 1, $commentsCreated = 0){
22 |
23 | // Only for comments, We use $commentsCreated
24 | // What's it? Whenever we create a comment, It will be added in-place to the current comments in current .php page,
25 | // So, we need to track of those were created, and skip them in the Pagination($offset & $totalCount).
26 |
27 | $options = "WHERE comments.post_id = :post_id ";
28 | $pagination = Pagination::pagination("comments", $options, [":post_id" => $postId], $pageNum, $commentsCreated);
29 | $offset = $pagination->getOffset() + $commentsCreated;
30 | $limit = $pagination->perPage;
31 |
32 | $database = Database::openConnection();
33 | $query = "SELECT comments.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, comments.content, comments.date ";
34 | $query .= "FROM users, posts, comments ";
35 | $query .= "WHERE comments.post_id = :post_id ";
36 | $query .= "AND posts.id = comments.post_id ";
37 | $query .= "AND users.id = comments.user_id ";
38 | $query .= "ORDER BY comments.date DESC ";
39 | $query .= "LIMIT $limit OFFSET $offset";
40 |
41 | $database->prepare($query);
42 | $database->bindValue(':post_id', (int)$postId);
43 | $database->execute();
44 | $comments = $database->fetchAllAssociative();
45 |
46 | // you can have post with no comments yet!
47 | return array("comments" => $comments, "pagination" => $pagination);
48 | }
49 |
50 | /**
51 | * get comment by Id
52 | *
53 | * @access public
54 | * @param string $commentId
55 | * @return array Array holds the data of the comment
56 | *
57 | */
58 | public function getById($commentId){
59 |
60 | $database = Database::openConnection();
61 | $query = "SELECT comments.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, comments.content, comments.date ";
62 | $query .= "FROM users, posts, comments ";
63 | $query .= "WHERE comments.id = :id ";
64 | $query .= "AND posts.id = comments.post_id ";
65 | $query .= "AND users.id = comments.user_id LIMIT 1";
66 |
67 | $database->prepare($query);
68 | $database->bindValue(':id', (int)$commentId);
69 | $database->execute();
70 |
71 | $comment = $database->fetchAllAssociative();
72 | return $comment;
73 | }
74 |
75 | /**
76 | * create Comment.
77 | *
78 | * @access public
79 | * @param string $userId
80 | * @param string $postId
81 | * @param string $content
82 | * @return array Array holds the created comment
83 | * @throws Exception If comment couldn't be created
84 | *
85 | */
86 | public function create($userId, $postId, $content){
87 |
88 | $validation = new Validation();
89 | if(!$validation->validate([ 'Content' => [$content, 'required|minLen(1)|maxLen(300)']])) {
90 | $this->errors = $validation->errors();
91 | return false;
92 | }
93 |
94 | $database = Database::openConnection();
95 | $query = "INSERT INTO comments (user_id, post_id, content) VALUES (:user_id, :post_id, :content)";
96 |
97 | $database->prepare($query);
98 | $database->bindValue(':user_id', $userId);
99 | $database->bindValue(':post_id', $postId);
100 | $database->bindValue(':content', $content);
101 | $database->execute();
102 |
103 | if($database->countRows() !== 1){
104 | throw new Exception ("Couldn't add comment");
105 | }
106 |
107 | $commentId = $database->lastInsertedId();
108 | $comment = $this->getById($commentId);
109 | return $comment;
110 | }
111 |
112 | /**
113 | * update Comment
114 | *
115 | * @access public
116 | * @param string $commentId
117 | * @param string $content
118 | * @return array Array holds the updated comment
119 | * @throws Exception If comment couldn't be updated
120 | *
121 | */
122 | public function update($commentId, $content){
123 |
124 | $validation = new Validation();
125 | if(!$validation->validate([ 'Content' => [$content, 'required|minLen(1)|maxLen(300)']])) {
126 | $this->errors = $validation->errors();
127 | return false;
128 | }
129 |
130 | $database = Database::openConnection();
131 | $query = "UPDATE comments SET content = :content WHERE id = :id LIMIT 1 ";
132 | $database->prepare($query);
133 | $database->bindValue(':content', $content);
134 | $database->bindValue(':id', $commentId);
135 | $result = $database->execute();
136 |
137 | if(!$result){
138 | throw new Exception("Couldn't update comment of ID: " . $commentId);
139 | }
140 |
141 | $comment = $this->getById($commentId);
142 | return $comment;
143 | }
144 |
145 | /**
146 | * counting the number of comments of a post.
147 | *
148 | * @access public
149 | * @static static method
150 | * @param string $postId
151 | * @return integer number of comments
152 | *
153 | */
154 | public static function countComments($postId){
155 |
156 | $database = Database::openConnection();
157 | $database->prepare("SELECT COUNT(*) AS count FROM comments WHERE post_id = :post_id");
158 | $database->bindValue(":post_id", $postId);
159 | $database->execute();
160 |
161 | return (int)$database->fetchAssociative()["count"];
162 | }
163 |
164 | }
--------------------------------------------------------------------------------
/app/models/File.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class File extends Model{
11 |
12 | /**
13 | * get all files.
14 | *
15 | * @access public
16 | * @param integer $pageNum
17 | * @return array
18 | *
19 | */
20 | public function getAll($pageNum = 1){
21 |
22 | // get pagination object
23 | $pagination = Pagination::pagination("files", "", [], $pageNum);
24 | $offset = $pagination->getOffset();
25 | $limit = $pagination->perPage;
26 |
27 | $database = Database::openConnection();
28 | $query = "SELECT files.id AS id, files.filename, users.id AS user_id, users.name AS user_name, files.extension AS format, files.hashed_filename, files.date ";
29 | $query .= "FROM users, files ";
30 | $query .= "WHERE users.id = files.user_id ";
31 | $query .= "ORDER BY files.date DESC ";
32 | $query .= "LIMIT $limit OFFSET $offset";
33 |
34 | $database->prepare($query);
35 | $database->execute();
36 | $files = $database->fetchAllAssociative();
37 |
38 | return array("files" => $files, "pagination" => $pagination);
39 | }
40 |
41 | /**
42 | * get file by Id.
43 | *
44 | * @access public
45 | * @param string $fileId
46 | * @return array Array holds the data of the file
47 | */
48 | public function getById($fileId){
49 |
50 | $database = Database::openConnection();
51 | $query = "SELECT files.id AS id, files.filename, users.id AS user_id, users.name AS user_name, files.extension AS format, files.hashed_filename, files.date ";
52 | $query .= "FROM users, files ";
53 | $query .= "WHERE files.id = :id ";
54 | $query .= "AND users.id = files.user_id LIMIT 1 ";
55 |
56 | $database->prepare($query);
57 | $database->bindValue(':id', (int)$fileId);
58 | $database->execute();
59 |
60 | $file = $database->fetchAllAssociative();
61 | return $file;
62 | }
63 |
64 | /**
65 | * get file by hashed name.
66 | * files are unique by the hashed file name(= hash(original filename . extension)).
67 | *
68 | * @access public
69 | * @param string $hashedFileName
70 | * @return array Array holds the data of the file
71 | */
72 | public function getByHashedName($hashedFileName){
73 |
74 | $database = Database::openConnection();
75 |
76 | $query = "SELECT files.id AS id, files.filename, files.extension, files.hashed_filename ";
77 | $query .= "FROM files ";
78 | $query .= "WHERE hashed_filename = :hashed_filename ";
79 | $query .= "LIMIT 1 ";
80 |
81 | $database->prepare($query);
82 | $database->bindValue(':hashed_filename', $hashedFileName);
83 | $database->execute();
84 |
85 | $file = $database->fetchAssociative();
86 | return $file;
87 | }
88 |
89 | /**
90 | * create file.
91 | *
92 | * @access public
93 | * @param integer $userId
94 | * @param array $fileData
95 | * @return array Array holds the created file
96 | * @throws Exception If file couldn't be created
97 | */
98 | public function create($userId, $fileData){
99 |
100 | // upload
101 | $file = Uploader::uploadFile($fileData);
102 |
103 | if(!$file) {
104 | $this->errors = Uploader::errors();
105 | return false;
106 | }
107 |
108 | $database = Database::openConnection();
109 |
110 | $query = "INSERT INTO files (user_id, filename, hashed_filename, extension) VALUES (:user_id, :filename, :hashed_filename, :extension)";
111 |
112 | $database->prepare($query);
113 | $database->bindValue(':user_id', $userId);
114 | $database->bindValue(':filename', $file["filename"]);
115 | $database->bindValue(':hashed_filename', $file["hashed_filename"]);
116 | $database->bindValue(':extension', strtolower($file["extension"]));
117 | $database->execute();
118 |
119 | // if insert failed, then delete the file
120 | if($database->countRows() !== 1){
121 | Uploader::deleteFile(APP ."uploads/" . $file["basename"]);
122 | throw new Exception ("Couldn't upload file");
123 | }
124 |
125 | $fileId = $database->lastInsertedId();
126 | $file = $this->getById($fileId);
127 | return $file;
128 | }
129 |
130 | /**
131 | * deletes file.
132 | * This method overrides the deleteById() method in Model class.
133 | *
134 | * @access public
135 | * @param array $id
136 | * @throws Exception If failed to delete the file
137 | *
138 | */
139 | public function deleteById($id){
140 |
141 | $database = Database::openConnection();
142 |
143 | $database->getById("files", $id);
144 | $file = $database->fetchAssociative();
145 |
146 | // start a transaction to guarantee the file will be deleted from both; database and filesystem
147 | $database->beginTransaction();
148 | $database->deleteById("files", $id);
149 |
150 | if($database->countRows() !== 1){
151 | $database->rollBack();
152 | throw new Exception ("Couldn't delete file");
153 | }
154 |
155 | $basename = $file["hashed_filename"] . "." . $file["extension"];
156 | Uploader::deleteFile(APP ."uploads/" . $basename);
157 |
158 | $database->commit();
159 | }
160 |
161 | }
--------------------------------------------------------------------------------
/app/models/Model.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 |
12 | class Model {
13 |
14 | /**
15 | * Table name for the Model.
16 | *
17 | * @var string
18 | */
19 | public $table = false;
20 |
21 | /**
22 | * Array of validation errors
23 | *
24 | * @var array
25 | */
26 | protected $errors = [];
27 |
28 | /**
29 | * Constructor
30 | *
31 | */
32 | public function __construct(){
33 | if($this->table === false){
34 | $this->table = $this->pluralize(get_class($this));
35 | }
36 | }
37 |
38 | /**
39 | * pluralize for table names
40 | *
41 | * Automatically selects a database table name based on a pluralized lowercase object class name
42 | * (i.e. class 'User' => table 'users')
43 | *
44 | * @param string $word
45 | * @return string
46 | */
47 | private function pluralize($word){
48 |
49 | $word = strtolower($word);
50 | $plural = [
51 | "newsfeed" => "newsfeed",
52 | "man" => "men",
53 | "woman" => "women"
54 | ];
55 |
56 | return isset($plural[$word])? $plural[$word]: $word . "s";
57 | }
58 |
59 | /**
60 | * delete record by id
61 | *
62 | * @param string $id
63 | * @return bool
64 | * @throws Exception if feed couldn't be deleted
65 | */
66 | public function deleteById($id){
67 |
68 | $database = Database::openConnection();
69 | $database->deleteById($this->table, $id);
70 |
71 | if($database->countRows() !== 1){
72 | throw new Exception ("Couldn't delete news feed");
73 | }
74 | }
75 |
76 | /**
77 | * get errors
78 | *
79 | * @return array errors
80 | */
81 | public function errors(){
82 | return $this->errors;
83 | }
84 |
85 | /**
86 | * is record exists?
87 | *
88 | * Almost all methods in model needs you to pass the current user id,
89 | * Another approach is to create in Model class a property call id(will be inherited by all extending classes)
90 | * Ex:
91 | * Inside postsController:
92 | * post->id = $postId
93 | * post->updatePost(....) -> Inside updatePost() you can get the post id by: $this->id
94 | *
95 | * @param string $id
96 | * @return bool
97 | */
98 | public function exists($id){
99 |
100 | $database = Database::openConnection();
101 | $database->getById($this->table, $id);
102 |
103 | return $database->countRows() === 1;
104 | }
105 |
106 | /**
107 | * Counting the number of a current model's table.
108 | *
109 | * @return integer
110 | */
111 | public function countAll(){
112 |
113 | $database = Database::openConnection();
114 | return $database->countAll($this->table);
115 | }
116 |
117 | /**
118 | * adds parts to current query using an array.
119 | *
120 | * The array will have $key which is the field value, and the $value is the query to be added to the current query
121 | * Only fields with existing value(false, and 0 are accepted as valid values) will be considered in our query.
122 | *
123 | * @param array $options
124 | * @param string $implodeBy
125 | * @return string
126 | */
127 | public function applyOptions(array $options, $implodeBy){
128 |
129 | $queries = [];
130 |
131 | foreach($options as $key => $value){
132 | if(!empty($key) || $key === false || $key === 0){
133 | $queries[] = $value;
134 | }
135 | }
136 | return implode($implodeBy, $queries);
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/app/models/NewsFeed.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | class NewsFeed extends Model{
10 |
11 | /**
12 | * get all news feed.
13 | *
14 | * @access public
15 | * @param integer $pageNum
16 | * @return array
17 | *
18 | */
19 | public function getAll($pageNum = 1){
20 |
21 | $pagination = Pagination::pagination("newsfeed", "", [], $pageNum);
22 | $offset = $pagination->getOffset();
23 | $limit = $pagination->perPage;
24 |
25 | $database = Database::openConnection();
26 | $query = "SELECT newsfeed.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, newsfeed.content, newsfeed.date ";
27 | $query .= "FROM users, newsfeed ";
28 | $query .= "WHERE users.id = newsfeed.user_id ";
29 | $query .= "ORDER BY newsfeed.date DESC ";
30 | $query .= "LIMIT $limit OFFSET $offset";
31 |
32 | $database->prepare($query);
33 | $database->execute();
34 | $newsfeed = $database->fetchAllAssociative();
35 |
36 | return array("newsfeed" => $newsfeed, "pagination" => $pagination);
37 | }
38 |
39 | /**
40 | * get news feed by Id.
41 | *
42 | * @param string $newsfeedId
43 | * @return array
44 | */
45 | public function getById($newsfeedId){
46 |
47 | $database = Database::openConnection();
48 | $query = "SELECT newsfeed.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, newsfeed.content, newsfeed.date ";
49 | $query .= "FROM users, newsfeed ";
50 | $query .= "WHERE newsfeed.id = :id ";
51 | $query .= "AND users.id = newsfeed.user_id LIMIT 1 ";
52 |
53 | $database->prepare($query);
54 | $database->bindValue(':id', (int)$newsfeedId);
55 | $database->execute();
56 |
57 | $feed = $database->fetchAllAssociative();
58 | return $feed;
59 | }
60 |
61 | /**
62 | * create news feed.
63 | *
64 | * @param integer $userId
65 | * @param string $content
66 | * @return array feed created
67 | * @throws Exception if feed couldn't be created
68 | */
69 | public function create($userId, $content){
70 |
71 | $validation = new Validation();
72 | if(!$validation->validate(['Content' => [$content, "required|minLen(4)|maxLen(300)"]])) {
73 | $this->errors = $validation->errors();
74 | return false;
75 | }
76 |
77 | $database = Database::openConnection();
78 | $query = "INSERT INTO newsfeed (user_id, content) VALUES (:user_id, :content)";
79 |
80 | $database->prepare($query);
81 | $database->bindValue(':user_id', $userId);
82 | $database->bindValue(':content', $content);
83 | $database->execute();
84 |
85 | if($database->countRows() !== 1){
86 | throw new Exception("Couldn't add news feed");
87 | }
88 |
89 | $newsfeedId = $database->lastInsertedId();
90 | $feed = $this->getById($newsfeedId);
91 | return $feed;
92 | }
93 |
94 | /**
95 | * update news feed.
96 | *
97 | * @param string $newsfeedId
98 | * @param string $content
99 | * @return array feed created
100 | * @throws Exception if feed couldn't be updated
101 | */
102 | public function update($newsfeedId, $content){
103 |
104 | $validation = new Validation();
105 | if(!$validation->validate(['Content' => [$content, "required|minLen(4)|maxLen(300)"]])) {
106 | $this->errors = $validation->errors();
107 | return false;
108 | }
109 |
110 | $database = Database::openConnection();
111 | $query = "UPDATE newsfeed SET content = :content WHERE id = :id LIMIT 1";
112 |
113 | $database->prepare($query);
114 | $database->bindValue(':content', $content);
115 | $database->bindValue(':id', $newsfeedId);
116 | $result = $database->execute();
117 |
118 | if(!$result){
119 | throw new Exception("Couldn't update newsfeed of ID: " . $newsfeedId);
120 | }
121 |
122 | $feed = $this->getById($newsfeedId);
123 | return $feed;
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/app/models/Pagination.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class Pagination {
11 |
12 | /**
13 | * @access public
14 | * @var integer Current Page
15 | */
16 | public $currentPage;
17 |
18 | /**
19 | * @access public
20 | * @var integer Number of items(newsfeed, posts, ..etc.) to be displayed per page
21 | */
22 | public $perPage;
23 |
24 | /**
25 | * @access public
26 | * @var integer Total count of items(newsfeed, posts, ..etc.)
27 | */
28 | public $totalCount;
29 |
30 | /**
31 | * This is the constructor for Pagination Object.
32 | *
33 | * @access public
34 | * @param integer $currentPage
35 | * @param integer $totalCount
36 | * @param integer $perPage Number of items per page
37 | */
38 | public function __construct($currentPage = 1, $totalCount = 0, $perPage = 0){
39 | $this->currentPage = (empty($currentPage))? 1: (int)$currentPage;
40 | $this->totalCount = (empty($totalCount))? 0: (int)$totalCount;
41 | $this->perPage = (empty($perPage))? Config::get('PAGINATION_DEFAULT_LIMIT'): (int)$perPage;
42 | }
43 |
44 | /**
45 | * get pagination object by executing COUNT(*) query.
46 | *
47 | * @access public
48 | * @param string $table
49 | * @param string $options
50 | * @param array $values array of data
51 | * @param integer $pageNum
52 | * @param integer $extraOffset check comment class
53 | * @return Pagination
54 | */
55 | public static function pagination($table, $options, $values, $pageNum, $extraOffset = 0){
56 |
57 | $database = Database::openConnection();
58 | $query = "SELECT COUNT(*) AS count FROM {$table} ";
59 | $query .= $options;
60 |
61 | $database->prepare($query);
62 | $database->execute($values);
63 | $totalCount = $database->fetchAssociative()["count"];
64 | $extraOffset = ((int)$extraOffset > $totalCount)? 0: (int)$extraOffset;
65 |
66 | return new Pagination((int)$pageNum, $totalCount - $extraOffset);
67 | }
68 |
69 | /**
70 | * Get the offset.
71 | *
72 | * @access public
73 | * @return integer
74 | */
75 | public function getOffset() {
76 | return ($this->currentPage - 1) * $this->perPage;
77 | }
78 |
79 | /**
80 | * Get number of total pages.
81 | *
82 | * @access public
83 | * @return integer
84 | */
85 | public function totalPages() {
86 | return ceil($this->totalCount/$this->perPage);
87 | }
88 |
89 | /**
90 | * Get the number of the previous page.
91 | *
92 | * @access public
93 | * @return integer Number of previous page
94 | */
95 | public function previousPage() {
96 | return $this->currentPage - 1;
97 | }
98 |
99 | /**
100 | * Get the number of the next page.
101 | *
102 | * @access public
103 | * @return integer Number of next page
104 | */
105 | public function nextPage() {
106 | return $this->currentPage + 1;
107 | }
108 |
109 | /**
110 | * checks if there is a previous page or not
111 | *
112 | * @access public
113 | * @return boolean
114 | */
115 | public function hasPreviousPage() {
116 | return $this->previousPage() >= 1 ? true : false;
117 | }
118 |
119 | /**
120 | * checks if there is a next page or not
121 | *
122 | * @access public
123 | * @return boolean
124 | */
125 | public function hasNextPage() {
126 | return $this->totalPages() >= $this->nextPage()? true : false;
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/app/models/Permission.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 |
16 | class Permission {
17 |
18 | /**
19 | * allowed permissions for actions on specific resources
20 | *
21 | * $perms[] = [
22 | * 'role' => 'student', //AROs
23 | * 'resource' => 'Post' //ACOs - actions could be ACOs instead.
24 | * 'actions' => ['edit', 'delete'],
25 | * 'conditions' => ['owner'] - things that you validate against if the user has access to the action
26 | * ];
27 | *
28 | * @var array
29 | */
30 | public static $perms = [];
31 |
32 | /**
33 | * check if the $role has access to $action on $resource
34 | *
35 | * @param string $role
36 | * @param string $resource
37 | * @param string $action if set to "*", then check if $actions parameter was assigned to "*" when using allow() method
38 | * This indicates the $role has access to all actions on $resource
39 | * @param array $config configuration data to be passed to condition methods
40 | * @throws Exception if $config is empty or method doesn't exists
41 | * @return boolean
42 | */
43 | public static function check($role, $resource, $action = "*", array $config = []){
44 |
45 | // checks if action was allowed at least once
46 | $allowed = false;
47 | $action = strtolower($action);
48 |
49 | foreach(self::$perms as $perm){
50 | if($perm['role'] === $role && $perm['resource'] === $resource){
51 |
52 | if(in_array($action, $perm["actions"], true) || $perm["actions"] === ["*"]){
53 |
54 | $allowed = true;
55 |
56 | foreach($perm["conditions"] as $condition){
57 |
58 | if (!method_exists(__CLASS__, $condition)) {
59 | throw new Exception("Permission, Method doesnt exists: " . $condition);
60 | }
61 |
62 | if(self::$condition($config) === false){
63 | Logger::log("Permission", $role . " is not allowed to perform '" . $action . "' action on " . $resource . " because of " . $condition, __FILE__, __LINE__);
64 | return false;
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | if(!$allowed){
72 | Logger::log("Permission", $role . " is not allowed to perform '" . $action . "' action on " . $resource, __FILE__, __LINE__);
73 | }
74 |
75 | return $allowed;
76 | }
77 |
78 | /**
79 | * Add new rule: allow a $role for $actions on $resource,
80 | * You may add additional $conditions that must be fulfilled as well.
81 | *
82 | * @param string $role
83 | * @param string $resource
84 | * @param mixed $actions
85 | * @param mixed $conditions
86 | */
87 | public static function allow($role, $resource, $actions = "*", $conditions = []){
88 |
89 | $actions = array_map("strtolower", (array)$actions);
90 |
91 | self::$perms[] = ['role' => $role, 'resource' => $resource, 'actions' => $actions, 'conditions' => (array)$conditions];
92 | }
93 |
94 | /**
95 | * deny or remove $actions for a $role on $resource
96 | *
97 | * @param string $role
98 | * @param string $resource
99 | * @param mixed $actions
100 | */
101 | public static function deny($role, $resource, $actions = "*"){
102 |
103 | $actions = array_map("strtolower", (array)$actions);
104 |
105 | foreach(self::$perms as $key => &$perm){
106 | if($perm['role'] === $role && $perm['resource'] === $resource){
107 | foreach($perm['actions'] as $index => $action){
108 | if(in_array($action, $actions, true) || $actions === ["*"]){
109 | unset($perm['actions'][$index]);
110 | }
111 | }
112 |
113 | if(empty($perm['actions'])){
114 | unset(self::$perms[$key]);
115 | }
116 | }
117 | }
118 | }
119 |
120 | /** *********************************************** **/
121 | /** ************** Conditions ************** **/
122 | /** *********************************************** **/
123 |
124 | /**
125 | * checks if user is owner
126 | *
127 | * @param array $config
128 | * @return bool
129 | */
130 | private static function owner($config){
131 |
132 | $database = Database::openConnection();
133 |
134 | $database->prepare('SELECT * FROM '.$config["table"]. ' WHERE id = :id AND user_id = :user_id LIMIT 1');
135 | $database->bindValue(':id', (int)$config["id"]);
136 | $database->bindValue(':user_id', (int)$config["user_id"]);
137 | $database->execute();
138 |
139 | return $database->countRows() === 1;
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/app/models/Post.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class Post extends Model{
11 |
12 | /**
13 | * get all posts
14 | *
15 | * @access public
16 | * @param integer $pageNum
17 | * @return array Associative array of the posts, and Pagination Object.
18 | *
19 | */
20 | public function getAll($pageNum = 1){
21 |
22 | $pagination = Pagination::pagination("posts", "", [], $pageNum);
23 | $offset = $pagination->getOffset();
24 | $limit = $pagination->perPage;
25 |
26 | $database = Database::openConnection();
27 | $query = "SELECT posts.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, posts.title, posts.content, posts.date ";
28 | $query .= "FROM users, posts ";
29 | $query .= "WHERE users.id = posts.user_id ";
30 | $query .= "ORDER BY posts.date DESC ";
31 | $query .= "LIMIT $limit OFFSET $offset";
32 |
33 | $database->prepare($query);
34 | $database->execute();
35 | $posts = $database->fetchAllAssociative();
36 |
37 | $this->appendNumberOfComments($posts, $database);
38 |
39 | return array("posts" => $posts, "pagination" => $pagination);
40 | }
41 |
42 | /**
43 | * append number of comments to the array of posts for each post.
44 | *
45 | * @access private
46 | * @param array
47 | *
48 | */
49 | private function appendNumberOfComments(&$posts){
50 |
51 | $postId = 0;
52 | $database = Database::openConnection();
53 |
54 | $query = "SELECT COUNT(*) AS comments FROM comments WHERE post_id = :post_id ";
55 | $database->prepare($query);
56 | $database->bindParam(':post_id', $postId);
57 |
58 | foreach($posts as $key => $post){
59 | $postId = (int)$posts[$key]["id"];
60 | $database->execute();
61 | $posts[$key]["comments"] = $database->fetchAssociative()["comments"];
62 | }
63 | }
64 |
65 | /**
66 | * get post by Id.
67 | *
68 | * @access public
69 | * @param integer $postId
70 | * @return array Array holds the data of the post
71 | */
72 | public function getById($postId){
73 |
74 | $database = Database::openConnection();
75 | $query = "SELECT posts.id AS id, users.profile_picture, users.id AS user_id, users.name AS user_name, posts.title, posts.content, posts.date ";
76 | $query .= "FROM users, posts ";
77 | $query .= "WHERE posts.id = :id ";
78 | $query .= "AND users.id = posts.user_id LIMIT 1 ";
79 |
80 | $database->prepare($query);
81 | $database->bindValue(':id', $postId);
82 | $database->execute();
83 |
84 | $post = $database->fetchAssociative();
85 | return $post;
86 | }
87 |
88 | /**
89 | * create post
90 | *
91 | * @access public
92 | * @param integer $userId
93 | * @param string $title
94 | * @param string $content
95 | * @return bool
96 | * @throws Exception If post couldn't be created
97 | *
98 | */
99 | public function create($userId, $title, $content){
100 |
101 | $validation = new Validation();
102 | if(!$validation->validate([
103 | 'Title' => [$title, "required|minLen(2)|maxLen(60)"],
104 | 'Content' => [$content, "required|minLen(4)|maxLen(1800)"]])) {
105 | $this->errors = $validation->errors();
106 | return false;
107 | }
108 |
109 | $database = Database::openConnection();
110 | $query = "INSERT INTO posts (user_id, title, content) VALUES (:user_id, :title, :content)";
111 |
112 | $database->prepare($query);
113 | $database->bindValue(':user_id', $userId);
114 | $database->bindValue(':title', $title);
115 | $database->bindValue(':content', $content);
116 | $database->execute();
117 |
118 | if($database->countRows() !== 1){
119 | throw new Exception ("Couldn't add news feed");
120 | }
121 |
122 | return true;
123 | }
124 |
125 | /**
126 | * update Post
127 | *
128 | * @access public
129 | * @static static method
130 | * @param string $postId
131 | * @param string $title
132 | * @param string $content
133 | * @return array Array of the updated post
134 | * @throws Exception If post couldn't be updated
135 | *
136 | */
137 | public function update($postId, $title, $content){
138 |
139 | $validation = new Validation();
140 | if(!$validation->validate([
141 | 'Title' => [$title, "required|minLen(2)|maxLen(60)"],
142 | 'Content' => [$content, "required|minLen(4)|maxLen(1800)"]])) {
143 | $this->errors = $validation->errors();
144 | return false;
145 | }
146 |
147 | $database = Database::openConnection();
148 | $query = "UPDATE posts SET title = :title, content = :content WHERE id = :id LIMIT 1";
149 |
150 | $database->prepare($query);
151 | $database->bindValue(':title', $title);
152 | $database->bindValue(':content', $content);
153 | $database->bindValue(':id', $postId);
154 | $result = $database->execute();
155 |
156 | if(!$result){
157 | throw new Exception("Couldn't update post of ID: " . $postId);
158 | }
159 |
160 | $post = $this->getById($postId);
161 | return $post;
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/app/models/Todo.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class Todo extends Model{
11 |
12 | public function getAll(){
13 |
14 | $database = Database::openConnection();
15 | $query = "SELECT todo.id AS id, users.id AS user_id, users.name AS user_name, todo.content ";
16 | $query .= "FROM users, todo ";
17 | $query .= "WHERE users.id = todo.user_id ";
18 |
19 | $database->prepare($query);
20 | $database->execute();
21 | $todo = $database->fetchAllAssociative();
22 |
23 | return $todo;
24 | }
25 |
26 | public function create($userId, $content){
27 |
28 | // using validation class
29 | $validation = new Validation();
30 | if(!$validation->validate(['Content' => [$content, "required|minLen(4)|maxLen(300)"]])) {
31 | $this->errors = $validation->errors();
32 | return false;
33 | }
34 |
35 | // using database class to insert new todo
36 | $database = Database::openConnection();
37 | $query = "INSERT INTO todo (user_id, content) VALUES (:user_id, :content)";
38 | $database->prepare($query);
39 | $database->bindValue(':user_id', $userId);
40 | $database->bindValue(':content', $content);
41 | $database->execute();
42 |
43 | if($database->countRows() !== 1){
44 | throw new Exception("Couldn't create todo");
45 | }
46 |
47 | return true;
48 | }
49 |
50 | public function delete($id){
51 |
52 | $database = Database::openConnection();
53 | $database->deleteById("todo", $id);
54 |
55 | if($database->countRows() !== 1){
56 | throw new Exception ("Couldn't delete todo");
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/uploads/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
--------------------------------------------------------------------------------
/app/utility/Utility.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class Utility{
12 |
13 | private function __construct(){}
14 |
15 | /**
16 | * Normalizes an array, and converts it to a standard format.
17 | *
18 | * @param array $arr
19 | * @return array normalized array
20 | */
21 | public static function normalize($arr){
22 |
23 | $keys = array_keys($arr);
24 | $count = count($keys);
25 |
26 | $newArr = [];
27 | for ($i = 0; $i < $count; $i++) {
28 | if (is_int($keys[$i])) {
29 | $newArr[$arr[$keys[$i]]] = null;
30 | } else {
31 | $newArr[$keys[$i]] = $arr[$keys[$i]];
32 | }
33 | }
34 | return $newArr;
35 | }
36 |
37 | /**
38 | * returns a string by separating array elements with commas
39 | *
40 | * @param array $arr
41 | * @return array
42 | */
43 | public static function commas($arr){
44 | return implode(",", (array)$arr);
45 | }
46 |
47 | /**
48 | * Merging two arrays
49 | *
50 | * @param mixed $arr1
51 | * @param mixed $arr2
52 | * @return array The merged array
53 | *
54 | */
55 | public static function merge($arr1, $arr2){
56 | return array_merge((array)$arr1, (array)$arr2);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/views/admin/backups.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Backups
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | File |
24 | Last Update |
25 | Update |
26 | Restore |
27 |
28 |
29 |
30 |
31 | controller->admin->getBackups();
33 | $isBackupExists = true;
34 | if(empty($data["basename"]) || empty($data["filename"])){
35 | $data["filename"] = "Not Available";
36 | $data["date"] = "-";
37 | $isBackupExists = false;
38 | }
39 | ?>
40 |
41 | ">
42 | = $data["filename"]; ?>
43 | |
44 | = $data["date"]; ?> |
45 |
46 |
47 | "
49 | class="btn btn-success update-backup" >
50 |
51 |
52 |
53 | |
54 |
55 |
56 | "
58 | class="btn btn-danger restore-backup" >
59 |
60 |
61 |
62 | |
63 |
64 |
65 |
66 | renderErrors(Session::getAndDestroy('backup-errors'));
69 | }else if(!empty(Session::get('backup-success'))){
70 | echo $this->renderSuccess(Session::getAndDestroy('backup-success'));
71 | }
72 | ?>
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/app/views/admin/users/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Users
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Name |
68 | Role |
69 | Email |
70 | |
71 |
72 |
73 |
74 | controller->admin->getUsers();
76 | echo $this->render(Config::get('ADMIN_VIEWS_PATH') . "users/users.php", array("users" => $usersData["users"]));
77 | ?>
78 |
79 |
80 |
81 |
82 |
83 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/app/views/admin/users/users.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | There is no users |
4 |
5 |
7 | ">
8 | = $user["name"]; ?> |
9 | = ucfirst($user["role"]); ?> |
10 |
11 | Not Available |
12 |
13 | = $this->encodeHTML($user["email"]); ?> |
14 |
15 |
16 |
17 | " class="btn btn-default">
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 | |
28 |
29 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/views/admin/users/viewUser.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 | controller->user->getProfileInfo($userId);
16 | ?>
17 |
18 | Update Profile
19 |
20 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/app/views/alerts/errors.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | System Error!
7 |
8 | Oops! There was an error, Please try again later or report a bug
9 |
10 |
11 |
12 |
13 | Heads Up!
14 |
15 |
16 | = $error; ?>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/views/alerts/success.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | = $this->encodeHTML($success);?>
5 |
--------------------------------------------------------------------------------
/app/views/bugs/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Report Bugs, Features & Enhancements
16 |
17 |
18 |
19 |
20 |
21 |
22 |
54 | renderSuccess(Session::getAndDestroy('report-bug-success')); } ?>
55 | renderErrors(Session::getAndDestroy('report-bug-errors'));
58 | }
59 | ?>
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/views/dashboard/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 | controller->user->dashboard();
16 | $updates = $data["updates"];
17 | $stats = $data["stats"];
18 | ?>
19 |
20 |
21 |
22 |
23 |
24 |
= $stats["newsfeed"]; ?>
25 |
News Feed
26 |
27 |
28 |
29 |
">
30 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
= $stats["posts"]; ?>
47 |
Posts
48 |
49 |
50 |
51 |
">
52 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
= $stats["files"]; ?>
69 |
Files
70 |
71 |
72 |
73 |
">
74 |
79 |
80 |
81 |
82 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
121 |
Latest Updates
122 |
123 |
124 |
125 |
126 | = $this->render(Config::get('VIEWS_PATH') . "dashboard/updates.php", array("updates" => $updates));?>
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/app/views/dashboard/updates.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | There is no updates yet!
4 |
5 |
6 |
7 |
8 |
9 | ";
14 | break;
15 | case "posts":
16 | $logo = "";
17 | break;
18 | case "files":
19 | $logo = "";
20 | break;
21 | }
22 | ?>
23 |
24 | = $logo; ?>
25 |
26 | = $this->truncate($this->encodeHTML($update["title"]),75); ?>
27 | By = $update["name"];?>
28 | = $this->timestamp($update["date"]);?>
29 |
30 |
--------------------------------------------------------------------------------
/app/views/errors/400.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/views/errors/401.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/views/errors/403.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/views/errors/404.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/views/errors/500.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/views/files/files.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | There is no files! |
4 |
5 |
7 |
8 | ">
9 | = $file["user_name"];?>
10 | = $this->timestamp($file["date"]);?>
11 | |
12 |
13 | ">
14 | = $this->truncate($this->encodeHTML($file["filename"]),20); ?>
15 | |
16 |
17 | = strtoupper($file["format"]); ?> |
18 |
19 |
20 | |
21 |
22 |
23 |
25 |
26 |
--------------------------------------------------------------------------------
/app/views/files/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Upload File
17 |
18 |
19 |
20 |
21 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Files
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | Author |
56 | File |
57 | Format |
58 | |
59 |
60 |
61 |
62 | controller->file->getAll(empty($pageNum)? 1: $pageNum);
64 | echo $this->render(Config::get('VIEWS_PATH') . "files/files.php", array("files" => $filesData["files"]));
65 | ?>
66 |
67 |
68 |
69 |
70 |
71 |
72 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/views/layout/default/footer.php:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |