This commit is contained in:
flash 2016-03-30 23:30:15 +02:00
parent 53e5116cff
commit daf21c8873
19 changed files with 593 additions and 956 deletions

View file

@ -89,8 +89,11 @@ class Comment
->where('vote_comment', $this->id) ->where('vote_comment', $this->id)
->get(); ->get();
$this->upvotes = 0;
$this->downvotes = 0;
foreach ($votes as $vote) { foreach ($votes as $vote) {
if ($vote->vote_state) { if (intval($vote->vote_state) !== 0) {
$this->upvotes += 1; $this->upvotes += 1;
} else { } else {
$this->downvotes += 1; $this->downvotes += 1;
@ -129,8 +132,44 @@ class Comment
return User::construct($this->user); return User::construct($this->user);
} }
public function vote() public function vote($user, $vote)
{ {
// can't be fucked to implement this right now $vote = $vote ? '1' : '0';
// Attempt to get previous vote
$previous = DB::table('comment_votes')
->where('vote_user', $user)
->where('vote_comment', $this->id)
->get();
// Check if anything was returned
if ($previous) {
// Check if the vote that's being casted is the same
if ($previous[0]->vote_state == $vote) {
// Delete the vote
DB::table('comment_votes')
->where('vote_user', $user)
->where('vote_comment', $this->id)
->delete();
} else {
// Otherwise update the vote
DB::table('comment_votes')
->where('vote_user', $user)
->where('vote_comment', $this->id)
->update([
'vote_state' => $vote,
]);
}
} else {
// Create a vote
DB::table('comment_votes')
->insert([
'vote_user' => $user,
'vote_comment' => $this->id,
'vote_state' => $vote,
]);
}
$this->getVotes();
} }
} }

View file

@ -1,248 +0,0 @@
<?php
/**
* Holds the holds the comment handler class.
*
* @package Sakura
*/
namespace Sakura;
/**
* Handles and serves comments on pages.
* Needs a reimplementation.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Comments
{
/**
* The array containing the comments.
*
* @var array
*/
public $comments = [];
/**
* The comment category.
*
* @var string
*/
public $category;
/**
* The amount of comments.
* @var int
*/
public $count = 0;
/**
* Constructor.
*
* @param mixed $category The category that comments should be fetched from.
*/
public function __construct($category)
{
// Set category
$this->category = $category;
// Get the comments and assign them to $comments
$comments = DB::table('comments')
->where('comment_category', $this->category)
->where('comment_reply_to', 0)
->orderBy('comment_id', 'desc')
->get();
// Feed them into the sorter
$this->comments = $this->sortComments($comments);
}
/**
* Sort the comments.
*
* @param array $comments Array containing comments.
*
* @return array Array containing the sorted comments.
*/
public function sortComments($comments)
{
// Create storage array
$layer = [];
// Sort comments
foreach ($comments as $comment) {
// Temporary hackjob to get rid of the old database layer, will reimplement later
$comment = get_object_vars($comment);
// Attach the poster
$comment['comment_poster'] = User::construct($comment['comment_poster']);
$comment['comment_text'] = BBcode::parseEmoticons(Utils::cleanString($comment['comment_text']));
// Get likes and dislikes
$votes = $this->getVotes($comment['comment_id']);
$comment['comment_likes'] = 0;
$comment['comment_dislikes'] = 0;
// Store amount in their respective variables
foreach ($votes as $vote) {
$vote = get_object_vars($vote);
if ($vote['vote_state']) {
$comment['comment_likes'] += 1;
} else {
$comment['comment_dislikes'] += 1;
}
}
// Add post to posts array
$layer[$comment['comment_id']] = $comment;
// Up the comment count
$this->count += 1;
// Attempt to get replies from the database
$replies = DB::table('comments')
->where('comment_category', $this->category)
->where('comment_reply_to', $comment['comment_id'])
->orderBy('comment_id', 'desc')
->get();
// Check if this was a reply to something
if ($replies) {
// Save the replies
$layer[$comment['comment_id']]['comment_replies'] = $this->sortComments($replies);
}
}
return $layer;
}
/**
* Get a single comment.
*
* @param int $cid ID of the comment.
*
* @return array The comment.
*/
public function getComment($cid)
{
// Get from database
$comment = DB::table('comments')
->where('comment_id', $cid)
->get();
return $comment ? get_object_vars($comment[0]) : [];
}
/**
* Get the votes for a comment.
*
* @param int $cid ID of the comment.
*
* @return array The votes.
*/
public function getVotes($cid)
{
// Get from database
$comment = DB::table('comment_votes')
->where('vote_comment', $cid)
->get();
return $comment;
}
/**
* Creating a new comment.
*
* @param int $uid ID of the user creating the comment.
* @param int $reply ID of the comment that is being replied to.
* @param string $content Contents of the comment.
*
* @return array Response identifier.
*/
public function makeComment($uid, $reply, $content)
{
// Check if the comment is long enough
if (strlen($content) < Config::get('comment_min_length')) {
return [0, 'TOO_SHORT'];
}
// Check if the comment isn't too long
if (strlen($content) > Config::get('comment_max_length')) {
return [0, 'TOO_LONG'];
}
// Insert into database
DB::table('comments')
->insert([
'comment_category' => $this->category,
'comment_timestamp' => time(),
'comment_poster' => (int) $uid,
'comment_reply_to' => (int) $reply,
'comment_text' => $content,
]);
// Return success
return [1, 'SUCCESS'];
}
/**
* Making a vote.
*
* @param int $uid User making this vote.
* @param int $cid ID of the comment that is being voted on.
* @param int $mode Positive or negative vote.
*
* @return bool Always returns true.
*/
public function makeVote($uid, $cid, $mode)
{
// Attempt to get previous vote
$vote = DB::table('comment_votes')
->where('vote_user', $uid)
->where('vote_comment', $cid)
->get();
// Check if anything was returned
if ($vote) {
// Check if the vote that's being casted is the same
if ($vote[0]->vote_state == $mode) {
// Delete the vote
DB::table('comment_votes')
->where('vote_user', $uid)
->where('vote_comment', $cid)
->delete();
} else {
// Otherwise update the vote
DB::table('comment_votes')
->where('vote_user', $uid)
->where('vote_comment', $cid)
->update([
'vote_state' => $mode,
]);
}
} else {
// Create a vote
DB::table('comment_votes')
->insert([
'vote_user' => $uid,
'vote_comment' => $cid,
'vote_state' => $mode,
]);
}
return true;
}
/**
* Remove a comment
*
* @param int $cid ID of the comment to remove.
*/
public function removeComment($cid)
{
// Remove from database
DB::table('comments')
->where('comment_id', $cid)
->delete();
}
}

View file

