flush -- WIP admin panel

This commit is contained in:
Tony Yang
2019-05-30 14:18:07 +08:00
parent 75af7df7b4
commit 84fb4180c9
53 changed files with 9104 additions and 2 deletions

238
ajax/comment.php Normal file
View File

@@ -0,0 +1,238 @@
<?php
set_include_path('../include/');
$includepath = TRUE;
require_once('../connection/SQL.php');
require_once('../config.php');
require_once('security.php');
require_once('user.php');
require_once('article.php');
require_once('notification.php');
$user = validate_user();
if (!$user->valid) {
http_response_code(403);
header("Content-Type: applcation/json");
echo json_encode(array('status' => 'novalid'));
exit;
}
if (!isset($_GET['pid']) && !isset($_GET['del']) && !isset($_POST['pid']) && !isset($_POST['edit'])) {
send_error(404, "error");
} else {
if (isset($_GET['pid']) && trim($_GET['pid']) != "") {
if (isset($_SESSION['cavern_comment_time']) && $_SERVER['REQUEST_TIME'] - $_SESSION['cavern_comment_time'] > 10) {
// after 10 seconds
$_SESSION['cavern_comment_time'] = NULL;
unset($_SESSION['cavern_comment_time']);
}
$data = process_comments($_GET['pid']);
} else {
if (!$user->islogin) { // guest
send_error(401, "nologin");
}
if (!validate_csrf()) {
send_error(403, "csrf");
}
if (isset($_GET['del']) && trim($_GET['del']) != "") {
// delete comment
$result = cavern_query_result("SELECT * FROM `comment` WHERE `id`='%d'", array($_GET['del']));
if ($result['num_rows'] < 1) {
send_error(404, "error");
}
$author = $result['row']['username'];
if ($author !== $user->username) {
send_error(403, false);
}
$SQL->query("DELETE FROM `comment` WHERE `id`='%d' AND `username`='%s'", array($_GET['del'], $user->username));
$data = array(
"status" => TRUE,
"time" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)
);
} else if (isset($_POST['content'])) {
if (isset($_POST['pid']) && trim($_POST['pid']) && isset($_SESSION['cavern_comment_time']) && $_SERVER['REQUEST_TIME'] - $_SESSION['cavern_comment_time'] < 10) {
// user can create one comment per 10 seconds
$remain_second = 10 - ($_SERVER['REQUEST_TIME'] - $_SESSION['cavern_comment_time']);
header('Retry-After: ' . $remain_second);
send_error(429, "ratelimit");
}
if ($user->muted) {
send_error(403, "muted");
}
if (trim($_POST['content']) != "") {
if (isset($_POST['pid']) && trim($_POST['pid']) != "") {
// new comment
try {
$article = new Article(intval($_POST['pid']));
} catch (NoPostException $e) {
send_error(404, "error");
}
http_response_code(201); // 201 Created
$time = date('Y-m-d H:i:s');
$SQL->query("INSERT INTO `comment` (`pid`, `username`, `time`, `content`) VALUES ('%d', '%s', '%s', '%s')", array($_POST['pid'], $user->username, $time, htmlspecialchars($_POST['content'])));
$comment_id = $SQL->insert_id();
$data = array(
"status" => TRUE,
"comment_id" => $comment_id,
"time" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)
);
/* notification */
// notify tagged user
// the user who tag himself is unnecessary to notify
$username_list = parse_user_tag($_POST['content']);
foreach ($username_list as $key => $id) {
if ($id == $user->username) continue;
cavern_notify_user($id, "{{$user->name}}@{$user->username} 在 [{$article->title}] 的留言中提到了你", "post.php?pid={$article->pid}#comment-$comment_id", "comment");
}
// notify commenters
$commenters = cavern_query_result("SELECT `username` FROM `comment` WHERE `pid` = '%d'", array($_POST['pid']));
if ($commenters['num_rows'] > 0) {
do {
$u = $commenters['row']['username'];
if (!in_array($u, $username_list) && $u != $article->author && $u != $user->username) {
cavern_notify_user($u, "在你回應的文章 [{$article->title}] 中有了新的回應", "post.php?pid={$article->pid}#comment-$comment_id", "comment");
}
} while ($commenters['row'] = $commenters['query']->fetch_assoc());
}
// notify liked user
/* we won't inform the author for his like on his own post
and no notice for his own comment */
$likers = cavern_query_result("SELECT `username` FROM `like` WHERE `pid` = '%d'", array($_POST['pid']));
if ($likers['num_rows'] > 0) {
do {
$u = $likers['row']['username'];
if (!in_array($u, $username_list) && $u != $article->author && $u != $user->username) {
cavern_notify_user($u, "在你喜歡的文章 [{$article->title}] 中有了新的回應", "post.php?pid={$article->pid}#comment-$comment_id", "comment");
}
} while ($likers['row'] = $likers['query']->fetch_assoc());
}
// notify post author
/* we won't inform the author if he has been notified for being tagged
also, we won't notify the author for his own comment */
if (!in_array($article->author, $username_list) && $article->author != $user->username) {
cavern_notify_user($article->author, "{{$user->name}}@{$user->username} 回應了 [{$article->title}]", "post.php?pid={$article->pid}#comment-$comment_id", "comment");
}
// only new comment should be limited
$_SESSION['cavern_comment_time'] = $_SERVER['REQUEST_TIME'];
} else if (isset($_POST['edit']) && trim($_POST['edit']) != "") {
// edit comment
$query = cavern_query_result("SELECT * FROM `comment` WHERE `id` = '%d'", array($_POST['edit']));
if ($query['num_rows'] < 1) {
send_error(404, "error");
}
if ($query['row']['username'] !== $user->username) {
send_error(403, "author");
}
$time = date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
$SQL->query("UPDATE `comment` SET `content`='%s', `modified`='%s' WHERE `id`='%d' AND `username`='%s'", array(htmlspecialchars($_POST['content']), $time, $_POST['edit'], $user->username));
$data = array(
"status" => TRUE,
"comment_id" => $_POST['edit'],
"time" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)
);
} else {
send_error(400, "empty");
}
} else {
send_error(400, "empty");
}
}
}
}
header('Content-Type: application/json');
echo json_encode($data);
exit;
function process_comments($pid) {
if (isset($_SESSION["cavern_username"])) {
$user = new User($_SESSION["cavern_username"]);
} else {
$user = new User(""); // guest
}
if (cavern_query_result("SELECT * FROM `post` WHERE `pid`=%d", array($pid))['num_rows'] < 1) {
http_response_code(404);
$json = array('status' => 'error', 'fetch' => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)); // to fit javascript unit
return $json;
}
if (isset($_COOKIE['cavern_commentLastFetch'])) {
$last_fetch_time = $_COOKIE['cavern_commentLastFetch'];
}
$email_hash = array();
$names = array();
$id_list = array();
$modified = array();
$comments = array();
$result = cavern_query_result("SELECT * FROM `comment` WHERE `pid`='%d'", array($pid));
$json = array('status' => TRUE, 'fetch' => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)); // to fit javascript unit
if ($result['num_rows'] > 0) {
do {
$username = $result['row']['username'];
if (!isset($names[$username])) {
$target_user = new User($username);
$name = $target_user->name;
$email = $target_user->email;
$names[$username] = $name;
$email_hash[$username] = md5(strtolower($email));
}
$comment = array(
"id" => $result['row']['id'],
"username" => $username,
"markdown" => $result['row']['content'],
"time" => $result['row']['time'],
"modified" => (is_null($result['row']['modified']) ? FALSE : $result['row']['modified'])
// if the comment has been modified, set this value as modified time; otherwise, set to FALSE
);
if ($user->islogin && $user->username === $username) {
$comment['actions'] = array("reply", "edit", "del");
} else if ($user->islogin) {
$comment['actions'] = array("reply");
} else {
$comment['actions'] = array();
}
$id_list[] = $comment['id']; // append id
$comments[] = $comment; // append comment
if (!is_null($result['row']['modified']) && isset($last_fetch_time)) {
if (strtotime($result['row']['modified']) - $last_fetch_time > 0) {
$modified[] = $comment["id"];
}
}
} while ($result['row'] = $result['query']->fetch_assoc());
}
$json['idList'] = $id_list;
$json['modified'] = $modified;
$json['comments'] = $comments;
$json['names'] = $names;
$json['hash'] = $email_hash;
return $json;
}
function send_error($code, $message) {
http_response_code($code);
header('Content-Type: application/json');
echo json_encode(array('status' => $message, 'fetch' => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000))); // to fit javascript timestamp
exit;
}
?>

