Selasa 19 November 2019
This commit is contained in:
parent
7803ea548d
commit
678dadb1ab
41
Application/Controllers/ApiController.php
Normal file
41
Application/Controllers/ApiController.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Application\Controllers;
|
||||
|
||||
use Application\HTTP\Request;
|
||||
use Application\HTTP\Response;
|
||||
use Application\Services\ServiceContainer;
|
||||
use Application\Models\Category;
|
||||
use Application\Models\Thread;
|
||||
use Application\Models\UserAction;
|
||||
use Application\Foundations\QueryBuilder;
|
||||
|
||||
class ApiController {
|
||||
public function __construct() {
|
||||
|
||||
}
|
||||
public function categories(Request $request, Response $response) {
|
||||
$bans = [];
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('user_id',ServiceContainer::Session()->get('user_id'))->where('expired_at','>',date('Y-m-d H:i:s'))->where('action_type','ban')->orderBy('expired_at','desc');
|
||||
$actions = UserAction::select($where);
|
||||
$bans = array_map(function($action) {
|
||||
return (int)$action->category_id;
|
||||
}, $actions);
|
||||
}
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('group_id',$request->id);
|
||||
if(count($bans) > 0) {
|
||||
$where = $where->whereNotIn('id',$bans);
|
||||
}
|
||||
$categories = Category::select($where);
|
||||
return $response->json()->data($categories);
|
||||
}
|
||||
public function threads(Request $request, Response $response) {
|
||||
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('category_id',$request->id);
|
||||
$threads = Thread::select($where);
|
||||
return $response->json()->data($threads);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ use Application\Foundations\QueryBuilder;
|
||||
use Application\Foundations\MailBuilder;
|
||||
use Application\Models\User;
|
||||
use Application\Models\UserConfirmation;
|
||||
use Application\Models\UserChange;
|
||||
|
||||
class AuthController {
|
||||
public function __construct() {
|
||||
@ -17,7 +18,7 @@ class AuthController {
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
return $response->redirect('/');
|
||||
}
|
||||
return $response->view('sign-up');
|
||||
return $response->view('auth/sign-up');
|
||||
}
|
||||
public function create_user(Request $request, Response $response) {
|
||||
if($request->email == "") {
|
||||
@ -45,6 +46,14 @@ class AuthController {
|
||||
[ 'errors' => 'Password and confirm password must be the same' ]
|
||||
);
|
||||
}
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select('id')->from('user')->where('username',$request->username)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
if(count($result) > 0) {
|
||||
return $response->redirect("/signup")->with(
|
||||
[ 'errors' => 'Username is already taken' ]
|
||||
);
|
||||
}
|
||||
ServiceContainer::Session()->unset('errors');
|
||||
$data = User::create([
|
||||
'id' => null,
|
||||
@ -55,6 +64,7 @@ class AuthController {
|
||||
'avatar_path' => '',
|
||||
'password' => password_hash($request->password,PASSWORD_DEFAULT),
|
||||
'is_confirmed' => 0,
|
||||
'role' => 0,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
@ -72,19 +82,17 @@ class AuthController {
|
||||
$email->from("metaforums@nanao.moe")->to($data->email)->subject("Complete your registration on Metaforums")->body($body);
|
||||
ServiceContainer::Email()->send($email);
|
||||
if($confirmator != null) {
|
||||
return $response->redirect('/signup/success')->with(
|
||||
return $response->redirect('/login')->with(
|
||||
[ 'signup-email' => $request->email ],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function sign_up_success(Request $request, Response $response) {
|
||||
return $response->view('sign-up-success');
|
||||
}
|
||||
public function sign_up_confirm(Request $request, Response $response) {
|
||||
$confirm = UserConfirmation::find($request->queryString());
|
||||
if(isset($confirm)) {
|
||||
if(isset($confirm) && strtotime($confirm->best_before) > time() ) {
|
||||
$id = $confirm->user_id;
|
||||
|
||||
$user = User::find($id);
|
||||
$user->update([ 'is_confirmed' => 1 ]);
|
||||
$confirm->delete();
|
||||
@ -95,39 +103,102 @@ class AuthController {
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
return $response->redirect('/');
|
||||
}
|
||||
return $response->view('login');
|
||||
return $response->view('auth/login');
|
||||
}
|
||||
public function login_check(Request $request, Response $response) {
|
||||
if ($request->username == "") {
|
||||
return $response->redirect("/login")->with(
|
||||
[ 'errors' => 'Username must not be empty' ]
|
||||
);
|
||||
} else if ($request->password == "") {
|
||||
return $response->redirect("/login")->with(
|
||||
[ 'errors' => 'Password must not be empty' ]
|
||||
);
|
||||
}
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select('id,password')->from('user')->where('username',$request->username)->where('is_confirmed',1)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
if(count($result) == 0) {
|
||||
$query = $query->where('username',$request->username)->orWhere('email',$request->username);
|
||||
$result = User::selectOne($query);
|
||||
if($result == null) {
|
||||
if(filter_var($request->username,FILTER_VALIDATE_EMAIL)) {
|
||||
return $response->redirect("/login")->with(
|
||||
[ 'errors' => 'Wrong username or password' ]
|
||||
[ 'errors' => 'Email is not associated with an account' ]
|
||||
);
|
||||
} else {
|
||||
$password = $result[0]["password"];
|
||||
return $response->redirect("/login")->with(
|
||||
[ 'errors' => 'Username does not exist' ]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$password = $result->password;
|
||||
$verify = password_verify($request->password,$password);
|
||||
if(!$verify) {
|
||||
return $response->redirect("/login")->with(
|
||||
[ 'errors' => 'Wrong username or password' ]
|
||||
[ 'errors' => 'Invalid password' ]
|
||||
);
|
||||
}
|
||||
}
|
||||
ServiceContainer::Session()->set('user_id',$result[0]['id']);
|
||||
$result->update([ 'logged_in' => 1, 'last_login' => date('Y-m-d H:i:s') ]);
|
||||
ServiceContainer::Session()->unset('errors');
|
||||
ServiceContainer::Session()->set('user_id',$result->id);
|
||||
return $response->redirect('/');
|
||||
}
|
||||
public function logout(Request $request, Response $response) {
|
||||
$user = User::find(ServiceContainer::Session()->get('user_id'));
|
||||
$user->update([ 'logged_in' => 0]);
|
||||
ServiceContainer::Session()->destroy();
|
||||
return $response->redirect('/login');
|
||||
}
|
||||
public function forget_password(Request $request, Response $response) {
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
return $response->redirect("/");
|
||||
}
|
||||
return $response->view('auth/forgot');
|
||||
}
|
||||
public function forget_password_confirm(Request $request, Response $response) {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select('id,username,email')->from('user')->where('email',$request->email)->where('is_confirmed',1)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
if(count($result) > 0) {
|
||||
$confirmator = UserChange::create([
|
||||
'user_id' => $result[0]["id"],
|
||||
'action_type' => 'password_reset',
|
||||
'confirm_key' => hash('sha256',$result[0]['username'].time()),
|
||||
'best_before' => date('Y-m-d H:i:s', strtotime('+6 hours', time())),
|
||||
]);
|
||||
$email = new MailBuilder();
|
||||
$body = "I heard you forgot your password.\n";
|
||||
$body .= "To reset your password, use the URL below:\n\n";
|
||||
$body .= $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'].'/login/reset?'.$confirmator->confirm_key;
|
||||
$email->from("metaforums@nanao.moe")->to($result[0]['email'])->subject("Someone asked to reset your password")->body($body);
|
||||
ServiceContainer::Email()->send($email);
|
||||
}
|
||||
return $response->redirect("/login/forget")->with([ 'forget_message' => 'We have sent a reset password link to the provided e-mail. If there is an account associated with the e-mail, the e-mail will be received in the inbox.' ]);
|
||||
}
|
||||
public function reset_password(Request $request, Response $response) {
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
return $response->redirect("/");
|
||||
}
|
||||
$where = new QueryBuilder();
|
||||
$where->where('confirm_key',$request->queryString())->where('action_type','password_reset')->where("best_before",">",date('Y-m-d H:i:s'));
|
||||
$confirmator = UserChange::selectOne($where);
|
||||
if(!isset($confirmator)) {
|
||||
return $response->redirect("/");
|
||||
}
|
||||
return $response->view('auth/reset', [ 'key' => $request->queryString() ]);
|
||||
}
|
||||
public function reset_password_confirm(Request $request, Response $response) {
|
||||
if(ServiceContainer::Authentication()->isLoggedIn()) {
|
||||
return $response->redirect("/");
|
||||
}
|
||||
$where = new QueryBuilder();
|
||||
$where->where('confirm_key',$request->confirm_key)->where('action_type','password_reset')->where("best_before",">",date('Y-m-d H:i:s'));
|
||||
$confirmator = UserChange::selectOne($where);
|
||||
if(!isset($confirmator)) {
|
||||
return $response->redirect("/");
|
||||
}
|
||||
if (strlen($request->password) < 8) {
|
||||
return $response->redirect("/login/reset?".$request->confirm_key)->with(
|
||||
[ 'errors' => 'Password must be at least 8 characters' ]
|
||||
);
|
||||
} else if ($request->password != $request->confirmpassword) {
|
||||
return $response->redirect("/login/reset?".$request->confirm_key)->with(
|
||||
[ 'errors' => 'Password and confirm password must be the same' ]
|
||||
);
|
||||
}
|
||||
$user = User::find($confirmator->user_id);
|
||||
$user->update(['password' => password_hash($request->password,PASSWORD_DEFAULT) ]);
|
||||
$confirmator->delete();
|
||||
return $response->redirect("/login");
|
||||
}
|
||||
}
|
||||
|
92
Application/Controllers/ForumThreadController.php
Normal file
92
Application/Controllers/ForumThreadController.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace Application\Controllers;
|
||||
|
||||
use Application\HTTP\Request;
|
||||
use Application\HTTP\Response;
|
||||
use Application\Services\ServiceContainer;
|
||||
use Application\Models\Category;
|
||||
use Application\Models\Post;
|
||||
use Application\Models\Thread;
|
||||
use Application\Models\UserAction;
|
||||
use Application\Foundations\QueryBuilder;
|
||||
|
||||
class ForumThreadController {
|
||||
public function __construct() {
|
||||
|
||||
}
|
||||
public function forum(Request $request, Response $response) {
|
||||
$thread = Thread::find($request->id);
|
||||
return $response->view('thread', [ 'thread' => $thread ] );
|
||||
}
|
||||
public function process(Request $request, Response $response) {
|
||||
$user = ServiceContainer::Authentication()->user();
|
||||
$eligible = true;
|
||||
if($user->is_confirmed == 0) {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->where("user_id",$user->id)->where("created_at",">",date("Y-m-d H:i:s",strtotime(" - 24 hours")));
|
||||
$posts = Post::select($query);
|
||||
if(count($posts) < 0) {
|
||||
$eligible = false;
|
||||
}
|
||||
}
|
||||
$category = Category::find($request->category);
|
||||
$thread = Thread::find($request->thread);
|
||||
$reply = Post::find($request->reply);
|
||||
$edit = Post::find($request->edit);
|
||||
if(isset($edit)) {
|
||||
$edit->update([ 'post' => $request->content, 'updated_at' => date("Y-m-d H:i:s") ]);
|
||||
return $response->redirect('/');
|
||||
} else if (isset($thread) && isset($reply)) {
|
||||
$title = $reply->title;
|
||||
if(strpos($reply->title,"Re: ") != 0) {
|
||||
$title .= "Re: ".$reply->title;
|
||||
}
|
||||
if($eligible) {
|
||||
Post::create([
|
||||
'thread_id' => $thread->id,
|
||||
'user_id' => $user->id,
|
||||
'title' => $title,
|
||||
'post' => $request->content,
|
||||
'created_at' => date("Y-m-d H:i:s"),
|
||||
'updated_at' => date("Y-m-d H:i:s"),
|
||||
]);
|
||||
}
|
||||
return $response->redirect('/');
|
||||
} else if (isset($category)) {
|
||||
$title = $request->title;
|
||||
$thread = Thread::create([
|
||||
'category_id' => $category->id,
|
||||
'title' => $title,
|
||||
'author' => $user->id,
|
||||
'created_at' => date("Y-m-d H:i:s"),
|
||||
'updated_at' => date("Y-m-d H:i:s"),
|
||||
]);
|
||||
Post::create([
|
||||
'thread_id' => $thread->id,
|
||||
'user_id' => $user->id,
|
||||
'title' => $title,
|
||||
'post' => $request->content,
|
||||
'created_at' => date("Y-m-d H:i:s"),
|
||||
'updated_at' => date("Y-m-d H:i:s"),
|
||||
]);
|
||||
return $response->redirect('/');
|
||||
}
|
||||
}
|
||||
public function editor(Request $request, Response $response) {
|
||||
$title = "";
|
||||
$category = Category::find($request->category);
|
||||
$thread = Thread::find($request->thread);
|
||||
$reply = Post::find($request->reply);
|
||||
$edit = Post::find($request->edit);
|
||||
if(isset($edit)) {
|
||||
$title = "Editing post";
|
||||
} else if (isset($thread) && isset($reply) && $thread->main_post->id == $reply->id ) {
|
||||
$title = "Replying to Main Post";
|
||||
} else if (isset($thread) && isset($reply)) {
|
||||
$title = "Replying to ".$reply->user()->username;
|
||||
} else if (isset($category)) {
|
||||
$title = "Creating Thread to ".$category->category_name;
|
||||
}
|
||||
return $response->view('editor', [ 'title' => $title, 'category' => $request->category, 'thread' => $request->thread, 'edit' => $request->edit, 'reply' => $request->reply, 'edit_post' => $edit ] );
|
||||
}
|
||||
}
|
@ -4,12 +4,28 @@ namespace Application\Controllers;
|
||||
use Application\HTTP\Request;
|
||||
use Application\HTTP\Response;
|
||||
use Application\Services\ServiceContainer;
|
||||
use Application\Models\Category;
|
||||
use Application\Models\Group;
|
||||
use Application\Models\Thread;
|
||||
|
||||
class IndexController {
|
||||
public function __construct() {
|
||||
|
||||
}
|
||||
public function index(Request $request, Response $response) {
|
||||
return $response->view('index');
|
||||
$groups = Group::all();
|
||||
$group = null;
|
||||
$category = null;
|
||||
$thread = null;
|
||||
if(isset($request->group)) {
|
||||
$group = Group::find($request->group);
|
||||
}
|
||||
if(isset($request->category)) {
|
||||
$category = Category::find($request->category);
|
||||
}
|
||||
if(isset($request->thread)) {
|
||||
$thread = Thread::find($request->thread);
|
||||
}
|
||||
return $response->view('index', ['groups' => $groups, 'group' => $group, 'category' => $category, 'thread' => $thread ] );
|
||||
}
|
||||
}
|
||||
|
35
Application/Foundations/DateHelper.php
Normal file
35
Application/Foundations/DateHelper.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace Application\Foundations;
|
||||
|
||||
class DateHelper {
|
||||
public function elapsedString($date) {
|
||||
$unixTimestamp = strtotime($date);
|
||||
$now = time();
|
||||
$dateDiff = $now - $unixTimestamp;
|
||||
$measure = "";
|
||||
$ago = "";
|
||||
if($dateDiff > 0) {
|
||||
$ago .= " ago";
|
||||
} else if($dateDiff < 0) {
|
||||
$ago .= " later";
|
||||
}
|
||||
$dateDiff = abs($dateDiff);
|
||||
$seconds = $dateDiff;
|
||||
$minutes = intval($dateDiff / 60);
|
||||
$hours = intval($dateDiff / 3600);
|
||||
$days = intval($dateDiff / 86400);
|
||||
$years = intval($dateDiff / (365 * 86400));
|
||||
if($years > 0) {
|
||||
$measure = $years." years";
|
||||
} else if($days > 0) {
|
||||
$measure = $days." days";
|
||||
} else if($hours > 0) {
|
||||
$measure = $hours." hours";
|
||||
} else if($minutes > 0) {
|
||||
$measure = $minutes." minutes";
|
||||
} else if($seconds > 0) {
|
||||
$measure = $seconds." seconds";
|
||||
}
|
||||
return $measure.$ago;
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ namespace Application\Foundations;
|
||||
|
||||
use Application\Services\ServiceContainer;
|
||||
|
||||
class Model {
|
||||
class Model implements \JsonSerializable {
|
||||
public $attributes;
|
||||
protected $primary_key = 'id';
|
||||
|
||||
@ -21,7 +21,7 @@ class Model {
|
||||
$data['id'] = $result;
|
||||
}
|
||||
$inst = new $calling_class();
|
||||
$inst->hydrate($result);
|
||||
$inst->hydrate($data);
|
||||
return $inst;
|
||||
}
|
||||
public static function find($key) {
|
||||
@ -36,6 +36,38 @@ class Model {
|
||||
$inst->hydrate($result[0]);
|
||||
return $inst;
|
||||
}
|
||||
public static function select($where_query) {
|
||||
$calling_class = get_called_class();
|
||||
$class = explode('\\',get_called_class());
|
||||
$tablename = strtolower($class[count($class)-1]);
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select('*')->from($tablename)->build();
|
||||
$query .= $where_query->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
if(count($result) == 0) return [];
|
||||
foreach($result as $key => $val) {
|
||||
$inst = new $calling_class();
|
||||
$inst->hydrate($result[$key]);
|
||||
$result[$key] = $inst;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
public static function selectOne($where_query) {
|
||||
$calling_class = get_called_class();
|
||||
$class = explode('\\',get_called_class());
|
||||
$tablename = strtolower($class[count($class)-1]);
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select('*')->from($tablename)->build();
|
||||
$query .= $where_query->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
if(count($result) == 0) return null;
|
||||
$inst = new $calling_class();
|
||||
$inst->hydrate($result[0]);
|
||||
return $inst;
|
||||
}
|
||||
public static function all() {
|
||||
return self::select(new QueryBuilder());
|
||||
}
|
||||
public function update($key) {
|
||||
$calling_class = get_called_class();
|
||||
$class = explode('\\',get_called_class());
|
||||
@ -61,11 +93,26 @@ class Model {
|
||||
}
|
||||
}
|
||||
function __get($prop) {
|
||||
$methodName = $prop."_attribute";
|
||||
if(method_exists($this,$methodName)) {
|
||||
return $this->$methodName();
|
||||
}
|
||||
return $this->attributes[$prop];
|
||||
}
|
||||
|
||||
function __set($prop, $val) {
|
||||
$this->attributes[$prop] = $val;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
$data = $this->attributes;
|
||||
$attr = get_class_methods($this);
|
||||
$attr = array_filter($attr, function ($var) {
|
||||
return strpos($var, "_attribute") !== false;
|
||||
});
|
||||
foreach($attr as $attr) {
|
||||
$attrName = substr($attr,0,strlen($attr) - strlen("_attribute"));
|
||||
$data[$attrName] = $this->$attr();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace Application\Foundations;
|
||||
class QueryBuilder {
|
||||
private $query = "";
|
||||
private $where = "";
|
||||
private $misc = "";
|
||||
public function select($fields) {
|
||||
if(!is_array($fields)) {
|
||||
$this->query .= "SELECT ".$fields;
|
||||
@ -12,6 +13,10 @@ class QueryBuilder {
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function orderBy($column, $order = 'asc') {
|
||||
$this->misc .= " ORDER BY ".$column." ".strtoupper($order);
|
||||
return $this;
|
||||
}
|
||||
public function delete() {
|
||||
$this->query .= "DELETE";
|
||||
return $this;
|
||||
@ -32,7 +37,7 @@ class QueryBuilder {
|
||||
}
|
||||
public function from($table) {
|
||||
// TODO: SQL injection
|
||||
$this->query .= " FROM ".$table;
|
||||
$this->query .= " FROM `".$table."`";
|
||||
return $this;
|
||||
}
|
||||
public function where($a, $b, $c = null) {
|
||||
@ -56,6 +61,26 @@ class QueryBuilder {
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function whereIn($a, $b) {
|
||||
$field = $a;
|
||||
$value = SQLHelper::encode_list($b);
|
||||
if($this->where == "") {
|
||||
$this->where .= " WHERE ".$field." IN (".implode(",",$value).")";
|
||||
} else {
|
||||
$this->where .= " AND ".$field." IN (".implode(",",$value).")";
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function whereNotIn($a, $b) {
|
||||
$field = $a;
|
||||
$value = SQLHelper::encode_list($b);
|
||||
if($this->where == "") {
|
||||
$this->where .= " WHERE ".$field." NOT IN (".implode(",",$value).")";
|
||||
} else {
|
||||
$this->where .= " AND ".$field." NOT IN (".implode(",",$value).")";
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function orWhere($a, $b, $c = null) {
|
||||
$field = "";
|
||||
$value = "";
|
||||
@ -69,6 +94,7 @@ class QueryBuilder {
|
||||
$value = $c;
|
||||
$operator = $b;
|
||||
}
|
||||
$value = SQLHelper::encode_literal($value);
|
||||
if($this->where == "") {
|
||||
$this->where .= " WHERE ".$field." ".$operator." ".$value;
|
||||
} else {
|
||||
@ -77,6 +103,6 @@ class QueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
public function build() {
|
||||
return $this->query.$this->where;
|
||||
return $this->query.$this->where.$this->misc;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ class Request {
|
||||
return $this->query;
|
||||
}
|
||||
function __get($prop) {
|
||||
if(!array_key_exists($prop,$this->data)) return "";
|
||||
return $this->data[$prop];
|
||||
}
|
||||
function __set($prop, $val) {
|
||||
|
8
Application/Models/Category.php
Normal file
8
Application/Models/Category.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class Category extends DBModel {
|
||||
|
||||
}
|
8
Application/Models/Group.php
Normal file
8
Application/Models/Group.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class Group extends DBModel {
|
||||
|
||||
}
|
8
Application/Models/ModeratorCategory.php
Normal file
8
Application/Models/ModeratorCategory.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class ModeratorCategory extends DBModel {
|
||||
|
||||
}
|
27
Application/Models/Post.php
Normal file
27
Application/Models/Post.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
use Application\Foundations\DateHelper;
|
||||
use Application\Foundations\QueryBuilder;
|
||||
use Application\Services\ServiceContainer;
|
||||
|
||||
class Post extends DBModel {
|
||||
public function user() {
|
||||
$user = User::Find($this->user_id);
|
||||
return $user;
|
||||
}
|
||||
public function elapsed_created_attribute() {
|
||||
return DateHelper::elapsedString($this->created_at);
|
||||
}
|
||||
public function favorites_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select("COUNT(user_id) AS count")->from("userfavorite")->where("post_id",$this->id)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
return $result[0]["count"];
|
||||
}
|
||||
public function is_main() {
|
||||
$id = Thread::find($this->thread_id)->main_post->id;
|
||||
return ($id == $this->id);
|
||||
}
|
||||
}
|
49
Application/Models/Thread.php
Normal file
49
Application/Models/Thread.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
use Application\Foundations\QueryBuilder;
|
||||
use Application\Foundations\DateHelper;
|
||||
use Application\Services\ServiceContainer;
|
||||
|
||||
class Thread extends DBModel {
|
||||
public function author_model_attribute() {
|
||||
return User::find($this->author);
|
||||
}
|
||||
public function post_count_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select("COUNT(id) AS count")->from("post")->where("thread_id",$this->id)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
return $result[0]["count"];
|
||||
}
|
||||
public function elapsed_created_attribute() {
|
||||
return DateHelper::elapsedString($this->created_at);
|
||||
}
|
||||
public function last_reply_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->where('thread_id',$this->id)->orderBy('created_at','desc');
|
||||
$post = Post::selectOne($query);
|
||||
return DateHelper::elapsedString($post->created_at);
|
||||
}
|
||||
public function main_post_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->where('thread_id',$this->id)->orderBy('created_at','asc');
|
||||
return Post::selectOne($query);
|
||||
}
|
||||
public function is_hot_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->where('thread_id',$this->id)->where('created_at','>',date('Y-m-d H:i:s',strtotime(' - 5 minutes')))->orderBy('created_at','desc');
|
||||
$post = Post::select($query);
|
||||
return count($post) > 10;
|
||||
}
|
||||
public function posts() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->where('thread_id',$this->id);
|
||||
$post = Post::select($query);
|
||||
return $post;
|
||||
}
|
||||
public function category() {
|
||||
$category = Category::Find($this->category_id);
|
||||
return $category;
|
||||
}
|
||||
}
|
@ -2,7 +2,46 @@
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
use Application\Foundations\QueryBuilder;
|
||||
use Application\Services\ServiceContainer;
|
||||
class User extends DBModel {
|
||||
public function is_moderator_attribute() {
|
||||
return ($this->role >= 2500);
|
||||
}
|
||||
public function is_admin_attribute() {
|
||||
return ($this->role >= 100000);
|
||||
}
|
||||
public function role_string_attribute() {
|
||||
if($this->is_admin) {
|
||||
return "Site Admin";
|
||||
} else if($this->is_moderator) {
|
||||
return "Moderator";
|
||||
}
|
||||
return "User";
|
||||
}
|
||||
public function post_count_attribute() {
|
||||
$query = new QueryBuilder();
|
||||
$query = $query->select("COUNT(id) AS count")->from("post")->where("user_id",$this->id)->build();
|
||||
$result = ServiceContainer::Database()->select($query);
|
||||
return $result[0]["count"];
|
||||
}
|
||||
public function isBanned($cat_id) {
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('user_id',$this->id)->where('category_id',$cat_id)->where('action_type','ban')->where('expired_at','>',date('Y-m-d H:i:s'))->orderBy('expired_at','desc');
|
||||
$actions = UserAction::select($where);
|
||||
return (count($actions) > 0);
|
||||
}
|
||||
public function isSilenced($cat_id) {
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('user_id',$this->id)->where('category_id',$cat_id)->where('action_type','silence')->where('expired_at','>',date('Y-m-d H:i:s'))->orderBy('expired_at','desc');
|
||||
$actions = UserAction::select($where);
|
||||
return (count($actions) > 0);
|
||||
}
|
||||
public function isPardoned($thread_id) {
|
||||
$where = new QueryBuilder();
|
||||
$where = $where->where('user_id',$this->id)->where('thread_id',$thread_id)->where('action_type','silence')->where('expired_at','>',date('Y-m-d H:i:s'))->orderBy('expired_at','desc');
|
||||
$actions = UserAction::select($where);
|
||||
return (count($actions) > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
8
Application/Models/UserAction.php
Normal file
8
Application/Models/UserAction.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class UserAction extends DBModel {
|
||||
|
||||
}
|
8
Application/Models/UserChange.php
Normal file
8
Application/Models/UserChange.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class UserChange extends DBModel {
|
||||
|
||||
}
|
8
Application/Models/UserFavorite.php
Normal file
8
Application/Models/UserFavorite.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class UserFavorite extends DBModel {
|
||||
|
||||
}
|
8
Application/Models/UserReport.php
Normal file
8
Application/Models/UserReport.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Models;
|
||||
|
||||
use Application\Foundations\Model as DBModel;
|
||||
|
||||
class UserReport extends DBModel {
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Application\Services;
|
||||
|
||||
use Application\Models\User;
|
||||
|
||||
class Authentication {
|
||||
public function __construct() {
|
||||
ServiceContainer::Session();
|
||||
@ -8,4 +10,21 @@ class Authentication {
|
||||
public function isLoggedIn() {
|
||||
return ServiceContainer::Session()->has('user_id');
|
||||
}
|
||||
public function isModerator() {
|
||||
if(!$this->isLoggedIn()) return false;
|
||||
$id = ServiceContainer::Session()->get('user_id');
|
||||
$user = User::find($id);
|
||||
return ($user->is_moderator);
|
||||
}
|
||||
public function isAdmin() {
|
||||
if(!$this->isLoggedIn()) return false;
|
||||
$id = ServiceContainer::Session()->get('user_id');
|
||||
$user = User::find($id);
|
||||
return ($user->is_admin);
|
||||
}
|
||||
public function user() {
|
||||
$id = ServiceContainer::Session()->get('user_id');
|
||||
$user = User::find($id);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class Database {
|
||||
if($result) {
|
||||
return mysqli_fetch_all($result,MYSQLI_ASSOC);
|
||||
} else {
|
||||
return null;
|
||||
return mysqli_error($this->conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,15 @@ class View {
|
||||
ob_start();
|
||||
extract($args);
|
||||
$auth = ServiceContainer::Authentication();
|
||||
$session = ServiceContainer::Session();
|
||||
$view = $this;
|
||||
$root = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'];
|
||||
include('Application/Views/'.$path.'.php');
|
||||
$rendered_string = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $rendered_string;
|
||||
}
|
||||
public function include($path, $args = []) {
|
||||
echo $this->render($path, $args);
|
||||
}
|
||||
}
|
||||
|
@ -660,34 +660,6 @@ main {
|
||||
min-height: calc(100vh - 8rem);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
main {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
padding-left: 4rem;
|
||||
padding-right: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
main {
|
||||
padding-left: 8rem;
|
||||
padding-right: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
main {
|
||||
padding-left: 16rem;
|
||||
padding-right: 16rem;
|
||||
}
|
||||
}
|
||||
|
||||
.header-link {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
@ -734,6 +706,173 @@ h1 {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
#forumbrowser {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.forumbrowser-group {
|
||||
height: 3rem;
|
||||
text-align: right;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
color: #3b90c6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forumbrowser-group.selected {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-group:hover {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-category {
|
||||
height: 3rem;
|
||||
text-align: right;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
color: #3b90c6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forumbrowser-category.selected {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-category:hover {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-thread-table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.forumbrowser-thread {
|
||||
text-align: left;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: table-row;
|
||||
height: 3rem;
|
||||
color: #3b90c6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forumbrowser-thread.selected {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-thread:hover {
|
||||
color: #fff;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forumbrowser-thread-col {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.forum-post {
|
||||
border-width: 1px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forum-post-title {
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: start;
|
||||
justify-content: flex-start;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #3b90c6;
|
||||
}
|
||||
|
||||
.forum-post-content {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.forum-post-user {
|
||||
width: 16.666667%;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.forum-post-text {
|
||||
width: 83.333333%;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.forum-post-footer {
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: start;
|
||||
justify-content: flex-start;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.forum-post-favorite {
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.forum-post-actions {
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
4
Application/Static/js/jquery.min.js
vendored
Normal file
4
Application/Static/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11944
Application/Static/js/vue.js
Normal file
11944
Application/Static/js/vue.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Application/Static/noava.jpg
Normal file
BIN
Application/Static/noava.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
13
Application/Views/404.php
Normal file
13
Application/Views/404.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
<div class="text-6xl">
|
||||
<p>Fuee...</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>The page you're trying to find cannot be found</p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$view->include('layouts/foot');
|
||||
?>
|
36
Application/Views/auth/forgot.php
Normal file
36
Application/Views/auth/forgot.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
<div id="login">
|
||||
<form action="/login/forget" method="POST" id="login">
|
||||
<h1 class="form-title">Help, I forgot my password!</h1>
|
||||
<div class="input-group">
|
||||
<input type="text" name="email" id="email" placeholder="email" v-model="email"></input>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<p>{{ forget_message }}</p>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button type="submit" id="submit" @click="validate">Sign in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: "#login",
|
||||
data: {
|
||||
email: "",
|
||||
password: "",
|
||||
forget_message: "<?php echo $session->get('forget_message') ?? "" ?>",
|
||||
},
|
||||
methods: {
|
||||
validate: function(e) {
|
||||
this.forget_message = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
$view->include('layouts/foot');
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
include 'layouts/head.php';
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
<div id="login">
|
||||
<form action="/login" method="POST" id="login">
|
||||
@ -10,6 +10,9 @@ include 'layouts/head.php';
|
||||
<div class="input-group">
|
||||
<input type="password" name="password" id="password" placeholder="password" v-model="password"></input>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<a href="/login/forget">I forgot my password</a>
|
||||
</div>
|
||||
<div id="errors" v-if="errors != ''">
|
||||
<p>{{ errors }}</p>
|
||||
</div>
|
||||
@ -32,15 +35,6 @@ var app = new Vue({
|
||||
var emailre = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
var emailvalid = emailre.test(String(this.email).toLowerCase()) ;
|
||||
|
||||
if(this.username == "") {
|
||||
this.errors = "Username must not be empty";
|
||||
e.preventDefault();
|
||||
return false;
|
||||
} else if(this.password == "") {
|
||||
this.errors = "Password must not be empty";
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
this.errors = "";
|
||||
return true;
|
||||
}
|
||||
@ -48,5 +42,5 @@ var app = new Vue({
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
include 'layouts/foot.php';
|
||||
$view->include('layouts/foot');
|
||||
?>
|
49
Application/Views/auth/reset.php
Normal file
49
Application/Views/auth/reset.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
<div id="signup">
|
||||
<form action="/login/reset" method="POST" id="signup">
|
||||
<h1 class="form-title">Time to reset your password</h1>
|
||||
<input type="hidden" name="confirm_key" value="<?php echo $key; ?>">
|
||||
<div class="input-group">
|
||||
<input type="password" name="password" id="password" placeholder="password" v-model="password"></input>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="password" name="confirmpassword" id="confirmpassword" placeholder="confirm password" v-model="confirmpassword"></input>
|
||||
</div>
|
||||
<div id="errors" v-if="errors != ''">
|
||||
<p>{{ errors }}</p>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button type="submit" id="submit" @click="validate">Sign Up</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: "#signup",
|
||||
data: {
|
||||
password: "",
|
||||
confirmpassword: "",
|
||||
errors: "<?php echo $_SESSION['errors'] ?? "" ?>",
|
||||
},
|
||||
methods: {
|
||||
validate: function(e) {
|
||||
if(this.password.length < 8) {
|
||||
this.errors = "Password must be at least 8 characters";
|
||||
e.preventDefault();
|
||||
return false;
|
||||
} else if(this.password !== this.confirmpassword) {
|
||||
this.errors = "Password and confirm password must be the same";
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
this.errors = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
$view->include('layouts/foot');
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
include 'layouts/head.php';
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
<div id="signup">
|
||||
<form action="/signup" method="POST" id="signup">
|
||||
@ -72,5 +72,5 @@ var app = new Vue({
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
include 'layouts/foot.php';
|
||||
$view->include('layouts/foot');
|
||||
?>
|
85
Application/Views/editor.php
Normal file
85
Application/Views/editor.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php if (!$auth->isLoggedIn()) { ?>
|
||||
<p class="">You need to be logged in to post. <a href="/login">Login</a></p>
|
||||
<?php } else if($auth->user()->isBanned($category)) {?>
|
||||
<p class="">You have been banned from this category.</p>
|
||||
<?php } else if($auth->user()->isSilenced($category)) {?>
|
||||
<p class="">You have been silenced from this category. You cannot post new replies or threads at this moment.</p>
|
||||
<?php } else if($auth->user()->isPardoned($thread)) {?>
|
||||
<p class="">You have been pardoned from this thread. You cannot post new replies at this moment.</p>
|
||||
<?php } else if(isset($edit_post) && (time() - strtotime($edit_post->created_at)) > 300) {?>
|
||||
<p class="">You cannot edit this post.</p>
|
||||
<?php } else { ?>
|
||||
<div id="editor-comp">
|
||||
<div class="forum-post" id="forum-editor">
|
||||
<div class="forum-post-title">
|
||||
<p class="text-lg flex-grow">
|
||||
<?php echo $title ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="forum-post-content">
|
||||
<div class="forum-post-user">
|
||||
<a href="/profile?id=<?php echo $auth->user()->id ?>">
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<img src="/noava.jpg">
|
||||
<p><?php echo $auth->user()->username ?></p>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<p><?php echo $auth->user()->logged_in ? 'Online' : 'Offline' ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $auth->user()->role_string ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $auth->user()->post_count ?> posts</p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $auth->user()->last_login ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<?php if($auth->user()->isBanned($category)) { ?>
|
||||
<p>Banned</p>
|
||||
<?php } else if($auth->user()->isSilenced($category)) { ?>
|
||||
<p>Silenced</p>
|
||||
<?php } else {?>
|
||||
<p>Active</p>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forum-post-text w-full h-full">
|
||||
<form method="POST" action="/thread/process">
|
||||
<input type="hidden" name="category" value="<?php echo $category ?>">
|
||||
<input type="hidden" name="thread" value="<?php echo $thread ?>">
|
||||
<input type="hidden" name="reply" value="<?php echo $reply ?>">
|
||||
<input type="hidden" name="edit" value="<?php echo $edit ?>">
|
||||
<textarea id="editor-text" class="w-full h-full" name="content"></textarea>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forum-post-footer">
|
||||
<div class="forum-post-favorite">
|
||||
<a class="cursor-pointer" @click="cancel()">Cancel</a>
|
||||
</div>
|
||||
<div class="forum-post-actions">
|
||||
<a class="cursor-pointer" @click="post()">Post</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var editorapp = new Vue({
|
||||
el: "#editor-comp",
|
||||
methods: {
|
||||
cancel() {
|
||||
confirm("Are you sure?");
|
||||
if(confirm) {
|
||||
$("#editor").html("");
|
||||
}
|
||||
},
|
||||
post() {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php } ?>
|
@ -1,9 +1,103 @@
|
||||
<?php
|
||||
include 'layouts/head.php';
|
||||
$view->include('layouts/head');
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<div id="forumbrowser">
|
||||
<div class="flex flex-col w-1/6">
|
||||
<div :id="'group-'+group.id":class="'forumbrowser-group'+(current_group == group.id ? ' selected' : '')" v-for="(group, key) in groups" @click="current_group = group.id;">
|
||||
{{ group.group_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col w-1/6">
|
||||
<div :id="'category-'+category.id":class="'forumbrowser-category'+(current_category == category.id ? ' selected' : '')" v-for="(category, key) in categories" @click="current_category = category.id;">
|
||||
{{ category.category_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="forumbrowser-thread-table w-4/6">
|
||||
<div v-if="current_category > 0" id="thread-create" class="forumbrowser-thread" @click="new_thread(current_category)">
|
||||
<div class="forumbrowser-thread-col"></div>
|
||||
<div class="forumbrowser-thread-col">Create New Thread</div>
|
||||
<div class="forumbrowser-thread-col"></div>
|
||||
<div class="forumbrowser-thread-col"></div>
|
||||
<div class="forumbrowser-thread-col"></div>
|
||||
</div>
|
||||
<div :id="'thread-'+thread.id":class="'forumbrowser-thread'+(current_thread == thread.id ? ' selected' : '')" v-for="(thread, key) in threads" @click="current_thread = thread.id;">
|
||||
<div class="forumbrowser-thread-col">
|
||||
<p v-if="thread.is_hot">[HOT]</p>
|
||||
</div>
|
||||
<div class="forumbrowser-thread-col">
|
||||
{{ thread.title }}
|
||||
</div>
|
||||
<div class="forumbrowser-thread-col">
|
||||
by {{ thread.author_model.username }}
|
||||
</div>
|
||||
<div class="forumbrowser-thread-col">
|
||||
View: {{ thread.view_count }} Post count: {{ thread.post_count }}
|
||||
</div>
|
||||
<div class="forumbrowser-thread-col">
|
||||
{{ thread.last_reply }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="threadreader">
|
||||
</div>
|
||||
<div id="editor">
|
||||
</div>
|
||||
<script>
|
||||
var selectapp = new Vue({
|
||||
el: "#forumbrowser",
|
||||
data: {
|
||||
groups: <?php echo json_encode($groups); ?>,
|
||||
current_group: 0,
|
||||
current_category: 0,
|
||||
current_thread: 0,
|
||||
current_group: <?php echo isset($group) ? $group->id : 0; ?>,
|
||||
current_category: <?php echo isset($category) ? $category->id : 0;?>,
|
||||
current_thread: <?php echo isset($thread) ? $thread->id : 0; ?>,
|
||||
categories: [],
|
||||
threads: [],
|
||||
},
|
||||
methods: {
|
||||
change_category(id) {
|
||||
$.ajax("<?php echo $root; ?>/api/get_threads?id="+id)
|
||||
.done(function(data) {
|
||||
this.threads = data;
|
||||
}.bind(this));
|
||||
},
|
||||
change_group(id) {
|
||||
$.ajax("<?php echo $root; ?>/api/get_categories?id="+id)
|
||||
.done(function(data) {
|
||||
this.categories = data;
|
||||
}.bind(this));
|
||||
},
|
||||
change_thread(id) {
|
||||
$.ajax("<?php echo $root; ?>/thread?id="+id)
|
||||
.done(function(data) {
|
||||
$("#threadreader").html(data);
|
||||
$("#editor").html("");
|
||||
}.bind(this));
|
||||
},
|
||||
new_thread(id) {
|
||||
$.ajax("<?php echo $root; ?>/thread/editor?category="+id)
|
||||
.done(function(data) {
|
||||
$("#threadreader").html("");
|
||||
$("#editor").html(data);
|
||||
}.bind(this));
|
||||
},
|
||||
},
|
||||
updated: function() {
|
||||
if(this.current_thread > 0) {
|
||||
this.change_thread(this.current_thread);
|
||||
}
|
||||
if(this.current_category > 0) {
|
||||
this.change_category(this.current_category);
|
||||
}
|
||||
if(this.current_group > 0) {
|
||||
this.change_group(this.current_group);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
include 'layouts/foot.php';
|
||||
$view->include('layouts/foot');
|
||||
?>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="/css/metaforums.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
<script src="/js/vue.js"></script>
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<title>Metaforums</title>
|
||||
</head>
|
||||
<body>
|
||||
@ -12,6 +13,9 @@
|
||||
<div class="header-middle">
|
||||
</div>
|
||||
<div class="header-left">
|
||||
<?php if($auth->isModerator()) { ?>
|
||||
<a href="/moderation" class="header-link">User Management</a>
|
||||
<?php } ?>
|
||||
<?php if($auth->isLoggedIn()) { ?>
|
||||
<a href="/logout" class="header-link">Logout</a>
|
||||
<?php } else {?>
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
include 'layouts/head.php';
|
||||
?>
|
||||
<h1>You are registered!</h1>
|
||||
<p>However, you will need to confirm your email address before you can start exploring the wonderful world of our forum</p>
|
||||
<p>Please check your inbox for further instructions</p>
|
||||
<?php
|
||||
include 'layouts/foot.php';
|
||||
?>
|
85
Application/Views/thread.php
Normal file
85
Application/Views/thread.php
Normal file
@ -0,0 +1,85 @@
|
||||
<div id="forum">
|
||||
<h1 class="text-2xl">Thread in: <?php echo $thread->category()->category_name ?></h1>
|
||||
<p class="text-4xl"><?php echo $thread->title ?></p>
|
||||
<p>Posted on <?php echo $thread->created_at ?> by <?php echo $thread->author_model->username ?></p>
|
||||
<p><?php echo $thread->elapsed_created ?></p>
|
||||
<div id="forum-posts">
|
||||
<?php foreach($thread->posts() as $post) { ?>
|
||||
<div class="forum-post" id="forum-post-<?php echo $post->id ?>">
|
||||
<div class="forum-post-title">
|
||||
<p class="text-lg flex-grow"><?php echo $post->title ?></p>
|
||||
<p class="text-lg"><?php echo $post->elapsed_created; ?></p>
|
||||
</div>
|
||||
<div class="forum-post-content">
|
||||
<div class="forum-post-user">
|
||||
<a href="/profile?id=<?php echo $post->user()->id ?>">
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<img src="/noava.jpg">
|
||||
<p><?php echo $post->user()->username ?></p>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<p><?php echo $post->user()->logged_in ? 'Online' : 'Offline' ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $post->user()->role_string ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $post->user()->post_count ?> posts</p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<p><?php echo $post->user()->last_login ?></p>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-start">
|
||||
<?php if($post->user()->isBanned($thread->category()->id)) { ?>
|
||||
<p>Banned</p>
|
||||
<?php } else if($post->user()->isSilenced($thread->category()->id)) { ?>
|
||||
<p>Silenced</p>
|
||||
<?php } else {?>
|
||||
<p>Active</p>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forum-post-text">
|
||||
<?php echo $post->post ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forum-post-footer">
|
||||
<div class="forum-post-favorite">
|
||||
0 favorites
|
||||
</div>
|
||||
<div class="forum-post-actions">
|
||||
<?php if(!$thread->lock_moderator && $auth->isLoggedIn()) { ?>
|
||||
<?php if($post->user_id == $auth->user()->id) { ?>
|
||||
<a class="cursor-pointer" @click="reply(<?php echo $post->id ?>)">Reply</a>
|
||||
<a class="cursor-pointer" @click="edit(<?php echo $post->id ?>)">Edit</a>
|
||||
<a class="cursor-pointer" @click="delete_post(<?php echo $post->id ?>)">Delete</a>
|
||||
<?php } else { ?>
|
||||
<a class="cursor-pointer" @click="favorite(<?php echo $post->id ?>)">Favorite</a>
|
||||
<a class="cursor-pointer" @click="reply(<?php echo $post->id ?>)">Reply</a>
|
||||
<a class="cursor-pointer" @click="report(<?php echo $post->id ?>)">Report Abuse</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var threadapp = new Vue({
|
||||
el: "#forum",
|
||||
methods: {
|
||||
reply(post_id) {
|
||||
$.ajax("<?php echo $root ?>/thread/editor?thread=<?php echo $thread->id ?>&reply="+post_id).done(function(data) {
|
||||
$("#editor").html(data);
|
||||
});
|
||||
},
|
||||
edit(post_id) {
|
||||
$.ajax("<?php echo $root ?>/thread/editor?thread=<?php echo $thread->id ?>&edit="+post_id).done(function(data) {
|
||||
$("#editor").html(data);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -2,6 +2,8 @@
|
||||
|
||||
require 'autoload.php';
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
|
||||
// Use helper classes from Application
|
||||
use Application\HTTP\Request;
|
||||
use Application\HTTP\Response;
|
||||
@ -39,9 +41,8 @@ $request_method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// Get current route from uri
|
||||
if(!array_key_exists($request_method.':'.$uri,$routes)) {
|
||||
http_response_code(404);
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode([ 'success' => false, 'error' => 'Not found' ]));
|
||||
$response->statusCode(404)->view('404')->render();
|
||||
die();
|
||||
}
|
||||
|
||||
$route = $routes[$request_method.':'.$uri];
|
||||
|
27
routes.php
27
routes.php
@ -9,9 +9,6 @@ return [
|
||||
'POST:/signup' => [
|
||||
'controller' => 'AuthController@create_user',
|
||||
],
|
||||
'GET:/signup/success' => [
|
||||
'controller' => 'AuthController@sign_up_success',
|
||||
],
|
||||
'GET:/signup/confirm' => [
|
||||
'controller' => 'AuthController@sign_up_confirm',
|
||||
],
|
||||
@ -24,4 +21,28 @@ return [
|
||||
'GET:/logout' => [
|
||||
'controller' => 'AuthController@logout',
|
||||
],
|
||||
'GET:/login/forget' => [
|
||||
'controller' => 'AuthController@forget_password',
|
||||
],
|
||||
'POST:/login/forget' => [
|
||||
'controller' => 'AuthController@forget_password_confirm',
|
||||
],
|
||||
'GET:/login/reset' => [
|
||||
'controller' => 'AuthController@reset_password',
|
||||
],
|
||||
'POST:/login/reset' => [
|
||||
'controller' => 'AuthController@reset_password_confirm',
|
||||
],
|
||||
'GET:/api/get_categories' => [
|
||||
'controller' => 'ApiController@categories',
|
||||
],
|
||||
'GET:/api/get_threads' => [
|
||||
'controller' => 'ApiController@threads',
|
||||
],
|
||||
'GET:/thread' => [
|
||||
'controller' => 'ForumThreadController@forum',
|
||||
],
|
||||
'GET:/thread/editor' => [
|
||||
'controller' => 'ForumThreadController@editor',
|
||||
],
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user