@ -8,7 +8,8 @@
namespace Sakura\Controllers; namespace Sakura\Controllers;
use Sakura\Comment; use Sakura\Comment;
use Sakura\Template; use Sakura\Config;
use Sakura\Perms\Site;
/** /**
* Handles comment stuff. * Handles comment stuff.
@ -21,9 +22,14 @@ class CommentsController extends Controller
public function post($category = '', $reply = 0) public function post($category = '', $reply = 0)
{ {
global $currentUser; global $currentUser;
// Set json content type $session = $_POST['session'] ?? '';
header('Content-Type: application/json; charset=utf-8');
// Check if the user can comment
if ($session !== session_id()) {
$error = "Your session expired, refresh the page!";
return $this->json(compact('error'));
}
// Check if the user can comment // Check if the user can comment
if (!$currentUser->permission(Site::CREATE_COMMENTS)) { if (!$currentUser->permission(Site::CREATE_COMMENTS)) {
@ -32,25 +38,26 @@ class CommentsController extends Controller
} }
// Checks // Checks
$length = strlen($content); $text = $_POST['text'] ?? '';
$length = strlen($text);
$tooShort = $length < Config::get('comment_min_length'); $tooShort = $length < Config::get('comment_min_length');
$tooLong = $length > Config::get('comment_max_length'); $tooLong = $length > Config::get('comment_max_length');
if ($tooShort || $tooLong) { if ($tooShort || $tooLong) {
$fill = $tooShort ? "short" : "long"; $fill = $tooShort ? "short" : "long";
$error = "Your comment is too {$fill}!"; $error = "Your comment is too {$fill}!";
return $this->json(compact('error')); return $this->json(compact('error'));
} }
$text = isset($_POST['text']) ? $_POST['text'] : ''; $text = $_POST['text'] ?? '';
$comment = new Comment; $comment = new Comment;
$comment->category = $category; $comment->category = $category;
$comment->time = time(); $comment->time = time();
$comment->reply = (int) $reply; $comment->reply = (int) $reply;
$comment->user = $currentUser->id; $comment->user = (int) $currentUser->id;
$comment->text = $text; $comment->text = $text;
$comment->save(); $comment->save();
@ -58,18 +65,60 @@ class CommentsController extends Controller
return $this->json($comment); return $this->json($comment);
} }
public function edit($id = 0)
{
//
}
public function delete($id = 0) public function delete($id = 0)
{ {
// global $currentUser;
// Check if the user can delete comments
if (!$currentUser->permission(Site::DELETE_COMMENTS)) {
$error = "You aren't allowed to delete comments!";
return $this->json(compact('error'));
}
$comment = new Comment($id);
if (!$comment->id) {
$error = "This comment doesn't exist!";
return $this->json(compact('error'));
}
if ($currentUser->id !== $comment->user) {
$error = "You aren't allowed to delete the comments of other people!";
return $this->json(compact('error'));
}
$deleted = $comment->id;
$comment->delete();
return $this->json(compact('deleted'));
} }
public function vote($id = 0) public function vote($id = 0)
{ {
// global $currentUser;
$vote = $_REQUEST['vote'] ?? 0;
$vote = $vote != 0;
// Check if the user can delete comments
if (!$currentUser->permission(Site::VOTE_COMMENTS)) {
$error = "You aren't allowed to vote on comments!";
return $this->json(compact('error'));
}
$comment = new Comment($id);
if (!$comment->id) {
$error = "This comment doesn't exist!";
return $this->json(compact('error'));
}
$comment->vote($currentUser->id, $vote);
$upvotes = $comment->upvotes;
$downvotes = $comment->downvotes;
return $this->json(compact('upvotes', 'downvotes'));
} }
} }

View file

@ -15,8 +15,10 @@ namespace Sakura\Controllers;
*/ */
class Controller class Controller
{ {
private function json($object) public function json($object)
{ {
header('Content-Type: application/json; charset=utf-8');
return json_encode( return json_encode(
$object, $object,
JSON_FORCE_OBJECT | JSON_NUMERIC_CHECK | JSON_BIGINT_AS_STRING JSON_FORCE_OBJECT | JSON_NUMERIC_CHECK | JSON_BIGINT_AS_STRING

View file

@ -0,0 +1,139 @@
<?php
/**
* Holds the friends controller.
*
* @package Sakura
*/
namespace Sakura\Controllers;
use Sakura\Notification;
use Sakura\Perms\Site;
use Sakura\Router;
use Sakura\User;
/**
* Friendly controller.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class FriendsController extends Controller
{
private function addNotification($friend, $user, $title, $text = "")
{
$alert = new Notification;
$alert->user = $friend->id;
$alert->time = time();
$alert->title = $title;
$alert->text = $text;
$alert->image = Router::route('file.avatar', $user->id);
$alert->timeout = 60000;
$alert->link = Router::route('user.profile', $user->id);
$alert->save();
}
public function add($id = 0)
{
global $currentUser;
$session = $_POST['session'] ?? '';
// Check if the user can comment
if ($session !== session_id()) {
$error = "Your session expired, refresh the page!";
return $this->json(compact('error'));
}
$friend = User::construct($id);
if ($friend->permission(Site::DEACTIVATED)
|| $currentUser->permission(Site::DEACTIVATED)) {
$error = "The user you tried to add does not exist!";
return $this->json(compact('error'));
}
if ($friend->id === $currentUser->id) {
$error = "You can't be friends with yourself, stop trying to bend reality!";
return $this->json(compact('error'));
}
if ($currentUser->isFriends($friend->id)) {
$error = "You are already friends with this person!";
return $this->json(compact('error'));
}
// Add friend
$currentUser->addFriend($friend->id);
$level = $currentUser->isFriends($friend->id);
$mutual = $level === 2;
$alertTitle = $mutual
? "{$currentUser->username} accepted your friend request!"
: "{$currentUser->username} added you as a friend!";
$alertText = $mutual
? ""
: "Click here to add them as well.";
$this->addNotification(
$friend,
$currentUser,
$alertTitle,
$alertText
);
$message = $mutual
? "You are now mutual friends with {$friend->username}!"
: "A friend request has been sent to {$friend->username}!";
return $this->json(compact('message', 'level'));
}
public function remove($id = 0)
{
global $currentUser;
$session = $_POST['session'] ?? '';
// Check if the user can comment
if ($session !== session_id()) {
$error = "Your session expired, refresh the page!";
return $this->json(compact('error'));
}
$friend = User::construct($id);
if ($friend->permission(Site::DEACTIVATED)
|| $currentUser->permission(Site::DEACTIVATED)) {
$error = "The user you tried to remove does not exist!";
return $this->json(compact('error'));
}
if (!$currentUser->isFriends($friend->id)) {
$error = "You aren't even friends with that person!";
return $this->json(compact('error'));
}
// Add friend
$currentUser->removeFriend($friend->id);
$level = $currentUser->isFriends($friend->id);
$alertTitle = "{$currentUser->username} removed you from their friends!";
$this->addNotification(
$friend,
$currentUser,
$alertTitle
);
$message = "Removed {$friend->username} from your friends!";
return $this->json(compact('message', 'level'));
}
}

View file

@ -29,9 +29,6 @@ class NotificationsController extends Controller
// TODO: add friend on/offline messages // TODO: add friend on/offline messages
global $currentUser; global $currentUser;
// Set json content type
header('Content-Type: application/json; charset=utf-8');
return $this->json($currentUser->notifications()); return $this->json($currentUser->notifications());
} }

View file

@ -546,24 +546,12 @@ class User
* Add a new friend. * Add a new friend.
* *
* @param int $uid The ID of the friend. * @param int $uid The ID of the friend.
*
* @return array Status indicator.
*/ */
public function addFriend($uid) public function addFriend($uid)
{ {
// Create the foreign object // Create the foreign object
$user = User::construct($uid); $user = User::construct($uid);
// Validate that the user exists
if ($user->permission(Site::DEACTIVATED)) {
return [0, 'USER_NOT_EXIST'];
}
// Check if the user already has this user a friend
if ($this->isFriends($uid)) {
return [0, 'ALREADY_FRIENDS'];
}
// Add friend // Add friend
DB::table('friends') DB::table('friends')
->insert([ ->insert([
@ -571,9 +559,6 @@ class User
'friend_id' => $uid, 'friend_id' => $uid,
'friend_timestamp' => time(), 'friend_timestamp' => time(),
]); ]);
// Return true because yay
return [1, $user->isFriends($this->id) ? 'FRIENDS' : 'NOT_MUTUAL'];
} }
/** /**
@ -581,19 +566,12 @@ class User
* *
* @param int $uid The friend Id * @param int $uid The friend Id
* @param bool $deleteRequest Delete the open request as well (remove you from their friends list). * @param bool $deleteRequest Delete the open request as well (remove you from their friends list).
*
* @return array Status indicator.
*/ */
public function removeFriend($uid, $deleteRequest = false) public function removeFriend($uid, $deleteRequest = false)
{ {
// Create the foreign object // Create the foreign object
$user = User::construct($uid); $user = User::construct($uid);
// Validate that the user exists
if ($user->permission(Site::DEACTIVATED)) {
return [0, 'USER_NOT_EXIST'];
}
// Remove friend // Remove friend
DB::table('friends') DB::table('friends')
->where('user_id', $this->id) ->where('user_id', $this->id)
@ -607,9 +585,6 @@ class User
->where('friend_id', $this->id) ->where('friend_id', $this->id)
->delete(); ->delete();
} }
// Return true because yay
return [1, 'REMOVED'];
} }
/** /**

View file

@ -367,91 +367,6 @@ function replaceTag(tag) {
function safeTagsReplace(str) { function safeTagsReplace(str) {
return str.replace(/[&<>]/g, replaceTag); return str.replace(/[&<>]/g, replaceTag);
} }
// Open a comment reply field
function commentReply(id, session, category, action, avatar) {
// Find subject post
var replyingTo = document.getElementById('comment-' + id);
// Check if it actually exists
if ((typeof replyingTo).toLowerCase() === 'undefined') {
return;
}
// Attempt to get previously created box
var replyBox = document.getElementById('comment-reply-container-' + id);
// Remove it if it already exists
if (replyBox) {
Sakura.removeById('comment-reply-container-' + id);
return;
}
// Container
var replyContainer = document.createElement('li');
replyContainer.id = 'comment-reply-container-' + id;
// Form
var replyForm = document.createElement('form');
replyForm.id = 'comment-reply-' + id;
replyForm.action = action;
replyForm.method = 'post';
// Session
var replyInput = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'session';
replyInput.value = session;
replyForm.appendChild(replyInput);
// Category
var replyInput = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'category';
replyInput.value = category;
replyForm.appendChild(replyInput);
// Reply ID
var replyInput = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'replyto';
replyInput.value = id.toString();
replyForm.appendChild(replyInput);
// Mode
var replyInput = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'mode';
replyInput.value = 'comment';
replyForm.appendChild(replyInput);
// Comment container
var replyDiv = document.createElement('div');
replyDiv.className = 'comment';
// Avatar
var replyAvatar = document.createElement('div');
replyAvatar.className = 'comment-avatar';
replyAvatar.style.backgroundImage = 'url(' + avatar + ')';
replyDiv.appendChild(replyAvatar);
// Pointer
var replyPoint = document.createElement('div');
replyPoint.className = 'comment-pointer';
replyDiv.appendChild(replyPoint);
// Textarea
var replyText = document.createElement('textarea');
replyText.className = 'comment-content';
replyText.name = 'comment';
replyDiv.appendChild(replyText);
// Submit
var replySubmit = document.createElement('input');
replySubmit.className = 'comment-submit';
replySubmit.type = 'submit';
replySubmit.name = 'submit';
replySubmit.value = "\uf1d8";
replyDiv.appendChild(replySubmit);
// Append to form
replyForm.appendChild(replyDiv);
// Append form to container
replyContainer.appendChild(replyForm);
// Insert the HTML
if (replyingTo.children[1].children.length > 0) {
replyingTo.children[1].insertBefore(replyContainer, replyingTo.children[1].firstChild);
}
else {
replyingTo.children[1].appendChild(replyContainer);
}
// Prepare AJAX submission
prepareAjaxForm(replyForm.id, 'Replying...');
}
// Inserting text into text box // Inserting text into text box
// Borrowed from http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery (therefore not in Typescript format, fix this later) // Borrowed from http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery (therefore not in Typescript format, fix this later)
function insertText(areaId, text) { function insertText(areaId, text) {

View file

@ -451,109 +451,6 @@ function safeTagsReplace(str: string): string {
return str.replace(/[&<>]/g, replaceTag); return str.replace(/[&<>]/g, replaceTag);
} }
// Open a comment reply field
function commentReply(id: number, session: string, category: string, action: string, avatar: string): void {
// Find subject post
var replyingTo: HTMLElement = document.getElementById('comment-' + id);
// Check if it actually exists
if ((typeof replyingTo).toLowerCase() === 'undefined') {
return;
}
// Attempt to get previously created box
var replyBox: HTMLElement = document.getElementById('comment-reply-container-' + id);
// Remove it if it already exists
if (replyBox) {
Sakura.removeById('comment-reply-container-' + id);
return;
}
// Container
var replyContainer: HTMLLIElement = document.createElement('li');
replyContainer.id = 'comment-reply-container-' + id;
// Form
var replyForm: HTMLFormElement = document.createElement('form');
replyForm.id = 'comment-reply-' + id;
replyForm.action = action;
replyForm.method = 'post';
// Session
var replyInput: HTMLInputElement = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'session';
replyInput.value = session;
replyForm.appendChild(replyInput);
// Category
var replyInput: HTMLInputElement = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'category';
replyInput.value = category;
replyForm.appendChild(replyInput);
// Reply ID
var replyInput: HTMLInputElement = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'replyto';
replyInput.value = id.toString();
replyForm.appendChild(replyInput);
// Mode
var replyInput: HTMLInputElement = document.createElement('input');
replyInput.type = 'hidden';
replyInput.name = 'mode';
replyInput.value = 'comment';
replyForm.appendChild(replyInput);
// Comment container
var replyDiv: HTMLDivElement = document.createElement('div');
replyDiv.className = 'comment';
// Avatar
var replyAvatar: HTMLDivElement = document.createElement('div');
replyAvatar.className = 'comment-avatar';
replyAvatar.style.backgroundImage = 'url(' + avatar + ')';
replyDiv.appendChild(replyAvatar);
// Pointer
var replyPoint: HTMLDivElement = document.createElement('div');
replyPoint.className = 'comment-pointer';
replyDiv.appendChild(replyPoint);
// Textarea
var replyText: HTMLTextAreaElement = document.createElement('textarea');
replyText.className = 'comment-content';
replyText.name = 'comment';
replyDiv.appendChild(replyText);
// Submit
var replySubmit: HTMLInputElement = document.createElement('input');
replySubmit.className = 'comment-submit';
replySubmit.type = 'submit';
replySubmit.name = 'submit';
replySubmit.value = "\uf1d8";
replyDiv.appendChild(replySubmit);
// Append to form
replyForm.appendChild(replyDiv);
// Append form to container
replyContainer.appendChild(replyForm);
// Insert the HTML
if (replyingTo.children[1].children.length > 0) {
replyingTo.children[1].insertBefore(replyContainer, replyingTo.children[1].firstChild);
} else {
replyingTo.children[1].appendChild(replyContainer);
}
// Prepare AJAX submission
prepareAjaxForm(replyForm.id, 'Replying...');
}
// Inserting text into text box // Inserting text into text box
// Borrowed from http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery (therefore not in Typescript format, fix this later) // Borrowed from http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery (therefore not in Typescript format, fix this later)
function insertText(areaId, text) { function insertText(areaId, text) {

View file

@ -7,413 +7,14 @@
namespace Sakura; namespace Sakura;
use Sakura\Perms\Site; use Sakura\Perms\Site;
use Sakura\Router;
// Legacy support!!!!!!!!! // Legacy support!!!!!!!!!
$renderData = []; $renderData = [];
// If this we're requesting notifications this page won't require templating
if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notifications']) {
define('SAKURA_NO_TPL', true);
}
// Include components // Include components
require_once str_replace(basename(__DIR__), '', dirname(__FILE__)) . 'sakura.php'; require_once str_replace(basename(__DIR__), '', dirname(__FILE__)) . 'sakura.php';
// Notifications (decommissioned) if (isset($_POST['submit']) && isset($_POST['submit'])) {
if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notifications']) {
// Create the notification container array
$notifications = [];
// Check if the user is logged in
if (Users::checkLogin()
&& isset($_REQUEST['time'])
&& $_REQUEST['time'] > (time() - 1000)
&& isset($_REQUEST['session']) && $_REQUEST['session'] == session_id()) {
// Get the user's notifications from the past forever but exclude read notifications
$alerts = $currentUser->notifications();
// Add the proper values to the array
foreach ($alerts as $alert) {
// Add the notification to the display array
$notifications[$alert->id] = [
'read' => $alert->read,
'title' => $alert->title,
'text' => $alert->text,
'link' => $alert->link,
'img' => $alert->image,
'timeout' => $alert->timeout,
'sound' => $alert->sound,
];
$alert->toggleRead();
$alert->save();
}
}
// Check if friendOnline is set (so it doesn't tell you all your friends all online on first visit)
$onlineFriends = isset($_SESSION['friendsOnline']) ? $_SESSION['friendsOnline'] : [];
$onlineNotify = isset($_SESSION['friendsOnline']);
// Set friendsOnline
if (!$onlineNotify) {
$_SESSION['friendsOnline'] = [];
}
// Populate the array
foreach ($currentUser->friends(1) as $friend) {
// Online status
$online = $friend->isOnline();
// If true check if they're already in the array
if ($online && !in_array($friend->id, $onlineFriends)) {
// Add user to the online array
$_SESSION['friendsOnline'][$friend->id] = $friend->id;
// Add the notification to the display array
if ($onlineNotify) {
$notifications[] = [
'read' => 0,
'title' => $friend->username . ' is online.',
'text' => '',
'link' => '',
'img' => Router::route('file.avatar', $friend->id),
'timeout' => 2000,
'sound' => false,
];
}
} elseif (!$online && in_array($friend->id, $onlineFriends)) {
// Remove the person from the array
unset($_SESSION['friendsOnline'][$friend->id]);
// Add the notification to the display array
if ($onlineNotify) {
$notifications[] = [
'read' => 0,
'title' => $friend->username . ' is offline.',
'text' => '',
'link' => '',
'img' => Router::route('file.avatar', $friend->id),
'timeout' => 2000,
'sound' => false,
];
}
}
}
// Set header, convert the array to json, print it and exit
echo json_encode($notifications, JSON_NUMERIC_CHECK);
exit;
} elseif (isset($_REQUEST['comment-action']) && $_REQUEST['comment-action']) {
// Referrer
$redirect = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : Router::route('main.index'));
// Continue
$continue = true;
// Match session ids for the same reason
if (!Users::checkLogin()) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You must be logged in to do that!',
'success' => 0,
];
// Prevent
$continue = false;
}
// Match session ids for the same reason
if (!isset($_REQUEST['session']) || $_REQUEST['session'] != session_id()) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'Invalid session, please try again.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Match session ids for the same reason
if (!isset($_REQUEST['category'])) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'No category was set.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Select the right action
if ($continue) {
$comments = new Comments($_REQUEST['category']);
switch (isset($_REQUEST['mode']) ? $_REQUEST['mode'] : false) {
case 'vote':
$comment = $comments->getComment(isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
// Check if the comment was actually made by the current user
if (!$comment) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'The requested comment does not exist.',
'success' => 0,
];
break;
}
// Check if the user can delete comments
if (!$currentUser->permission(Site::VOTE_COMMENTS)) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You aren\'t allowed to vote on comments.',
'success' => 0,
];
break;
}
$comments->makeVote(
$currentUser->id,
isset($_REQUEST['id']) ? $_REQUEST['id'] : 0,
isset($_REQUEST['state']) && $_REQUEST['state'] ? '1' : '0'
);
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'Your vote has been cast!',
'success' => 1,
];
break;
case 'delete':
$comment = $comments->getComment(isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
// Check if the comment was actually made by the current user
if (!$comment) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'The requested comment does not exist.',
'success' => 0,
];
break;
}
// Check if the user can delete comments
if (!$currentUser->permission(Site::DELETE_COMMENTS)) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You aren\'t allowed to delete comments.',
'success' => 0,
];
break;
}
// Check if the comment was actually made by the current user
if ($comment['comment_poster'] !== $currentUser->id) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You can\'t delete the comments of others.',
'success' => 0,
];
break;
}
$comments->removeComment(isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'The comment has been deleted!',
'success' => 1,
];
break;
case 'comment':
// Check if the user can delete comments
if (!$currentUser->permission(Site::CREATE_COMMENTS)) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You aren\'t allowed to comment.',
'success' => 0,
];
break;
}
// Attempt to make a new comment
$comment = $comments->makeComment($currentUser->id, $_POST['replyto'], $_POST['comment']);
// Messages
$messages = [
'TOO_SHORT' => 'The comment you\'re trying to make is too short!',
'TOO_LONG' => 'The comment you\'re trying to make is too long!',
'SUCCESS' => 'Posted!',
];
$renderData['page'] = [
'redirect' => $redirect,
'message' => $messages[$comment[1]],
'success' => $comment[0],
];
break;
default:
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'Unknown action.',
'success' => 0,
];
}
}
// Print page contents or if the AJAX request is set only display the render data
if (isset($_REQUEST['ajax'])) {
echo $renderData['page']['message'] . '|' .
$renderData['page']['success'] . '|' .
$renderData['page']['redirect'];
} else {
// If not allowed print the restricted page
Template::vars($renderData);
// Print page contents
echo Template::render('global/information');
}
exit;
} elseif (isset($_REQUEST['friend-action']) && $_REQUEST['friend-action'] && Users::checkLogin()) {
// Continue
$continue = true;
// Referrer
$redirect = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : Router::route('main.index'));
// Compare time and session so we know the link isn't forged
if (!isset($_REQUEST['add']) && !isset($_REQUEST['remove'])) {
if (!isset($_REQUEST['ajax'])) {
header('Location: ' . $redirect);
exit;
}
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'One of the required operators isn\'t set.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Compare time and session so we know the link isn't forged
if ($continue && $_REQUEST[(isset($_REQUEST['add']) ? 'add' : 'remove')] == $currentUser->id) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'You can\'t be friends with yourself, stop trying to bend reality.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Compare time and session so we know the link isn't forged
if (!isset($_REQUEST['time']) || $_REQUEST['time'] < time() - 1000) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'Timestamps differ too much, refresh the page and try again.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Match session ids for the same reason
if (!isset($_REQUEST['session']) || $_REQUEST['session'] != session_id()) {
$renderData['page'] = [
'redirect' => $redirect,
'message' => 'Invalid session, please try again.',
'success' => 0,
];
// Prevent
$continue = false;
}
// Continue if nothing fucked up
if ($continue) {
// Execute the action
$action = (isset($_REQUEST['add']) ?
$currentUser->addFriend($_REQUEST['add']) :
$currentUser->removeFriend($_REQUEST['remove'], true));
// Set the messages
$messages = [
'USER_NOT_EXIST' => 'The user you tried to add doesn\'t exist.',
'ALREADY_FRIENDS' => 'You are already friends with this person!',
'FRIENDS' => 'You are now mutual friends!',
'NOT_MUTUAL' => 'A friend request has been sent to this person.',
'ALREADY_REMOVED' => 'You aren\'t friends with this person.',
'REMOVED' => 'Removed this person from your friends list.',
];
// Notification strings
$notifStrings = [
'FRIENDS' => ['%s accepted your friend request!', 'You can now do mutual friend things!'],
'NOT_MUTUAL' => ['%s added you as a friend!', 'Click here to add them as well.'],
'REMOVED' => ['%s removed you from their friends.', 'You can no longer do friend things now ;_;'],
];
// Add page specific things
$renderData['page'] = [
'redirect' => $redirect,
'message' => $messages[$action[1]],
'success' => $action[0],
];
// Create a notification
if (array_key_exists($action[1], $notifStrings)) {
// Get the current user's profile data
$user = User::construct($currentUser->id);
$friend = User::construct($_REQUEST[(isset($_REQUEST['add']) ? 'add' : 'remove')]);
$alert = new Notification;
$alert->user = $friend->id;
$alert->time = time();
$alert->sound = true;
$alert->title = sprintf($notifStrings[$action[1]][0], $user->username);
$alert->text = $notifStrings[$action[1]][1];
$alert->image = Router::route('file.avatar', $user->id);
$alert->timeout = 60000;
$alert->link = Router::route('user.profile', $user->id);
$alert->save();
}
}
// Print page contents or if the AJAX request is set only display the render data
if (isset($_REQUEST['ajax'])) {
echo $renderData['page']['message'] . '|' .
$renderData['page']['success'] . '|' .
$renderData['page']['redirect'];
} else {
// If not allowed print the restricted page
Template::vars($renderData);
// Print page contents
echo Template::render('global/information');
}
exit;
} elseif (isset($_POST['submit']) && isset($_POST['submit'])) {
$continue = true; $continue = true;
// Set redirector // Set redirector

View file

@ -108,7 +108,15 @@ Router::group(['prefix' => 'notifications'], function () {
// Comments // Comments
Router::group(['prefix' => 'comments', 'before' => 'loginCheck'], function () { Router::group(['prefix' => 'comments', 'before' => 'loginCheck'], function () {
Router::post('/{category:c}/post/{reply:i}?', 'CommentsController@post', 'comments.post'); Router::post('/{category:c}/post/{reply:i}?', 'CommentsController@post', 'comments.category.post');
Router::post('/{id:i}/delete', 'CommentsController@delete', 'comments.comment.delete');
Router::post('/{id:i}/vote', 'CommentsController@vote', 'comments.comment.vote');
});
// Comments
Router::group(['prefix' => 'friends', 'before' => 'loginCheck'], function () {
Router::post('/{id:i}/add', 'FriendsController@add', 'friends.add');
Router::post('/{id:i}/remove', 'FriendsController@remove', 'friends.remove');
}); });
// Files // Files
@ -136,7 +144,7 @@ Router::group(['prefix' => 'settings', 'before' => 'loginCheck'], function () {
Router::get('/', function () { Router::get('/', function () {
$route = Router::route('settings.general.home'); $route = Router::route('settings.general.home');
return header("Location: {$route}"); return header("Location: {$route}");
}); }, 'settings.index');
// General section // General section
Router::group(['prefix' => 'general'], function () { Router::group(['prefix' => 'general'], function () {
@ -203,7 +211,7 @@ Router::group(['prefix' => 'settings', 'before' => 'loginCheck'], function () {
return header("Location: {$route}"); return header("Location: {$route}");
}); });
Router::get('/email', 'Settings.AccountController@avatar', 'settings.account.email'); Router::get('/email', 'Settings.AccountController@email', 'settings.account.email');
Router::get('/username', 'Settings.AccountController@username', 'settings.account.username'); Router::get('/username', 'Settings.AccountController@username', 'settings.account.username');
Router::get('/title', 'Settings.AccountController@title', 'settings.account.title'); Router::get('/title', 'Settings.AccountController@title', 'settings.account.title');
Router::get('/password', 'Settings.AccountController@password', 'settings.account.password'); Router::get('/password', 'Settings.AccountController@password', 'settings.account.password');

View file

@ -8,7 +8,7 @@
namespace Sakura; namespace Sakura;
// Define Sakura version // Define Sakura version
define('SAKURA_VERSION', 20160328); define('SAKURA_VERSION', 20160330);
// Define Sakura Path // Define Sakura Path
define('ROOT', __DIR__ . '/'); define('ROOT', __DIR__ . '/');

View file

@ -6,13 +6,13 @@
<div class="comment-controls"> <div class="comment-controls">
<ul> <ul>
{% if comment.userData.id == user.id %} {% if comment.userData.id == user.id %}
<li><a href="{{ urls.format('COMMENT_DELETE', [comment.id, comment.category, session_id()])}}" class="clean fa fa-trash-o comment-deletion-link" title="Delete" id="comment-action-delete-{{ comment.id }}"></a></li> <li><a href="javascript:void(0);" onclick="commentDelete({{ comment.id }});" class="clean fa fa-trash-o" title="Delete"></a></li>
{% else %} {% else %}
<li><a href="{{ urls.format('USER_REPORT', [comment.userData.id]) }}" class="clean fa fa-exclamation-circle" title="Report" id="comment-action-report-{{ comment.id }}"></a></li> <li><a href="{{ urls.format('USER_REPORT', [comment.userData.id]) }}" class="clean fa fa-exclamation-circle" title="Report"></a></li>
{% endif %} {% endif %}
<li><a href="javascript:void(0);" onclick="commentReply({{ comment.id }}, '{{ session_id() }}', '{{ comment.category }}', '{{ urls.format('COMMENT_POST') }}', '{{ route('file.avatar', user.id) }}');" class="clean fa fa-reply" title="Reply" id="comment-action-reply-{{ comment.id }}"></a></li> <li><a href="javascript:void(0);" onclick="commentReply({{ comment.id }}, '{{ session_id() }}', '{{ route('file.avatar', user.id) }}');" class="clean fa fa-reply" title="Reply" id="comment-action-reply-{{ comment.id }}"></a></li>
<li class="shown voting like"><a href="{{ urls.format('COMMENT_VOTE', [comment.id, 1, comment.category, session_id()])}}" class="clean comment-like-link" id="comment-action-like-{{ comment.id }}"><span class="fa fa-thumbs-up"></span> <span id="comment-{{ comment.id }}-likes">{{ comment.upvotes }}</span></a></li> <li class="shown voting like"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 1);" class="clean"><span class="fa fa-thumbs-up"></span> <span id="comment-{{ comment.id }}-likes">{{ comment.upvotes }}</span></a></li>
<li class="shown voting dislike"><a id="comment-action-dislike-{{ comment.id }}" href="{{ urls.format('COMMENT_VOTE', [comment.id, 0, comment.category, session_id()])}}" class="clean comment-dislike-link"><span class="fa fa-thumbs-down"></span> <span id="comment-{{ comment.id }}-dislikes">{{ comment.downvotes }}</span></a></li> <li class="shown voting dislike"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 0);" class="clean"><span class="fa fa-thumbs-down"></span> <span id="comment-{{ comment.id }}-dislikes">{{ comment.downvotes }}</span></a></li>
</ul> </ul>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
@ -21,7 +21,7 @@
</div> </div>
</div> </div>
</div> </div>
<ul> <ul class="comment-replies">
{% for comment in comment.replies %} {% for comment in comment.replies %}
{% include 'elements/comment.twig' %} {% include 'elements/comment.twig' %}
{% endfor %} {% endfor %}

View file

@ -1,23 +1,12 @@
<div id="comments"> <div id="comments">
<div class="comment-input-section"> <div class="comment-input-section">
{% if session.checkLogin %} {% if session.checkLogin %}
<form action="{{ urls.format('COMMENT_POST') }}" method="post" id="commentsForm"> <div class="comment">
<input type="hidden" name="session" value="{{ session_id() }}" /> <div class="comment-avatar" style="background-image: url('{{ route('file.avatar', user.id) }}');"></div>
<input type="hidden" name="category" value="{{ commentsCategory }}" /> <div class="comment-pointer"></div>
<input type="hidden" name="replyto" value="0" /> <textarea class="comment-content" name="text" placeholder="Join the conversation..."></textarea>
<input type="hidden" name="mode" value="comment" /> <button class="comment-submit new" name="session" value="{{ session_id() }}" onclick="commentPost(this.parentNode, '{{ route('comments.category.post', commentsCategory) }}');">&#xf1d8;</button>
<div class="comment"> </div>
<div class="comment-avatar" style="background-image: url('{{ route('file.avatar', user.id) }}');"></div>
<div class="comment-pointer"></div>
<textarea class="comment-content" name="comment" placeholder="Join the conversation..."></textarea>
<input class="comment-submit new" name="submit" type="submit" value="&#xf1d8;" />
</div>
</form>
<script type="text/javascript">
window.addEventListener("load", function() {
prepareAjaxForm('commentsForm', 'Posting comment...');
});
</script>
{% else %} {% else %}
<h1 class="stylised" style="text-align: center; padding: 10px 0">Log in to comment!</h1> <h1 class="stylised" style="text-align: center; padding: 10px 0">Log in to comment!</h1>
{% endif %} {% endif %}
@ -37,18 +26,276 @@
{% block js %} {% block js %}
<script type="text/javascript"> <script type="text/javascript">
var deletionLinks = document.querySelectorAll('.comment-deletion-link'); var commentClient = new AJAX();
var likeLinks = document.querySelectorAll('.comment-like-link');
var dislikeLinks = document.querySelectorAll('.comment-dislike-link');
for(var link in deletionLinks) { commentClient.contentType("application/x-www-form-urlencoded");
prepareAjaxLink(deletionLinks[link].id, 'submitPost', ', true, "Deleting..."');
function commentPost(element, url) {
var text = element.querySelector('[name="text"]'),
session = element.querySelector('[name="session"]');
commentClient.setSend({"text":text.value,"session":session.value});
text.value = '';
commentClient.setUrl(url);
commentClient.addCallback(200, function () {
var resp = JSON.parse(commentClient.response());
if (resp.error) {
ajaxBusyView(true, resp.error, 'fail');
setTimeout(function () {
ajaxBusyView(false);
}, 1500);
} else {
commentAdd(resp);
}
commentClient.setRawSend("");
});
commentClient.start(HTTPMethods.POST);
} }
for(var link in dislikeLinks) {
prepareAjaxLink(likeLinks[link].id, 'submitPost', ', true, "Voting..."'); function commentAdd(obj) {
var container = document.createElement('li');
container.id = "comment-" + obj.id;
var inner = document.createElement('div');
inner.className = 'comment';
var avatar = document.createElement('a');
avatar.className = 'comment-avatar clean';
avatar.href = "{{ route('user.profile', 1) }}".replace(1, obj.user);
avatar.style.backgroundImage = "url('{{ route('file.avatar', 1) }}')".replace(1, obj.user);
inner.appendChild(avatar);
var pointer = document.createElement('div');
pointer.className = 'comment-pointer';
inner.appendChild(pointer);
var content = document.createElement('div');
content.className = 'comment-content';
var controls = document.createElement('div');
controls.className = 'comment-controls';
var controlsInner = document.createElement('ul');
if (Sakura.cookie('id') == obj.user) {
var controlsTrashContainer = document.createElement('li');
var controlsTrash = document.createElement('a');
controlsTrash.href = 'javascript:void(0);';
controlsTrash.title = 'Delete';
controlsTrash.className = 'clean fa fa-trash-o';
controlsTrash.setAttribute('onclick', 'commentDelete(' + obj.id + ');');
controlsTrashContainer.appendChild(controlsTrash);
controlsInner.appendChild(controlsTrashContainer);
} else {
var controlsReportContainer = document.createElement('li');
var controlsReport = document.createElement('a');
controlsReport.href = '#';
controlsReport.title = 'Report';
controlsReport.className = 'clean fa fa-trash-o';
controlsReportContainer.appendChild(controlsReport);
controlsInner.appendChild(controlsReportContainer);
}
var controlsReplyContainer = document.createElement('li');
var controlsReply = document.createElement('a');
controlsReply.href = 'javascript:void(0);';
controlsReply.title = 'Reply';
controlsReply.className = 'clean fa fa-reply';
controlsReply.setAttribute('onclick', 'commentReply(' + obj.id + ', "{{ session_id() }}", "{{ route("file.avatar", user.id) }}");');
controlsReplyContainer.appendChild(controlsReply);
controlsInner.appendChild(controlsReplyContainer);
var controlsLikeContainer = document.createElement('li');
controlsLikeContainer.className = 'shown voting like';
var controlsLike = document.createElement('a');
controlsLike.href = 'javascript:void(0);';
controlsLike.setAttribute('onclick', 'commentVote(' + obj.id + ', 1);');
controlsLike.className = 'clean';
var controlsLikeIcon = document.createElement('span');
controlsLikeIcon.className = 'fa fa-thumbs-up';
controlsLike.appendChild(controlsLikeIcon);
controlsLike.innerHTML += "\r\n";
var controlsLikeCount = document.createElement('span');
controlsLikeCount.id = 'comment-' + obj.id + '-likes';
controlsLikeCount.innerText = obj.upvotes;
controlsLike.appendChild(controlsLikeCount);
controlsLikeContainer.appendChild(controlsLike);
controlsInner.appendChild(controlsLikeContainer);
var controlsDislikeContainer = document.createElement('li');
controlsDislikeContainer.className = 'shown voting dislike';
var controlsDislike = document.createElement('a');
controlsDislike.href = 'javascript:void(0);';
controlsDislike.setAttribute('onclick', 'commentVote(' + obj.id + ', 0);');
controlsDislike.className = 'clean';
var controlsDislikeIcon = document.createElement('span');
controlsDislikeIcon.className = 'fa fa-thumbs-down';
controlsDislike.appendChild(controlsDislikeIcon);
controlsDislike.innerHTML += "\r\n";
var controlsDislikeCount = document.createElement('span');
controlsDislikeCount.id = 'comment-' + obj.id + '-dislikes';
controlsDislikeCount.innerText = obj.upvotes;
controlsDislike.appendChild(controlsDislikeCount);
controlsDislikeContainer.appendChild(controlsDislike);
controlsInner.appendChild(controlsDislikeContainer);
controls.appendChild(controlsInner);
var clear = document.createElement('div');
clear.className = 'clear';
controls.appendChild(clear);
content.appendChild(controls);
var text = document.createElement('text');
text.className = 'comment-text';
text.innerHTML = obj.text;
content.appendChild(text);
inner.appendChild(content);
container.appendChild(inner);
var replies = document.createElement('ul');
replies.className = 'comment-replies';
container.appendChild(replies);
var discussion = document.getElementById('comments').querySelector('.comments-list');
if (obj.reply) {
discussion = document.getElementById('comment-' + obj.reply).querySelector('.comment-replies');
}
if (discussion.children.length > 0) {
discussion.insertBefore(container, discussion.firstChild);
} else {
discussion.appendChild(container);
}
} }
for(var link in dislikeLinks) {
prepareAjaxLink(dislikeLinks[link].id, 'submitPost', ', true, "Voting..."'); function commentReply(id, session, avatar) {
var url = "{{ route('comments.category.post', [':cat', 1]) }}"
.replace('1', id)
.replace(':cat', '{{ commentsCategory }}');
// Find subject post
var replyingTo = document.getElementById('comment-' + id);
// Check if it actually exists
if ((typeof replyingTo).toLowerCase() === 'undefined') {
return;
}
// Attempt to get previously created box
var replyBox = document.getElementById('comment-reply-container-' + id);
// Remove it if it already exists
if (replyBox) {
Sakura.removeById('comment-reply-container-' + id);
return;
}
// Container
var replyContainer = document.createElement('li');
replyContainer.id = 'comment-reply-container-' + id;
// Comment container
var replyDiv = document.createElement('div');
replyDiv.className = 'comment';
replyDiv.id = 'comment-reply-' + id;
// Avatar
var replyAvatar = document.createElement('div');
replyAvatar.className = 'comment-avatar';
replyAvatar.style.backgroundImage = 'url(' + avatar + ')';
replyDiv.appendChild(replyAvatar);
// Pointer
var replyPoint = document.createElement('div');
replyPoint.className = 'comment-pointer';
replyDiv.appendChild(replyPoint);
// Textarea
var replyText = document.createElement('textarea');
replyText.className = 'comment-content';
replyText.name = 'text';
replyDiv.appendChild(replyText);
// Submit
var replySubmit = document.createElement('button');
replySubmit.className = 'comment-submit';
replySubmit.name = 'session';
replySubmit.value = session;
replySubmit.setAttribute('onclick', 'commentPost(this.parentNode, "' + url + '"); commentReply(' + id + ');');
replySubmit.innerHTML = "\uf1d8";
replyDiv.appendChild(replySubmit);
// Append form to container
replyContainer.appendChild(replyDiv);
// Insert the HTML
if (replyingTo.querySelector('.comment-replies').children.length > 0) {
replyingTo.querySelector('.comment-replies').insertBefore(replyContainer, replyingTo.querySelector('.comment-replies').firstChild);
} else {
replyingTo.querySelector('.comment-replies').appendChild(replyContainer);
}
}
function commentDelete(id) {
var url = "{{ route('comments.comment.delete', 1) }}".replace(1, id);
commentClient.setUrl(url);
commentClient.addCallback(200, function () {
var resp = JSON.parse(commentClient.response());
if (resp.error) {
ajaxBusyView(true, resp.error, 'fail');
setTimeout(function () {
ajaxBusyView(false);
}, 1500);
} else {
Sakura.removeById("comment-" + id);
}
});
commentClient.start(HTTPMethods.POST);
}
function commentVote(id, vote) {
var url = "{{ route('comments.comment.vote', 1) }}".replace(1, id),
upvotes = document.getElementById("comment-" + id + "-likes"),
downvotes = document.getElementById("comment-" + id + "-dislikes");
commentClient.setSend({"vote":vote});
commentClient.setUrl(url);
commentClient.addCallback(200, function () {
var resp = JSON.parse(commentClient.response());
if (resp.error) {
ajaxBusyView(true, resp.error, 'fail');
setTimeout(function () {
ajaxBusyView(false);
}, 1500);
} else {
upvotes.innerText = resp.upvotes;
downvotes.innerText = resp.downvotes;
}
commentClient.setRawSend("");
});
commentClient.start(HTTPMethods.POST);
} }
</script> </script>
{% endblock %} {% endblock %}

View file

@ -96,10 +96,8 @@
<a class="fa fa-trash" title="Delete this post" href="{{ route('forums.post.delete', post.id) }}"></a> <a class="fa fa-trash" title="Delete this post" href="{{ route('forums.post.delete', post.id) }}"></a>
{% endif %} {% endif %}
{% if not (post.poster.permission(constant('Sakura\\Perms\\Site::DEACTIVATED')) or post.poster.permission(constant('Sakura\\Perms\\Site::RESTRICTED')) or user.id == post.poster.id) %} {% if not (post.poster.permission(constant('Sakura\\Perms\\Site::DEACTIVATED')) or post.poster.permission(constant('Sakura\\Perms\\Site::RESTRICTED')) or user.id == post.poster.id) %}
{% if user.isFriends(post.poster.id) != 0 %} <a class="fa fa-{% if user.isFriends(post.poster.id) == 2 %}heart{% else %}star{% endif %} friend-{{ post.poster.id }}-level" title="You are friends" {% if user.isFriends(post.poster.id) == 0 %}style="display: none;"{% endif %}></a>
<a class="fa fa-{% if user.isFriends(post.poster.id) == 2 %}heart{% else %}star{% endif %}" title="You are friends"></a> <a class="fa fa-user-{% if user.isFriends(post.poster.id) == 0 %}plus{% else %}times{% endif %} forum-friend-toggle friend-{{ post.poster.id }}-toggle" title="{% if user.isFriends(post.poster.id) == 0 %}Add {{ post.poster.username }} as a friend{% else %}Remove friend{% endif %}" href="javascript:void(0);" onclick="{% if user.isFriends(post.poster.id) == 0 %}addFriend({{ post.poster.id }}){% else %}removeFriend({{ post.poster.id }}){% endif %}"></a>
{% endif %}
<a class="fa fa-user-{% if user.isFriends(post.poster.id) == 0 %}plus{% else %}times{% endif %} forum-friend-toggle" title="{% if user.isFriends(post.poster.id) == 0 %}Add {{ post.poster.username }} as a friend{% else %}Remove friend{% endif %}" href="{% if user.isFriends(post.poster.id) == 0 %}{{ urls.format('FRIEND_ADD', [post.poster.id, session_id(), date().timestamp, sakura.currentPage]) }}{% else %}{{ urls.format('FRIEND_REMOVE', [post.poster.id, session_id(), date().timestamp, sakura.currentPage]) }}{% endif %}"></a>
<a class="fa fa-flag" title="Report {{ post.poster.username }}" href="{{ urls.format('USER_REPORT', [post.poster.id]) }}"></a> <a class="fa fa-flag" title="Report {{ post.poster.username }}" href="{{ urls.format('USER_REPORT', [post.poster.id]) }}"></a>
{% endif %} {% endif %}
<a class="fa fa-reply" title="Quote this post" href="javascript:void(0);" onclick="quotePost({{ post.id }});"></a> <a class="fa fa-reply" title="Quote this post" href="javascript:void(0);" onclick="quotePost({{ post.id }});"></a>

View file

@ -97,11 +97,11 @@
<!-- User menu, displayed on right side of the bar. --> <!-- User menu, displayed on right side of the bar. -->
{% if session.checkLogin %} {% if session.checkLogin %}
<a class="menu-item avatar" href="{{ route('user.profile', user.id) }}" title="Logged in as {{ user.username }}" style="background-image: url('{{ route('file.avatar', user.id) }}'); width: auto; color: {{ user.colour }}; border-color: {{ user.colour }}; font-weight: 700;"></a> <a class="menu-item avatar" href="{{ route('user.profile', user.id) }}" title="Logged in as {{ user.username }}" style="background-image: url('{{ route('file.avatar', user.id) }}'); width: auto; color: {{ user.colour }}; border-color: {{ user.colour }}; font-weight: 700;"></a>
{#<a class="menu-item fa-envelope" href="{{ urls.format('SETTING_CAT', ['messages']) }}" title="Messages"></a>#} {#<a class="menu-item fa-envelope" href="#" title="Messages"></a>#}
{% if user.permission(constant('Sakura\\Perms\\Manage::USE_MANAGE'), constant('Sakura\\Perms::MANAGE')) %} {% if user.permission(constant('Sakura\\Perms\\Manage::USE_MANAGE'), constant('Sakura\\Perms::MANAGE')) %}
<a class="menu-item fa-gavel" href="{{ urls.format('MANAGE_INDEX') }}" title="Manage"></a> <a class="menu-item fa-gavel" href="#" title="Manage"></a>
{% endif %} {% endif %}
<a class="menu-item fa-cogs" href="{{ urls.format('SETTINGS_INDEX') }}" title="Settings"></a> <a class="menu-item fa-cogs" href="{{ route('settings.index') }}" title="Settings"></a>
<a class="menu-item fa-sign-out" href="{{ route('auth.logout') }}?s={{ session_id() }}" title="Logout" id="headerLogoutLink"></a> <a class="menu-item fa-sign-out" href="{{ route('auth.logout') }}?s={{ session_id() }}" title="Logout" id="headerLogoutLink"></a>
{% else %} {% else %}
{% if sakura.lockAuth %} {% if sakura.lockAuth %}
@ -224,6 +224,48 @@
setInterval(function() { setInterval(function() {
notifyRequest('{{ session_id() }}'); notifyRequest('{{ session_id() }}');
}, 60000); }, 60000);
var friendClient = new AJAX();
friendClient.contentType("application/x-www-form-urlencoded");
function addFriend(id) {
var url = "{{ route('friends.add', 1) }}".replace(1, id);
friendMode(url, id);
}
function removeFriend(id) {
var url = "{{ route('friends.remove', 1) }}".replace(1, id);
friendMode(url, id);
}
function friendMode(url, id) {
var level = document.querySelectorAll('.friend-' + id + '-level'),
toggle = document.querySelectorAll('.friend-' + id + '-toggle');
friendClient.setUrl(url);
friendClient.setSend({"session":"{{ session_id() }}"});
friendClient.addCallback(200, function () {
var resp = JSON.parse(friendClient.response());
friendClient.setRawSend("");
if (resp.error) {
ajaxBusyView(true, resp.error, 'fail');
setTimeout(function () {
ajaxBusyView(false);
}, 1500);
} else {
ajaxBusyView(true, resp.message, 'ok');
location.reload();
}
});
friendClient.start(HTTPMethods.POST);
}
</script> </script>
{% if sakura.dev.showChangelog and stats %} {% if sakura.dev.showChangelog and stats %}
<script type="text/javascript" src="https://sakura.flash.moe/?get=all&amp;limit=5&amp;variable=true"></script> <script type="text/javascript" src="https://sakura.flash.moe/?get=all&amp;limit=5&amp;variable=true"></script>

View file

@ -3,18 +3,6 @@
{% set paginationPages = friends %} {% set paginationPages = friends %}
{% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'listing']) }}{% endset %} {% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'listing']) }}{% endset %}
{% block js %}
<script type="text/javascript">
window.addEventListener("load", function() {
var friendsListActions = document.querySelectorAll('[id^="friendslist-friend-action-"]');
for (var i in friendsListActions) {
prepareAjaxLink(friendsListActions[i], 'submitPost', ', true, "Please wait.."');
}
});
</script>
{% endblock %}
{% block css %} {% block css %}
<style type="text/css"> <style type="text/css">
.pagination { .pagination {
@ -31,8 +19,8 @@ window.addEventListener("load", function() {
<img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;" /> <img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;" />
<div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div> <div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div>
</a> </a>
<div class="friends-list-actions"><!-- urls --> <div class="friends-list-actions">
<a class="remove fill fa fa-remove" title="Remove friend" href="/friends?remove={{ friend.id }}&amp;session={{ session_id() }}&amp;time={{ date().timestamp }}" id="friendslist-friend-action-remove-{{ friend.id }}"></a> <a class="remove fill fa fa-remove" title="Remove friend" href="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
</div> </div>

View file

@ -3,18 +3,6 @@
{% set paginationPages = friends %} {% set paginationPages = friends %}
{% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'requests']) }}{% endset %} {% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'requests']) }}{% endset %}
{% block js %}
<script type="text/javascript">
window.addEventListener("load", function() {
var friendsListActions = document.querySelectorAll('[id^="friendslist-friend-action-"]');
for (var i in friendsListActions) {
prepareAjaxLink(friendsListActions[i], 'submitPost', ', true, "Please wait.."');
}
});
</script>
{% endblock %}
{% block css %} {% block css %}
<style type="text/css"> <style type="text/css">
.pagination { .pagination {
@ -31,9 +19,9 @@ window.addEventListener("load", function() {
<img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;" /> <img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;" />
<div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div> <div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div>
</a> </a>
<div class="friends-list-actions"><!-- urls --> <div class="friends-list-actions">
<a class="add fa fa-check" title="Add friend" href="/friends?add={{ friend.id }}&amp;session={{ session_id() }}&amp;time={{ date().timestamp }}" id="friendslist-friend-action-add-{{ friend.id }}"></a> <a class="add fa fa-check" title="Add friend" href="javascript:void(0);" onclick="addFriend({{ friend.id }});"></a>
<a class="remove fa fa-remove" title="Remove friend" href="/friends?remove={{ friend.id }}&amp;session={{ session_id() }}&amp;time={{ date().timestamp }}" id="friendslist-friend-action-remove-{{ friend.id }}"></a> <a class="remove fa fa-remove" title="Remove friend" href="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
</div> </div>

View file

@ -114,7 +114,7 @@
<a class="fa fa-pencil-square-o" title="Edit your profile" href="{{ urls.format('SETTING_MODE', ['general', 'profile']) }}"></a> <a class="fa fa-pencil-square-o" title="Edit your profile" href="{{ urls.format('SETTING_MODE', ['general', 'profile']) }}"></a>
{% else %} {% else %}
{% if user.isFriends(profile.id) != 0 %}<a class="fa fa-{% if user.isFriends(profile.id) == 2 %}heart{% else %}star{% endif %}" title="You are friends"></a>{% endif %} {% if user.isFriends(profile.id) != 0 %}<a class="fa fa-{% if user.isFriends(profile.id) == 2 %}heart{% else %}star{% endif %}" title="You are friends"></a>{% endif %}
<a class="fa fa-user-{% if user.isFriends(profile.id) == 0 %}plus{% else %}times{% endif %}" title="{% if user.isFriends(profile.id) == 0 %}Add {{ profile.username }} as a friend{% else %}Remove friend{% endif %}" href="{% if user.isFriends(profile.id) == 0 %}{{ urls.format('FRIEND_ADD', [profile.id, session_id(), date().timestamp, sakura.currentPage]) }}{% else %}{{ urls.format('FRIEND_REMOVE', [profile.id, session_id(), date().timestamp, sakura.currentPage]) }}{% endif %}" id="profileFriendToggle"></a> <a class="fa fa-user-{% if user.isFriends(profile.id) == 0 %}plus{% else %}times{% endif %}" title="{% if user.isFriends(profile.id) == 0 %}Add {{ profile.username }} as a friend{% else %}Remove friend{% endif %}" href="javascript:void(0);" onclick="{% if user.isFriends(profile.id) == 0 %}addFriend({{ profile.id }}){% else %}removeFriend({{ profile.id }}){% endif %}"></a>
{#<a class="fa fa-exclamation-circle" title="Report {{ profile.username }}" href="{{ urls.format('USER_REPORT', [profile.id]) }}"></a>#} {#<a class="fa fa-exclamation-circle" title="Report {{ profile.username }}" href="{{ urls.format('USER_REPORT', [profile.id]) }}"></a>#}
{% endif %} {% endif %}
{% if user.permission(constant('Sakura\\Perms\\Manage::CAN_RESTRICT_USERS'), constant('Sakura\\Perms::MANAGE')) %} {% if user.permission(constant('Sakura\\Perms\\Manage::CAN_RESTRICT_USERS'), constant('Sakura\\Perms::MANAGE')) %}