109
ajax/like.php Normal file
View File

@@ -0,0 +1,109 @@
<?php
set_include_path('../include/');
$includepath = TRUE;
require_once('../connection/SQL.php');
require_once('../config.php');
require_once('user.php');
require_once('security.php');
require_once('notification.php');
$user = validate_user();
if (!$user->valid) {
http_response_code(403);
header("Content-Type: applcation/json");
echo json_encode(array('status' => 'novalid'));
exit;
}
if (!isset($_GET['pid'])) {
http_response_code(404);
$data = array('status' => 'error');
} else {
$pid = $_GET['pid'];
$article = cavern_query_result("SELECT * FROM `post` WHERE `pid`='%d'", array($pid));
if ($article['num_rows'] < 1) {
http_response_code(404);
echo json_encode(array('status' => 'nopost', 'id' => $pid));
exit;
}
$likes_query = process_like($pid, $user);
$islike = $likes_query[0];
$likes = $likes_query[1];
$likers = $likes_query[2];
if (isset($_GET['fetch'])) {
// fetch likes
$data = array('status' => 'fetch', 'id' => $pid, 'likes' => $likes, 'likers' => $likers);
header('Content-Type: application/json');
echo json_encode($data);
exit;
} else if (!$user->islogin) {
// ask guest to login
$data = array('status' => 'nologin', 'id' => $pid, 'likes' => $likes, 'likers' => $likers);
http_response_code(401);
header('Content-Type: application/json');
echo json_encode($data);
exit;
} else {
// user like actions
if (!validate_csrf()) { // csrf attack!
http_response_code(403);
header('Content-Type: application/json');
echo json_encode(array('status' => 'csrf', 'id' => $pid, 'likes' => $likes, 'likers' => $likers));
exit;
}
if ($islike) {
// unlike
$SQL->query("DELETE FROM `like` WHERE `pid`='%d' AND `username`='%s'", array($pid, $user->username));
$result = process_like($pid, $user);
$likes = $result[1];
$likers = $result[2];
$data = array('status' => FALSE, 'id' => $pid, 'likes' => $likes, 'likers' => $likers);
} else {
// like
$SQL->query("INSERT INTO `like` (`pid`, `username`) VALUES ('%d', '%s')", array($pid, $user->username));
$result = process_like($pid, $user);
$likes = $result[1];
$likers = $result[2];
$data = array('status' => TRUE, 'id' => $pid, 'likes' => $likes, 'likers' => $likers);
/* notification */
// notify article author
// we should notify author this only once
$author = $article['row']['username'];
$notification_query = cavern_query_result("SELECT * FROM `notification` WHERE `username`='%s' AND `url`='%s' AND `type`='%s'", array($author, "post.php?pid=$pid", "like"));
if (!($notification_query['num_rows'] > 0) && $user->username !== $author) {
cavern_notify_user($author, "{{$user->name}}@{$user->username} 推了 [{$article['row']['title']}]", "post.php?pid=$pid", "like");
}
}
}
}
function process_like($pid, $user) {
$islike = false;
$likers = array();
$likes_query = cavern_query_result("SELECT * FROM `like` WHERE `pid`='%d'", array($pid));
if ($likes_query['num_rows'] < 1){
$likes = 0;
} else {
$likes = $likes_query['num_rows'];
do {
$likers[] = $likes_query['row']['username'];
if ($user->username === $likes_query['row']['username']) {
$islike = true;
}
} while ($likes_query['row'] = $likes_query['query']->fetch_assoc());
}
return array($islike, $likes, array_unique($likers));
}
header('Content-Type: application/json');
echo json_encode($data);
exit;
?>

51
ajax/notification.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
set_include_path('../include/');
$includepath = TRUE;
require_once('../include/security.php');
require_once('../connection/SQL.php');
require_once('../config.php');
if (isset($_GET['fetch']) || isset($_GET['count'])) {
if (isset($_SESSION['cavern_username'])) {
if (isset($_GET['fetch'])) {
$data = process_notifications(20); // fetch 20 comments
$SQL->query("UPDATE `notification` SET `read` = 1 WHERE `read` = 0 AND `username` = '%s'", array($_SESSION['cavern_username'])); // read all comments
} else if (isset($_GET['count'])) {
$query = cavern_query_result("SELECT COUNT(*) AS `count` FROM `notification` WHERE `username` = '%s' AND `read` = 0", array($_SESSION['cavern_username']));
$count = $query['row']['count'];
$data = array("status" => TRUE, "fetch" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000), "unread_count" => $count);
}
} else {
send_error(401, "nologin");
}
} else {
send_error(404, "error");
}
header('Content-Type: application/json');
echo json_encode($data);
exit;
function process_notifications($limit) {
$result = cavern_query_result("SELECT * FROM `notification` WHERE `username` = '%s' ORDER BY `time` DESC LIMIT %d" ,array($_SESSION['cavern_username'], $limit));
$json = array('status' => TRUE, 'fetch' => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000)); // to fit javascript unit
$feeds = array();
if ($result['num_rows'] > 0) {
do {
$feeds[] = $result['row'];
} while ($result['row'] = $result['query']->fetch_assoc());
}
$json['feeds'] = $feeds;
return $json;
}
function send_error($code, $message) {
http_response_code($code);
header('Content-Type: application/json');
echo json_encode(array('status' => $message, 'fetch' => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000))); // to fit javascript timestamp
exit;
}
?>

107
ajax/posts.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
set_include_path('../include/');
$includepath = TRUE;
require_once('../connection/SQL.php');
require_once('../config.php');
require_once('user.php');
require_once('article.php');
$data = array("fetch" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000));
$user = validate_user();
if (!$user->valid) {
$data["status"] = "invalid";
http_response_code(403);
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
if (isset($_GET['pid'])) {
// get data of single post
$pid = abs($_GET['pid']);
try {
$article = new Article($pid);
} catch (NoPostException $e) {
// post not found
http_response_code(404);
$data['message'] = $e->getMessage();
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
$post = array(
'author' => $article->author,
'name' => $article->name,
'title' => $article->title,
'content' => $article->content,
'time' => $article->time,
'likes_count' => $article->likes_count,
'comments_count' => $article->comments_count,
'islike' => $article->is_like($user)
);
$data['post'] = $post;
} else {
// get posts list
if (isset($_GET['limit']) && trim($_GET['limit']) != ""){
$limit = intval($_GET['limit']);
} else {
$limit = intval($blog['limit']);
}
if (isset($_GET['page']) && trim($_GET['page']) != "") {
$page = intval($_GET['page']);
$limit_start = abs(($page - 1) * $limit);
} else if (isset($_GET['username']) && trim($_GET['username']) != "") {
$mode = "username";
} else {
$page = 1;
$limit_start = 0;
}
if (isset($mode) && $mode == "username") {
$post_list = article_list(cavern_query_result(
"SELECT `post`.*, `user`.name FROM `post` INNER JOIN `user` ON `post`.username = `user`.username WHERE `post`.username = '%s' ORDER BY `time`",
array($_GET['username'])
));
$all_posts_count = cavern_query_result("SELECT COUNT(*) AS `count` FROM `post` WHERE `username` = '%s'", array($_GET['username']))['row']['count'];
} else {
$post_list = article_list(cavern_query_result(
"SELECT `post`.*, `user`.name FROM `post` INNER JOIN `user` ON `post`.username = `user`.username ORDER BY `time` DESC LIMIT %d,%d",
array($limit_start, $limit)
));
$all_posts_count = cavern_query_result("SELECT COUNT(*) AS `count` FROM `post`")['row']['count'];
$data['page_limit'] = $limit;
$data['page'] = $page;
}
$data['all_posts_count'] = intval($all_posts_count);
$posts = array();
foreach ($post_list as $_key => $article) {
$post = array(
'username' => $article->author,
'name' => $article->name,
'pid' => $article->pid,
'title' => $article->title,
'content' => $article->content,
'time' => $article->time,
'likes_count' => $article->likes_count,
'comments_count' => $article->comments_count,
'islike' => $article->is_like($user)
);
$posts[] = $post; // append post
}
$data["posts"] = $posts;
}
header('Content-Type: application/json');
echo json_encode($data);
exit;
?>

60
ajax/user.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
set_include_path('../include/');
$includepath = TRUE;
require_once('../connection/SQL.php');
require_once('../config.php');
require_once('user.php');
$user = validate_user();
if (!$user->valid) {
send_error(403, "invalid", $user->islogin);
}
if (isset($_GET['username']) && trim($_GET['username']) != "") {
// query other user's profile
$username = trim($_GET['username']);
} else if ($user->islogin) {
// query the profile of the user himself
$username = $user->username;
} else {
// username isn't provided
send_error(404, "error");
}
try {
$target_user = new User($username);
} catch (NoUserException $_e) {
send_error(404, "nouser", $user->islogin);
}
$posts = cavern_query_result("SELECT * FROM `post` WHERE `username`='%s'", array($username));
$posts_count = ($posts['num_rows'] > 0 ? $posts['num_rows'] : 0);
$data = array(
"username" => $target_user->username,
"name" => $target_user->name,
"level" => $target_user->level,
"role" => cavern_level_to_role($target_user->level),
"hash" => md5(strtolower($target_user->email)),
"muted" => $target_user->muted,
"posts_count" => $posts_count
);
// user himself and admin can see user's email
if ($user->username === $target_user->username || $user->level >= 8) {
$data["email"] = $target_user->email;
}
$data["login"] = $user->islogin;
$data["fetch"] = round($_SERVER['REQUEST_TIME_FLOAT'] * 1000); // fit javascript timestamp
header('Content-Type: application/json');
echo json_encode($data);
exit;
function send_error($code, $message, $islogin) {
http_response_code($code);
header('Content-Type: application/json');
echo json_encode(array("status" => $message, "login" => $islogin, "fetch" => round($_SERVER['REQUEST_TIME_FLOAT'] * 1000))); // to fit javascript timestamp
exit;
}