big commit
This commit is contained in:
parent
f69f2742cd
commit
e77ffc6849
47 changed files with 2544 additions and 1374 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,4 +7,3 @@ google*.html
|
|||
[Tt]humbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
composer.lock
|
||||
|
|
|
@ -19,5 +19,13 @@
|
|||
"phroute/phroute": "^2.1",
|
||||
"illuminate/database": "5.2.*",
|
||||
"doctrine/dbal": "~2.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sakura\\": "libraries/"
|
||||
},
|
||||
"files": [
|
||||
"utility.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
1604
composer.lock
generated
Normal file
1604
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,6 @@ use JBBCode\CodeDefinition;
|
|||
use JBBCode\ElementNode;
|
||||
use Sakura\Router;
|
||||
use Sakura\User as SakuraUser;
|
||||
use Sakura\Utils;
|
||||
|
||||
/**
|
||||
* Username BBcode for JBBCode.
|
||||
|
@ -44,7 +43,7 @@ class User extends CodeDefinition
|
|||
$content = "";
|
||||
|
||||
foreach ($el->getChildren() as $child) {
|
||||
$content .= Utils::cleanString($child->getAsText(), true);
|
||||
$content .= clean_string($child->getAsText(), true);
|
||||
}
|
||||
|
||||
$user = SakuraUser::construct($content);
|
||||
|
|
|
@ -104,7 +104,7 @@ class Comment
|
|||
public function parsed()
|
||||
{
|
||||
if (!$this->parsedCache) {
|
||||
$this->parsedCache = BBcode::parseEmoticons(Utils::cleanString($this->text));
|
||||
$this->parsedCache = BBcode::parseEmoticons(clean_string($this->text));
|
||||
}
|
||||
|
||||
return $this->parsedCache;
|
||||
|
|
|
@ -18,8 +18,6 @@ use Sakura\Router;
|
|||
use Sakura\Session;
|
||||
use Sakura\Template;
|
||||
use Sakura\User;
|
||||
use Sakura\Users;
|
||||
use Sakura\Utils;
|
||||
|
||||
/**
|
||||
* Authentication controllers.
|
||||
|
@ -125,7 +123,7 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Get account data
|
||||
$user = User::construct(Utils::cleanString($username, true, true));
|
||||
$user = User::construct(clean_string($username, true, true));
|
||||
|
||||
// Check if the user that's trying to log in actually exists
|
||||
if ($user->id === 0) {
|
||||
|
@ -304,11 +302,12 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Attempt to get account data
|
||||
$user = User::construct(Utils::cleanString($username, true, true));
|
||||
$user = User::construct(clean_string($username, true, true));
|
||||
|
||||
// Check if the username already exists
|
||||
if ($user && $user->id !== 0) {
|
||||
$message = "{$user->username} is already a member here! If this is you please use the password reset form instead of making a new account.";
|
||||
$message = "{$user->username} is already a member here!"
|
||||
. " If this is you please use the password reset form instead of making a new account.";
|
||||
|
||||
Template::vars(['page' => compact('success', 'redirect', 'message')]);
|
||||
|
||||
|
@ -343,7 +342,7 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Check the MX record of the email
|
||||
if (!Utils::checkMXRecord($email)) {
|
||||
if (!check_mx_record($email)) {
|
||||
$message = 'No valid MX-Record found on the e-mail address you supplied.';
|
||||
|
||||
Template::vars(['page' => compact('success', 'redirect', 'message')]);
|
||||
|
@ -364,7 +363,7 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Check password entropy
|
||||
if (Utils::pwdEntropy($password) < Config::get('min_entropy')) {
|
||||
if (password_entropy($password) < Config::get('min_entropy')) {
|
||||
$message = 'Your password is too weak, try adding some special characters.';
|
||||
|
||||
Template::vars(['page' => compact('success', 'redirect', 'message')]);
|
||||
|
@ -382,7 +381,7 @@ class AuthController extends Controller
|
|||
// Check if we require e-mail activation
|
||||
if ($requireActive) {
|
||||
// Send activation e-mail to user
|
||||
Users::sendActivationMail($user->id);
|
||||
$this->sendActivationMail($user);
|
||||
}
|
||||
|
||||
// Return true with a specific message if needed
|
||||
|
@ -502,8 +501,8 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Grab forms
|
||||
$username = isset($_POST['username']) ? Utils::cleanString($_POST['username'], true) : null;
|
||||
$email = isset($_POST['email']) ? Utils::cleanString($_POST['email'], true) : null;
|
||||
$username = isset($_POST['username']) ? clean_string($_POST['username'], true) : null;
|
||||
$email = isset($_POST['email']) ? clean_string($_POST['email'], true) : null;
|
||||
|
||||
// Do database request
|
||||
$getUser = DB::table('users')
|
||||
|
@ -533,7 +532,7 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Send activation e-mail to user
|
||||
Users::sendActivationMail($user->id);
|
||||
$this->sendActivationMail($user);
|
||||
|
||||
$success = 1;
|
||||
$redirect = Router::route('auth.login');
|
||||
|
@ -587,8 +586,8 @@ class AuthController extends Controller
|
|||
$userId = isset($_POST['user']) ? $_POST['user'] : 0;
|
||||
$key = isset($_POST['key']) ? $_POST['key'] : "";
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : "";
|
||||
$userName = isset($_POST['username']) ? Utils::cleanString($_POST['username'], true) : "";
|
||||
$email = isset($_POST['email']) ? Utils::cleanString($_POST['email'], true) : null;
|
||||
$userName = isset($_POST['username']) ? clean_string($_POST['username'], true) : "";
|
||||
$email = isset($_POST['email']) ? clean_string($_POST['email'], true) : null;
|
||||
|
||||
// Create user object
|
||||
$user = User::construct($userId ? $userId : $userName);
|
||||
|
@ -613,7 +612,7 @@ class AuthController extends Controller
|
|||
|
||||
if ($key && $password) {
|
||||
// Check password entropy
|
||||
if (Utils::pwdEntropy($password) < Config::get('min_entropy')) {
|
||||
if (password_entropy($password) < Config::get('min_entropy')) {
|
||||
$message = "Your password doesn't meet the strength requirements!";
|
||||
|
||||
Template::vars(['page' => compact('success', 'redirect', 'message')]);
|
||||
|
@ -650,8 +649,8 @@ class AuthController extends Controller
|
|||
$message = "Changed your password! You may now log in.";
|
||||
$redirect = Router::route('auth.login');
|
||||
} else {
|
||||
// Send e-mail
|
||||
Users::sendPasswordForgot($user->id, $user->email);
|
||||
// Send the e-mail
|
||||
$this->sendPasswordMail($user);
|
||||
|
||||
$success = 1;
|
||||
$message = "Sent the e-mail, keep an eye on your spam folder as well!";
|
||||
|
@ -662,4 +661,72 @@ class AuthController extends Controller
|
|||
|
||||
return Template::render('global/information');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the activation e-mail
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
private function sendActivationMail($user)
|
||||
{
|
||||
// Generate activation key
|
||||
$activate = ActionCode::generate('ACTIVATE', $user->id);
|
||||
|
||||
$siteName = Config::get('sitename');
|
||||
$baseUrl = "http://" . Config::get('url_main');
|
||||
$activateLink = Router::route('auth.activate') . "?u={$user->id}&k={$activate}";
|
||||
$profileLink = Router::route('user.profile', $user->id);
|
||||
$signature = Config::get('mail_signature');
|
||||
|
||||
// Build the e-mail
|
||||
$message = "Welcome to {$siteName}!\r\n\r\n"
|
||||
. "Please keep this e-mail for your records. Your account intormation is as follows:\r\n\r\n"
|
||||
. "----------------------------\r\n\r\n"
|
||||
. "Username: {$user->username}\r\n\r\n"
|
||||
. "Your profile: {$baseUrl}{$profileLink}\r\n\r\n"
|
||||
. "----------------------------\r\n\r\n"
|
||||
. "Please visit the following link in order to activate your account:\r\n\r\n"
|
||||
. "{$baseUrl}{$activateLink}\r\n\r\n"
|
||||
. "Your password has been securely stored in our database and cannot be retrieved. "
|
||||
. "In the event that it is forgotten,"
|
||||
. " you will be able to reset it using the email address associated with your account.\r\n\r\n"
|
||||
. "Thank you for registering.\r\n\r\n"
|
||||
. "--\r\n\r\nThanks\r\n\r\n{$signature}";
|
||||
|
||||
// Send the message
|
||||
send_mail([$user->email => $user->username], "{$siteName} activation mail", $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the activation e-mail
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
private function sendPasswordMail($user)
|
||||
{
|
||||
// Generate the verification key
|
||||
$verk = ActionCode::generate('LOST_PASS', $user->id);
|
||||
|
||||
$siteName = Config::get('sitename');
|
||||
$baseUrl = "http://" . Config::get('url_main');
|
||||
$reactivateLink = Router::route('auth.resetpassword') . "?u={$user->id}&k={$verk}";
|
||||
$signature = Config::get('mail_signature');
|
||||
|
||||
// Build the e-mail
|
||||
$message = "Hello {$user->username},\r\n\r\n"
|
||||
. "You are receiving this notification because you have (or someone pretending to be you has)"
|
||||
. " requested a password reset link to be sent for your account on \"{$siteName}\"."
|
||||
. " If you did not request this notification then please ignore it,"
|
||||
. " if you keep receiving it please contact the site administrator.\r\n\r\n"
|
||||
. "To use this password reset key you need to go to a special page."
|
||||
. " To do this click the link provided below.\r\n\r\n"
|
||||
. "{$baseUrl}{$reactivateLink}\r\n\r\n"
|
||||
. "If successful you should be able to change your password here.\r\n\r\n"
|
||||
. "You can of course change this password yourself via the settings page."
|
||||
. " If you have any difficulties please contact the site administrator.\r\n\r\n"
|
||||
. "--\r\n\r\nThanks\r\n\r\n{$signature}";
|
||||
|
||||
// Send the message
|
||||
send_mail([$user->email => $user->username], "{$siteName} password restoration", $message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
namespace Sakura\Controllers\Settings;
|
||||
|
||||
use Sakura\ActiveUser;
|
||||
use Sakura\DB;
|
||||
use Sakura\Template;
|
||||
|
||||
/**
|
||||
* Advanced settings.
|
||||
*
|
||||
|
@ -17,7 +21,13 @@ class AdvancedController extends Controller
|
|||
{
|
||||
public function sessions()
|
||||
{
|
||||
return $this->go('advanced.sessions');
|
||||
$sessions = DB::table('sessions')
|
||||
->where('user_id', ActiveUser::$user->id)
|
||||
->get();
|
||||
|
||||
Template::vars(compact('sessions'));
|
||||
|
||||
return Template::render('settings/advanced/sessions');
|
||||
}
|
||||
|
||||
public function deactivate()
|
||||
|
|
|
@ -11,6 +11,7 @@ use Sakura\ActiveUser;
|
|||
use Sakura\Controllers\Controller as BaseController;
|
||||
use Sakura\Perms\Site;
|
||||
use Sakura\Router;
|
||||
use Sakura\Template;
|
||||
use Sakura\Urls;
|
||||
|
||||
/**
|
||||
|
@ -26,6 +27,10 @@ class Controller extends BaseController
|
|||
public function __construct()
|
||||
{
|
||||
$this->urls = new Urls();
|
||||
|
||||
$navigation = $this->navigation();
|
||||
|
||||
Template::vars(compact('navigation'));
|
||||
}
|
||||
|
||||
public function go($location)
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Sakura\Controllers\Settings;
|
||||
|
||||
use Sakura\Template;
|
||||
|
||||
/**
|
||||
* Friends settings.
|
||||
*
|
||||
|
@ -17,11 +19,11 @@ class FriendsController extends Controller
|
|||
{
|
||||
public function listing()
|
||||
{
|
||||
return $this->go('friends.listing');
|
||||
return Template::render('settings/friends/listing');
|
||||
}
|
||||
|
||||
public function requests()
|
||||
{
|
||||
return $this->go('friends.requests');
|
||||
return Template::render('settings/friends/requests');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
namespace Sakura\Controllers\Settings;
|
||||
|
||||
use Sakura\ActiveUser;
|
||||
use Sakura\DB;
|
||||
use Sakura\Perms\Site;
|
||||
use Sakura\Router;
|
||||
use Sakura\Template;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* General settings.
|
||||
|
@ -21,23 +24,177 @@ class GeneralController extends Controller
|
|||
{
|
||||
public function home()
|
||||
{
|
||||
ActiveUser::class;
|
||||
Site::class;
|
||||
|
||||
$navigation = $this->navigation();
|
||||
|
||||
Template::vars(compact('navigation'));
|
||||
|
||||
return Template::render('settings/general/home');
|
||||
}
|
||||
|
||||
public function profile()
|
||||
{
|
||||
return $this->go('general.profile');
|
||||
// Check permission
|
||||
if (!ActiveUser::$user->permission(Site::ALTER_PROFILE)) {
|
||||
$message = "You aren't allowed to edit your profile!";
|
||||
$redirect = Router::route('settings.general.home');
|
||||
|
||||
Template::vars(compact('message', 'redirect'));
|
||||
|
||||
return Template::render('global/information');
|
||||
}
|
||||
|
||||
// Get profile fields
|
||||
$rawFields = DB::table('profilefields')
|
||||
->get();
|
||||
|
||||
// Create output array
|
||||
$fields = [];
|
||||
|
||||
// Iterate over the fields and clean them up
|
||||
foreach ($rawFields as $fieldData) {
|
||||
$field = new stdClass;
|
||||
$field->id = clean_string($fieldData->field_name, true, true);
|
||||
$field->name = $fieldData->field_name;
|
||||
$field->type = $fieldData->field_type;
|
||||
$field->link = $fieldData->field_link;
|
||||
$field->format = $fieldData->field_linkformat;
|
||||
$field->description = $fieldData->field_description;
|
||||
$field->additional = json_decode($fieldData->field_additional, true);
|
||||
$fields[$fieldData->field_id] = $field;
|
||||
}
|
||||
|
||||
// Attempt to get the session value
|
||||
$session = $_POST['session'] ?? null;
|
||||
|
||||
if ($session) {
|
||||
$redirect = Router::route('settings.general.profile');
|
||||
|
||||
// Go over each field
|
||||
foreach ($fields as $field) {
|
||||
// Add to the store table
|
||||
if (isset($_POST["profile_{$field->id}"])) {
|
||||
DB::table('user_profilefields')
|
||||
->insert([
|
||||
'user_id' => ActiveUser::$user->id,
|
||||
'field_name' => $field->id,
|
||||
'field_value' => $_POST["profile_{$field->id}"],
|
||||
]);
|
||||
}
|
||||
|
||||
// Check if there's additional values we should keep in mind
|
||||
if (!empty($field->additional)) {
|
||||
// Go over each additional value
|
||||
foreach ($field->additional as $addKey => $addVal) {
|
||||
// Add to the array
|
||||
$store = (isset($_POST["profile_additional_{$addKey}"]))
|
||||
? $_POST["profile_additional_{$addKey}"]
|
||||
: false;
|
||||
|
||||
DB::table('user_profilefields')
|
||||
->insert([
|
||||
'user_id' => ActiveUser::$user->id,
|
||||
'field_name' => $addKey,
|
||||
'field_value' => $store,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Birthdays
|
||||
if (isset($_POST['birthday_day'])
|
||||
&& isset($_POST['birthday_month'])
|
||||
&& isset($_POST['birthday_year'])) {
|
||||
$day = intval($_POST['birthday_day']);
|
||||
$month = intval($_POST['birthday_month']);
|
||||
$year = intval($_POST['birthday_year']);
|
||||
|
||||
// Check the values
|
||||
if (!checkdate($month, $day, $year ? $year : 1)
|
||||
|| $year > date("Y")
|
||||
|| ($year != 0 && $year < (date("Y") - 100))) {
|
||||
$message = "Your birthdate was considered invalid, everything else was saved though.";
|
||||
|
||||
Template::vars(compact('message', 'redirect'));
|
||||
|
||||
return Template::render('global/information');
|
||||
}
|
||||
|
||||
// Combine it into a YYYY-MM-DD format
|
||||
$birthdate = implode(
|
||||
'-',
|
||||
[$_POST['birthday_year'], $_POST['birthday_month'], $_POST['birthday_day']]
|
||||
);
|
||||
|
||||
DB::table('users')
|
||||
->where('user_id', ActiveUser::$user->id)
|
||||
->update([
|
||||
'user_birthday' => $birthdate,
|
||||
]);
|
||||
}
|
||||
|
||||
$message = "Updated your profile!";
|
||||
|
||||
Template::vars(compact('message', 'redirect'));
|
||||
|
||||
return Template::render('global/information');
|
||||
}
|
||||
|
||||
Template::vars(compact('fields'));
|
||||
|
||||
return Template::render('settings/general/profile');
|
||||
}
|
||||
|
||||
public function options()
|
||||
{
|
||||
return $this->go('general.options');
|
||||
// Get profile fields
|
||||
$rawFields = DB::table('optionfields')
|
||||
->get();
|
||||
|
||||
// Create output array
|
||||
$fields = [];
|
||||
|
||||
// Iterate over the fields and clean them up
|
||||
foreach ($rawFields as $fieldData) {
|
||||
if (!ActiveUser::$user->permission(constant("Sakura\Perms\Site::{$fieldData->option_permission}"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$field = new stdClass;
|
||||
$field->id = $fieldData->option_id;
|
||||
$field->name = $fieldData->option_name;
|
||||
$field->description = $fieldData->option_description;
|
||||
$field->type = $fieldData->option_type;
|
||||
$field->permission = $fieldData->option_permission;
|
||||
$fields[$fieldData->option_id] = $field;
|
||||
}
|
||||
|
||||
// Attempt to get the session value
|
||||
$session = $_POST['session'] ?? null;
|
||||
|
||||
if ($session) {
|
||||
// Delete all option fields for this user
|
||||
DB::table('user_optionfields')
|
||||
->where('user_id', ActiveUser::$user->id)
|
||||
->delete();
|
||||
|
||||
// Go over each field
|
||||
foreach ($fields as $field) {
|
||||
if (isset($_POST["option_{$field->id}"])) {
|
||||
DB::table('user_optionfields')
|
||||
->insert([
|
||||
'user_id' => ActiveUser::$user->id,
|
||||
'field_name' => $field->id,
|
||||
'field_value' => $_POST["option_{$field->id}"],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$message = "Updated your options!";
|
||||
$redirect = Router::route('settings.general.options');
|
||||
|
||||
Template::vars(compact('message', 'redirect'));
|
||||
|
||||
return Template::render('global/information');
|
||||
}
|
||||
|
||||
Template::vars(compact('fields'));
|
||||
|
||||
return Template::render('settings/general/options');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Sakura\Controllers\Settings;
|
||||
|
||||
use Sakura\Template;
|
||||
|
||||
/**
|
||||
* Notification settings.
|
||||
*
|
||||
|
@ -17,6 +19,6 @@ class NotificationsController extends Controller
|
|||
{
|
||||
public function history()
|
||||
{
|
||||
return $this->go('notifications.history');
|
||||
return Template::render('settings/notifications/history');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ use Sakura\Rank;
|
|||
use Sakura\Router;
|
||||
use Sakura\Template;
|
||||
use Sakura\User;
|
||||
use Sakura\Utils;
|
||||
|
||||
/**
|
||||
* Everything that is just for serving user data.
|
||||
|
@ -41,7 +40,7 @@ class UserController extends Controller
|
|||
if ($profile->id == 0) {
|
||||
// Fetch from username_history
|
||||
$check = DB::table('username_history')
|
||||
->where('username_old_clean', Utils::cleanString($id, true, true))
|
||||
->where('username_old_clean', clean_string($id, true, true))
|
||||
->orderBy('change_id', 'desc')
|
||||
->get();
|
||||
|
||||
|
@ -94,7 +93,7 @@ class UserController extends Controller
|
|||
}
|
||||
|
||||
// Get the active rank
|
||||
$rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : 2);
|
||||
$rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : intval(Config::get('default_rank_id')));
|
||||
|
||||
// Get members per page
|
||||
$membersPerPage = Config::get('members_per_page');
|
||||
|
@ -105,4 +104,9 @@ class UserController extends Controller
|
|||
// Render the template
|
||||
return Template::render('user/members');
|
||||
}
|
||||
|
||||
public function report($id = 0)
|
||||
{
|
||||
return Template::render('user/report');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class Session
|
|||
->insert([
|
||||
'user_id' => $this->userId,
|
||||
'user_ip' => Net::pton(Net::ip()),
|
||||
'user_agent' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'),
|
||||
'user_agent' => clean_string(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'),
|
||||
'session_key' => $session,
|
||||
'session_start' => time(),
|
||||
'session_expire' => time() + 604800,
|
||||
|
|
|
@ -8,325 +8,33 @@
|
|||
namespace Sakura;
|
||||
|
||||
/**
|
||||
* Rewrite URL generator.
|
||||
* URL generator.
|
||||
*
|
||||
* @package Sakura
|
||||
* @author Julian van de Groep <me@flash.moe>
|
||||
*/
|
||||
class Urls
|
||||
{
|
||||
/**
|
||||
* Unformatted links
|
||||
* 0 - Plain
|
||||
* 1 - mod_rewrite
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urls = [
|
||||
|
||||
// General site sections
|
||||
'SITE_HOME' => [
|
||||
'/index.php',
|
||||
'/',
|
||||
],
|
||||
'SITE_NEWS' => [
|
||||
'/news.php',
|
||||
'/news',
|
||||
],
|
||||
'SITE_NEWS_POST' => [
|
||||
'/news.php?id=%u',
|
||||
'/news/%u',
|
||||
],
|
||||
'SITE_NEWS_CAT' => [
|
||||
'/news.php?cat=$s',
|
||||
'/news/%s',
|
||||
],
|
||||
'SITE_NEWS_CAT_POST' => [
|
||||
'/news.php?cat=%s&id=%u',
|
||||
'/news/%s/%u',
|
||||
],
|
||||
'SITE_SEARCH' => [
|
||||
'/search.php',
|
||||
'/search',
|
||||
],
|
||||
'SITE_PREMIUM' => [
|
||||
'/support.php',
|
||||
'/support',
|
||||
],
|
||||
'SITE_DONATE_TRACK' => [
|
||||
'/support.php?tracker=true',
|
||||
'/support/tracker',
|
||||
],
|
||||
'SITE_FAQ' => [
|
||||
'/faq.php',
|
||||
'/faq',
|
||||
],
|
||||
'SITE_LOGIN' => [
|
||||
'/authenticate.php?ref=login',
|
||||
'/login',
|
||||
],
|
||||
'SITE_LOGOUT' => [
|
||||
'/authenticate.php?ref=logout',
|
||||
'/logout',
|
||||
],
|
||||
'SITE_REGISTER' => [
|
||||
'/authenticate.php?ref=register',
|
||||
'/register',
|
||||
],
|
||||
'SITE_FORGOT_PASSWORD' => [
|
||||
'/authenticate.php?ref=forgotpassword',
|
||||
'/forgotpassword',
|
||||
],
|
||||
'SITE_ACTIVATE' => [
|
||||
'/authenticate.php?ref=activate',
|
||||
'/activate',
|
||||
],
|
||||
'INFO_PAGE' => [
|
||||
'/index.php?p=%s',
|
||||
'/p/%s',
|
||||
],
|
||||
'AUTH_ACTION' => [
|
||||
'/authenticate.php',
|
||||
'/authenticate',
|
||||
],
|
||||
|
||||
// Memberlist
|
||||
'MEMBERLIST_INDEX' => [
|
||||
'/members.php',
|
||||
'/members',
|
||||
],
|
||||
'MEMBERLIST_SORT' => [
|
||||
'/members.php?sort=%s',
|
||||
'/members/%s',
|
||||
],
|
||||
'MEMBERLIST_RANK' => [
|
||||
'/members.php?rank=%u',
|
||||
'/members/%u',
|
||||
],
|
||||
'MEMBERLIST_PAGE' => [
|
||||
'/members.php?page=%u',
|
||||
'/members?page=%u',
|
||||
],
|
||||
'MEMBERLIST_ALL' => [
|
||||
'/members.php?sort=%s&rank=%u',
|
||||
'/members/%s/%u',
|
||||
],
|
||||
|
||||
// Forums
|
||||
'FORUM_INDEX' => [
|
||||
'/index.php?forum=true',
|
||||
'/forum',
|
||||
],
|
||||
'FORUM_SUB' => [
|
||||
'/viewforum.php?f=%u',
|
||||
'/forum/%u',
|
||||
],
|
||||
'FORUM_MARK_READ' => [
|
||||
'/viewforum.php?f=%u&read=true&session=%s',
|
||||
'/forum/%u?read=true&session=%s',
|
||||
],
|
||||
'FORUM_THREAD' => [
|
||||
'/viewtopic.php?t=%u',
|
||||
'/forum/thread/%u',
|
||||
],
|
||||
'FORUM_POST' => [
|
||||
'/viewtopic.php?p=%u',
|
||||
'/forum/post/%u',
|
||||
],
|
||||
'FORUM_REPLY' => [
|
||||
'/posting.php?t=%u',
|
||||
'/forum/thread/%u/reply',
|
||||
],
|
||||
'FORUM_NEW_THREAD' => [
|
||||
'/posting.php?f=%u',
|
||||
'/forum/%u/new',
|
||||
],
|
||||
'FORUM_EDIT_POST' => [
|
||||
'/posting.php?p=%1$u&edit=%1$u',
|
||||
'/forum/post/%u/edit',
|
||||
],
|
||||
'FORUM_DELETE_POST' => [
|
||||
'/posting.php?p=%1$u&delete=%1$u',
|
||||
'/forum/post/%u/delete',
|
||||
],
|
||||
'FORUM_QUOTE_POST' => [
|
||||
'/posting.php?p=%1$u"e=%1$u',
|
||||
'/forum/post/%u/quote',
|
||||
],
|
||||
'FORUM_LOCK' => [
|
||||
'/viewtopic.php?t=%u&lock=%s',
|
||||
'/forum/thread/%u?lock=%s',
|
||||
],
|
||||
'FORUM_STICKY' => [
|
||||
'/viewtopic.php?t=%u&sticky=%s',
|
||||
'/forum/thread/%u?sticky=%s',
|
||||
],
|
||||
'FORUM_ANNOUNCE' => [
|
||||
'/viewtopic.php?t=%u&announce=%s',
|
||||
'/forum/thread/%u?announce=%s',
|
||||
],
|
||||
'FORUM_RESTORE' => [
|
||||
'/viewtopic.php?t=%u&restore=%s',
|
||||
'/forum/thread/%u?restore=%s',
|
||||
],
|
||||
'FORUM_TRASH' => [
|
||||
'/viewtopic.php?t=%u&trash=%s',
|
||||
'/forum/thread/%u?trash=%s',
|
||||
],
|
||||
'FORUM_PRUNE' => [
|
||||
'/viewtopic.php?t=%u&prune=%s',
|
||||
'/forum/thread/%u?prune=%s',
|
||||
],
|
||||
|
||||
// Image serve references
|
||||
'IMAGE_AVATAR' => [
|
||||
'/imageserve.php?m=avatar&u=%u',
|
||||
'/a/%u',
|
||||
],
|
||||
'IMAGE_BACKGROUND' => [
|
||||
'/imageserve.php?m=background&u=%u',
|
||||
'/bg/%u',
|
||||
],
|
||||
'IMAGE_HEADER' => [
|
||||
'/imageserve.php?m=header&u=%u',
|
||||
'/u/%u/header',
|
||||
],
|
||||
|
||||
// User actions
|
||||
'USER_LOGOUT' => [
|
||||
'/authenticate.php?mode=logout&time=%u&session=%s&redirect=%s',
|
||||
'/logout?mode=logout&time=%u&session=%s&redirect=%s',
|
||||
],
|
||||
'USER_REPORT' => [
|
||||
'/report.php?mode=user&u=%u',
|
||||
'/u/%u/report',
|
||||
],
|
||||
'USER_PROFILE' => [
|
||||
'/profile.php?u=%s',
|
||||
'/u/%s',
|
||||
],
|
||||
'USER_COMMENTS' => [
|
||||
'/profile.php?u=%u&view=comments',
|
||||
'/u/%u/comments',
|
||||
],
|
||||
'USER_FRIENDS' => [
|
||||
'/profile.php?u=%u&view=friends',
|
||||
'/u/%u/friends',
|
||||
],
|
||||
'USER_GROUPS' => [
|
||||
'/profile.php?u=%u&view=groups',
|
||||
'/u/%u/groups',
|
||||
],
|
||||
'USER_THREADS' => [
|
||||
'/profile.php?u=%u&view=threads',
|
||||
'/u/%u/threads',
|
||||
],
|
||||
'USER_POSTS' => [
|
||||
'/profile.php?u=%u&view=posts',
|
||||
'/u/%u/posts',
|
||||
],
|
||||
'USER_GROUP' => [
|
||||
'/group.php?g=%u',
|
||||
'/g/%u',
|
||||
],
|
||||
|
||||
// Settings urls
|
||||
'SETTINGS_INDEX' => [
|
||||
'/settings.php',
|
||||
'/settings',
|
||||
],
|
||||
'SETTING_CAT' => [
|
||||
'/settings.php?cat=%s',
|
||||
'/settings/%s',
|
||||
],
|
||||
'SETTING_MODE' => [
|
||||
'/settings.php?cat=%s&mode=%s',
|
||||
'/settings/%s/%s',
|
||||
],
|
||||
'REQUEST_NOTIFICATIONS' => [
|
||||
'/settings.php?request-notifications=true',
|
||||
'/notifications',
|
||||
],
|
||||
|
||||
// Friend Actions
|
||||
'FRIEND_ACTION' => [
|
||||
'/settings.php?friend-action=true',
|
||||
'/friends',
|
||||
],
|
||||
'FRIEND_ADD' => [
|
||||
'/settings.php?friend-action=true&add=%u&session=%s&time=%u&redirect=%s',
|
||||
'/friends?add=%u&session=%s&time=%u&redirect=%s',
|
||||
],
|
||||
'FRIEND_REMOVE' => [
|
||||
'/settings.php?friend-action=true&remove=%u&session=%s&time=%u&redirect=%s',
|
||||
'/friends?remove=%u&session=%s&time=%u&redirect=%s',
|
||||
],
|
||||
|
||||
// Manage urls
|
||||
'MANAGE_INDEX' => [
|
||||
'/manage.php',
|
||||
'/manage',
|
||||
],
|
||||
'MANAGE_CAT' => [
|
||||
'/manage.php?cat=%s',
|
||||
'/manage/%s',
|
||||
],
|
||||
'MANAGE_MODE' => [
|
||||
'/manage.php?cat=%s&mode=%s',
|
||||
'/manage/%s/%s',
|
||||
],
|
||||
|
||||
// Comments urls
|
||||
'COMMENT_POST' => [
|
||||
'/settings.php?comment-action=true',
|
||||
'/comments',
|
||||
],
|
||||
'COMMENT_VOTE' => [
|
||||
'/settings.php?comment-action=true&id=%u&mode=vote&state=%u&category=%s&session=%s',
|
||||
'/comments?id=%u&mode=vote&state=%u&category=%s&session=%s',
|
||||
],
|
||||
'COMMENT_DELETE' => [
|
||||
'/settings.php?comment-action=true&id=%u&category=%s&mode=delete&session=%s',
|
||||
'/comments?id=%u&mode=delete&category=%s&session=%s',
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* Format a URL.
|
||||
*
|
||||
* @param string $lid The ID of a URL.
|
||||
* @param array $args Additional arguments.
|
||||
* @param bool $rewrite Toggle mod_rewrite.
|
||||
* @param string $lid doesn't do anything
|
||||
* @param array $args [category, mode]
|
||||
* @param bool $rewrite doesn't do anything either
|
||||
* @param bool $b hackjob for the settings panel
|
||||
*
|
||||
* @return null|string The formatted URL.
|
||||
* @return null|string url
|
||||
*/
|
||||
public function format($lid, $args = [], $rewrite = null, $b = true)
|
||||
{
|
||||
|
||||
// Check if the requested url exists
|
||||
if (!array_key_exists($lid, $this->urls)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($b && ($lid === 'SETTING_CAT' || $lid === 'SETTING_MODE')) {
|
||||
if (in_array('messages', $args)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($lid === 'SETTING_CAT') {
|
||||
return Router::route("settings.{$args[0]}");
|
||||
}
|
||||
|
||||
if ($lid === 'SETTING_MODE') {
|
||||
$a = implode('.', $args);
|
||||
$a = str_replace("usertitle", "title", $a);
|
||||
return Router::route("settings.{$a}");
|
||||
}
|
||||
if ($b) {
|
||||
$a = implode('.', $args);
|
||||
$a = str_replace("usertitle", "title", $a);
|
||||
return Router::route("settings.{$a}");
|
||||
}
|
||||
|
||||
// Format urls
|
||||
$formatted = vsprintf($this->urls[$lid][0], $args);
|
||||
$formatted = vsprintf('/settings.php?cat=%s&mode=%s', $args);
|
||||
|
||||
// Return the formatted url
|
||||
return $formatted;
|
||||
|
|
|
@ -254,8 +254,8 @@ class User
|
|||
public static function create($username, $password, $email, $ranks = [2])
|
||||
{
|
||||
// Set a few variables
|
||||
$usernameClean = Utils::cleanString($username, true);
|
||||
$emailClean = Utils::cleanString($email, true);
|
||||
$usernameClean = clean_string($username, true);
|
||||
$emailClean = clean_string($email, true);
|
||||
$password = Hashing::createHash($password);
|
||||
|
||||
// Insert the user into the database and get the id
|
||||
|
@ -273,7 +273,7 @@ class User
|
|||
'last_ip' => Net::pton(Net::ip()),
|
||||
'user_registered' => time(),
|
||||
'user_last_online' => 0,
|
||||
'user_country' => Utils::getCountryCode(),
|
||||
'user_country' => get_country_code(),
|
||||
]);
|
||||
|
||||
// Create a user object
|
||||
|
@ -299,7 +299,7 @@ class User
|
|||
// Get the user database row
|
||||
$userRow = DB::table('users')
|
||||
->where('user_id', $userId)
|
||||
->orWhere('username_clean', Utils::cleanString($userId, true, true))
|
||||
->orWhere('username_clean', clean_string($userId, true, true))
|
||||
->get();
|
||||
|
||||
// Populate the variables
|
||||
|
@ -401,7 +401,7 @@ class User
|
|||
*/
|
||||
public function country($long = false)
|
||||
{
|
||||
return $long ? Utils::getCountryName($this->country) : $this->country;
|
||||
return $long ? get_country_name($this->country) : $this->country;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -793,7 +793,7 @@ class User
|
|||
// Check if profile fields aren't fake
|
||||
foreach ($profileFields as $field) {
|
||||
// Completely strip all special characters from the field name
|
||||
$fieldName = Utils::cleanString($field->field_name, true, true);
|
||||
$fieldName = clean_string($field->field_name, true, true);
|
||||
|
||||
// Check if the user has the current field set otherwise continue
|
||||
if (!array_key_exists($fieldName, $profileValues)) {
|
||||
|
@ -1087,7 +1087,7 @@ class User
|
|||
public function setUsername($username)
|
||||
{
|
||||
// Create a cleaned version
|
||||
$username_clean = Utils::cleanString($username, true);
|
||||
$username_clean = clean_string($username, true);
|
||||
|
||||
// Check if the username is too short
|
||||
if (strlen($username_clean) < Config::get('username_min_length')) {
|
||||
|
@ -1210,7 +1210,7 @@ class User
|
|||
}
|
||||
|
||||
// Check password entropy
|
||||
if (Utils::pwdEntropy($new) < Config::get('min_entropy')) {
|
||||
if (password_entropy($new) < Config::get('min_entropy')) {
|
||||
return [0, 'PASS_TOO_SHIT'];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Holds various functions to interface with users.
|
||||
*
|
||||
* @package Sakura
|
||||
*/
|
||||
|
||||
namespace Sakura;
|
||||
|
||||
use Sakura\Perms\Site;
|
||||
|
||||
/**
|
||||
* User management
|
||||
*
|
||||
* @package Sakura
|
||||
* @author Julian van de Groep <me@flash.moe>
|
||||
*/
|
||||
class Users
|
||||
{
|
||||
/**
|
||||
* Send password forgot e-mail
|
||||
*
|
||||
* @param string $userId The user id.
|
||||
* @param string $email The e-mail.
|
||||
*/
|
||||
public static function sendPasswordForgot($userId, $email)
|
||||
{
|
||||
$user = User::construct($userId);
|
||||
|
||||
if (!$user->id || $user->permission(Site::DEACTIVATED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the verification key
|
||||
$verk = ActionCode::generate('LOST_PASS', $user->id);
|
||||
|
||||
$siteName = Config::get('sitename');
|
||||
$baseUrl = "http://" . Config::get('url_main');
|
||||
$reactivateLink = Router::route('auth.resetpassword') . "?u={$user->id}&k={$verk}";
|
||||
$signature = Config::get('mail_signature');
|
||||
|
||||
// Build the e-mail
|
||||
$message = "Hello {$user->username},\r\n\r\n"
|
||||
. "You are receiving this notification because you have (or someone pretending to be you has)"
|
||||
. " requested a password reset link to be sent for your account on \"{$siteName}\"."
|
||||
. " If you did not request this notification then please ignore it,"
|
||||
. " if you keep receiving it please contact the site administrator.\r\n\r\n"
|
||||
. "To use this password reset key you need to go to a special page."
|
||||
. " To do this click the link provided below.\r\n\r\n"
|
||||
. "{$baseUrl}{$reactivateLink}\r\n\r\n"
|
||||
. "If successful you should be able to change your password here.\r\n\r\n"
|
||||
. "You can of course change this password yourself via the settings page."
|
||||
. " If you have any difficulties please contact the site administrator.\r\n\r\n"
|
||||
. "--\r\n\r\nThanks\r\n\r\n{$signature}";
|
||||
|
||||
// Send the message
|
||||
Utils::sendMail([$user->email => $user->username], "{$siteName} password restoration", $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send activation e-mail.
|
||||
*
|
||||
* @param mixed $userId User ID.
|
||||
* @param mixed $customKey Key.
|
||||
*/
|
||||
public static function sendActivationMail($userId, $customKey = null)
|
||||
{
|
||||
|
||||
// Get the user data
|
||||
$user = User::construct($userId);
|
||||
|
||||
// User is already activated or doesn't even exist
|
||||
if (!$user->id || !$user->permission(Site::DEACTIVATED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate activation key
|
||||
$activate = ActionCode::generate('ACTIVATE', $user->id);
|
||||
|
||||
$siteName = Config::get('sitename');
|
||||
$baseUrl = "http://" . Config::get('url_main');
|
||||
$activateLink = Router::route('auth.activate') . "?u={$user->id}&k={$activate}";
|
||||
$profileLink = Router::route('user.profile', $user->id);
|
||||
$signature = Config::get('mail_signature');
|
||||
|
||||
// Build the e-mail
|
||||
$message = "Welcome to {$siteName}!\r\n\r\n"
|
||||
. "Please keep this e-mail for your records. Your account intormation is as follows:\r\n\r\n"
|
||||
. "----------------------------\r\n\r\n"
|
||||
. "Username: {$user->username}\r\n\r\n"
|
||||
. "Your profile: {$baseUrl}{$profileLink}\r\n\r\n"
|
||||
. "----------------------------\r\n\r\n"
|
||||
. "Please visit the following link in order to activate your account:\r\n\r\n"
|
||||
. "{$baseUrl}{$activateLink}\r\n\r\n"
|
||||
. "Your password has been securely stored in our database and cannot be retrieved. "
|
||||
. "In the event that it is forgotten,"
|
||||
. " you will be able to reset it using the email address associated with your account.\r\n\r\n"
|
||||
. "Thank you for registering.\r\n\r\n"
|
||||
. "--\r\n\r\nThanks\r\n\r\n{$signature}";
|
||||
|
||||
// Send the message
|
||||
Utils::sendMail([$user->email => $user->username], "{$siteName} activation mail", $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available profile fields.
|
||||
*
|
||||
* @return array|null The fields.
|
||||
*/
|
||||
public static function getProfileFields()
|
||||
{
|
||||
// Get profile fields
|
||||
$profileFields = DB::table('profilefields')
|
||||
->get();
|
||||
|
||||
// If there's nothing just return null
|
||||
if (!count($profileFields)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create output array
|
||||
$fields = [];
|
||||
|
||||
// Iterate over the fields and clean them up
|
||||
foreach ($profileFields as $field) {
|
||||
$field = get_object_vars($field);
|
||||
$fields[$field['field_id']] = $field;
|
||||
$fields[$field['field_id']]['field_identity'] = Utils::cleanString($field['field_name'], true, true);
|
||||
$fields[$field['field_id']]['field_additional'] = json_decode($field['field_additional'], true);
|
||||
}
|
||||
|
||||
// Return the yeahs
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available option fields.
|
||||
*
|
||||
* @return array|null The fields.
|
||||
*/
|
||||
public static function getOptionFields()
|
||||
{
|
||||
// Get option fields
|
||||
$optionFields = DB::table('optionfields')
|
||||
->get();
|
||||
|
||||
// If there's nothing just return null
|
||||
if (!count($optionFields)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create output array
|
||||
$fields = [];
|
||||
|
||||
// Iterate over the fields and clean them up
|
||||
foreach ($optionFields as $field) {
|
||||
$field = get_object_vars($field);
|
||||
|
||||
if (!ActiveUser::$user->permission(constant('Sakura\Perms\Site::' . $field['option_permission']))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields[$field['option_id']] = $field;
|
||||
}
|
||||
|
||||
// Return the yeahs
|
||||
return $fields;
|
||||
}
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Holds various utility functions.
|
||||
*
|
||||
* @package Sakura
|
||||
*/
|
||||
|
||||
namespace Sakura;
|
||||
|
||||
use PHPMailer;
|
||||
|
||||
/**
|
||||
* Meta utility functions.
|
||||
*
|
||||
* @package Sakura
|
||||
* @author Julian van de Groep <me@flash.moe>
|
||||
*/
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* The error handler.
|
||||
*
|
||||
* @param int $errno The error ID.
|
||||
* @param string $errstr Quick description of the event.
|
||||
* @param string $errfile File the error occurred in.
|
||||
* @param int $errline Line the error occurred on.
|
||||
*/
|
||||
public static function errorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
// Remove ROOT path from the error string and file location
|
||||
$errstr = str_replace(ROOT, '', $errstr);
|
||||
$errfile = str_replace(ROOT, '', $errfile);
|
||||
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
case E_USER_ERROR:
|
||||
$error = '<b>FATAL ERROR</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
$error = '<b>WARNING</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
case E_NOTICE:
|
||||
case E_USER_NOTICE:
|
||||
$error = '<b>NOTICE</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
default:
|
||||
$error = '<b>Unknown error type</b> [' . $errno . ']: ' . $errstr . ' on line ' . $errline
|
||||
. ' in ' . $errfile;
|
||||
}
|
||||
|
||||
// Truncate all previous outputs
|
||||
ob_clean();
|
||||
ob_end_clean();
|
||||
|
||||
// Check for dev mode
|
||||
$detailed = Config::local('dev', 'show_errors');
|
||||
|
||||
// Build page
|
||||
$errorPage = '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Sakura Internal Error</title>
|
||||
<style type="text/css">
|
||||
body { margin: 0; padding: 0; background: #EEE; color: #000;
|
||||
font: 12px/20px Verdana, Arial, Helvetica, sans-serif; }
|
||||
h1, h2 { font-weight: 100; background: #CAA; padding: 8px 5px 10px;
|
||||
margin: 0; font-style: italic; font-family: serif; }
|
||||
h1 { border-radius: 8px 8px 0 0; }
|
||||
h2 { margin: 0 -10px; }
|
||||
.container { border: 1px solid #CAA; margin: 10px auto; background: #FFF;
|
||||
box-shadow: 2px 2px 1em #888; max-width: 1024px; border-radius: 10px; }
|
||||
.container .inner { padding: 0 10px; }
|
||||
.container .inner .error { background: #555; color: #EEE; border-left: 5px solid #C22;
|
||||
padding: 4px 6px; text-shadow: 0 1px 1px #888; white-space: pre-wrap;
|
||||
word-wrap: break-word; margin: 12px 0; border-radius: 5px; box-shadow: inset 0 0 1em #333; }
|
||||
.container .footer { border-top: 1px solid #CAA; font-size: x-small; padding: 0 5px 1px; }
|
||||
a { color: #77E; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
a:active { color: #E77; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>An error occurred while executing the script.</h1>
|
||||
<div class="inner">
|
||||
<p>To prevent potential security risks or data loss Sakura has stopped execution of the script.</p>';
|
||||
|
||||
if (isset($errid)) {
|
||||
$errorPage .= '<p>The error and surrounding data has been logged.</p>
|
||||
<h2>' . (!$detailed ? 'Report the following text to a staff member' : 'Logged as') . '</h2>
|
||||
<pre class="error">' . $errid . '</pre>';
|
||||
} else {
|
||||
$errorPage .= '<p>Sakura was not able to log this error which could mean that there was an error
|
||||
with the database connection. If you\'re the system administrator check the database credentials
|
||||
and make sure the server is running and if you\'re not please let the system administrator
|
||||
know about this error if it occurs again.</p>';
|
||||
}
|
||||
|
||||
if ($detailed) {
|
||||
$errorPage .= ' <h2>Summary</h2>
|
||||
<pre class="error">' . $error . '</pre>
|
||||
<h2>Backtraces</h2>';
|
||||
|
||||
foreach (debug_backtrace() as $num => $trace) {
|
||||
$errorPage .= '<h3>#' . $num . '</h3><pre class="error">';
|
||||
|
||||
foreach ($trace as $key => $val) {
|
||||
$errorPage .=
|
||||
str_pad(
|
||||
'[' . $key . ']',
|
||||
12
|
||||
) . '=> ' . (
|
||||
is_array($val) || is_object($val) ?
|
||||
json_encode($val) :
|
||||
$val
|
||||
) . "\r\n";
|
||||
}
|
||||
|
||||
$errorPage .= '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
$errorPage .= '</div>
|
||||
<div class="footer">
|
||||
Sakura r' . SAKURA_VERSION . '.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
// Die and display error message
|
||||
die($errorPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an e-mail.
|
||||
*
|
||||
* @param string $to Destination e-mail.
|
||||
* @param string $subject E-mail subject.
|
||||
* @param string $body Contents of the message.
|
||||
* @return bool|string Return whatever PHPMailer returns.
|
||||
*/
|
||||
public static function sendMail($to, $subject, $body)
|
||||
{
|
||||
// Initialise PHPMailer
|
||||
$mail = new PHPMailer();
|
||||
|
||||
// Set to SMTP
|
||||
$mail->isSMTP();
|
||||
|
||||
// Set the SMTP server host
|
||||
$mail->Host = Config::get('smtp_server');
|
||||
|
||||
// Do we require authentication?
|
||||
$mail->SMTPAuth = Config::get('smtp_auth');
|
||||
|
||||
// Do we encrypt as well?
|
||||
$mail->SMTPSecure = Config::get('smtp_secure');
|
||||
|
||||
// Set the port to the SMTP server
|
||||
$mail->Port = Config::get('smtp_port');
|
||||
|
||||
// If authentication is required log in as well
|
||||
if (Config::get('smtp_auth')) {
|
||||
$mail->Username = Config::get('smtp_username');
|
||||
$mail->Password = base64_decode(Config::get('smtp_password'));
|
||||
}
|
||||
|
||||
// Add a reply-to header
|
||||
$mail->addReplyTo(Config::get('smtp_replyto_mail'), Config::get('smtp_replyto_name'));
|
||||
|
||||
// Set a from address as well
|
||||
$mail->setFrom(Config::get('smtp_from_email'), Config::get('smtp_from_name'));
|
||||
|
||||
// Set the addressee
|
||||
foreach ($to as $email => $name) {
|
||||
$mail->addBCC($email, $name);
|
||||
}
|
||||
|
||||
// Subject line
|
||||
$mail->Subject = $subject;
|
||||
|
||||
// Set body
|
||||
$mail->Body = $body;
|
||||
|
||||
// Send the message
|
||||
$send = $mail->send();
|
||||
|
||||
// Clear the addressee list
|
||||
$mail->clearAddresses();
|
||||
|
||||
// If we got an error return the error
|
||||
if (!$send) {
|
||||
return $mail->ErrorInfo;
|
||||
}
|
||||
|
||||
// Else just return whatever
|
||||
return $send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a string
|
||||
*
|
||||
* @param string $string Dirty string.
|
||||
* @param bool $lower Make the string lowercase.
|
||||
* @param bool $noSpecial String all special characters.
|
||||
* @param bool $replaceSpecial Thing to replace special characters with.
|
||||
*
|
||||
* @return string Clean string.
|
||||
*/
|
||||
public static function cleanString($string, $lower = false, $noSpecial = false, $replaceSpecial = '')
|
||||
{
|
||||
// Run common sanitisation function over string
|
||||
$string = htmlentities($string, ENT_NOQUOTES | ENT_HTML401, Config::get('charset'));
|
||||
$string = stripslashes($string);
|
||||
$string = strip_tags($string);
|
||||
|
||||
// If set also make the string lowercase
|
||||
if ($lower) {
|
||||
$string = strtolower($string);
|
||||
}
|
||||
|
||||
// If set remove all characters that aren't a-z or 0-9
|
||||
if ($noSpecial) {
|
||||
$string = preg_replace('/[^a-z0-9]/', $replaceSpecial, $string);
|
||||
}
|
||||
|
||||
// Return clean string
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate MX records.
|
||||
*
|
||||
* @param string $email E-mail address.
|
||||
*
|
||||
* @return bool Success.
|
||||
*/
|
||||
public static function checkMXRecord($email)
|
||||
{
|
||||
// Get the domain from the e-mail address
|
||||
$domain = substr(strstr($email, '@'), 1);
|
||||
|
||||
// Check the MX record
|
||||
$record = checkdnsrr($domain, 'MX');
|
||||
|
||||
// Return the record data
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country code of a visitor.
|
||||
*
|
||||
* @return string 2 character country code.
|
||||
*/
|
||||
public static function getCountryCode()
|
||||
{
|
||||
// Attempt to get country code using PHP's built in geo thing
|
||||
if (function_exists("geoip_country_code_by_name")) {
|
||||
try {
|
||||
$code = geoip_country_code_by_name(Net::ip());
|
||||
|
||||
// Check if $code is anything
|
||||
if ($code) {
|
||||
return $code;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the required header is set and return it
|
||||
if (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
|
||||
return $_SERVER['HTTP_CF_IPCOUNTRY'];
|
||||
}
|
||||
|
||||
// Return XX as a fallback
|
||||
return 'XX';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the entropy of a password.
|
||||
*
|
||||
* @param string $pw Password.
|
||||
*
|
||||
* @return double|int Entropy.
|
||||
*/
|
||||
public static function pwdEntropy($pw)
|
||||
{
|
||||
// Decode utf-8 chars
|
||||
$pw = utf8_decode($pw);
|
||||
|
||||
// Count the amount of unique characters in the password string and calculate the entropy
|
||||
return count(count_chars($pw, 1)) * log(256, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country name from a 2 character code.
|
||||
*
|
||||
* @param string $code The country code.
|
||||
*
|
||||
* @return string The country name.
|
||||
*/
|
||||
public static function getCountryName($code)
|
||||
{
|
||||
// Catch XX
|
||||
if (strtolower($code) === 'xx') {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
// Catch proxy
|
||||
if (strtolower($code) === 'a1') {
|
||||
return 'Anonymous Proxy';
|
||||
}
|
||||
|
||||
return locale_get_display_region("-{$code}", 'en');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte symbol for a unit from bytes.
|
||||
*
|
||||
* @param int $bytes The amount of bytes.
|
||||
*
|
||||
* @return string The converted amount with the symbol.
|
||||
*/
|
||||
public static function getByteSymbol($bytes)
|
||||
{
|
||||
// Return nothing if the input was 0
|
||||
if (!$bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Array with byte symbols
|
||||
$symbols = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
|
||||
// Calculate byte entity
|
||||
$exp = floor(log($bytes) / log(1024));
|
||||
|
||||
// Format the things
|
||||
$bytes = sprintf("%.2f " . $symbols[$exp], ($bytes / pow(1024, floor($exp))));
|
||||
|
||||
// Return the formatted string
|
||||
return $bytes;
|
||||
}
|
||||
}
|
|
@ -1277,12 +1277,12 @@ a.default:active {
|
|||
}
|
||||
|
||||
.content .content-right {
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.content .content-left {
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
min-height: 0;
|
||||
border-top: 1px solid #9475B2;
|
||||
}
|
||||
|
@ -1558,6 +1558,13 @@ a.default:active {
|
|||
margin-left: 2em;
|
||||
list-style: square;
|
||||
}
|
||||
.settings .content-left {
|
||||
width: 850px;
|
||||
}
|
||||
.settings .content-right {
|
||||
width: 174px;
|
||||
text-align: right;
|
||||
}
|
||||
.settings .right-menu-nav > div {
|
||||
background: #C2AFFE;
|
||||
padding: 4px;
|
||||
|
@ -1573,7 +1580,7 @@ a.default:active {
|
|||
line-height: 25px;
|
||||
color: #22E;
|
||||
text-decoration: none;
|
||||
padding-left: 10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.settings .right-menu-nav > a:hover {
|
||||
|
|
|
@ -244,145 +244,6 @@ if (isset($_POST['submit']) && isset($_POST['submit'])) {
|
|||
];
|
||||
break;
|
||||
|
||||
// Profile
|
||||
case 'profile':
|
||||
// Get profile fields and create storage var
|
||||
$fields = Users::getProfileFields();
|
||||
|
||||
// Delete all profile fields
|
||||
DB::table('user_profilefields')
|
||||
->where('user_id', $currentUser->id)
|
||||
->delete();
|
||||
|
||||
// Go over each field
|
||||
foreach ($fields as $field) {
|
||||
// Add to the store array
|
||||
if (isset($_POST['profile_' . $field['field_identity']]) && !empty($_POST['profile_' . $field['field_identity']])) {
|
||||
DB::table('user_profilefields')
|
||||
->insert([
|
||||
'user_id' => $currentUser->id,
|
||||
'field_name' => $field['field_identity'],
|
||||
'field_value' => $_POST['profile_' . $field['field_identity']],
|
||||
]);
|
||||
}
|
||||
|
||||
// Check if there's additional values we should keep in mind
|
||||
if (isset($field['field_additional']) && !empty($field['field_additional'])) {
|
||||
// Go over each additional value
|
||||
foreach ($field['field_additional'] as $addKey => $addVal) {
|
||||
// Add to the array
|
||||
$store = (isset($_POST['profile_additional_' . $addKey]) || !empty($_POST['profile_additional_' . $addKey])) ? $_POST['profile_additional_' . $addKey] : false;
|
||||
DB::table('user_profilefields')
|
||||
->insert([
|
||||
'user_id' => $currentUser->id,
|
||||
'field_name' => $addKey,
|
||||
'field_value' => $store,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set render data
|
||||
$renderData['page'] = [
|
||||
'redirect' => $redirect,
|
||||
'message' => 'Your profile has been updated!',
|
||||
'success' => 1,
|
||||
];
|
||||
|
||||
// Birthdays
|
||||
if (isset($_POST['birthday_day'])
|
||||
&& isset($_POST['birthday_month'])
|
||||
&& isset($_POST['birthday_year'])) {
|
||||
// Check if the values aren't fucked with
|
||||
if ($_POST['birthday_day'] < 0
|
||||
|| $_POST['birthday_day'] > 31
|
||||
|| $_POST['birthday_month'] < 0
|
||||
|| $_POST['birthday_month'] > 12
|
||||
|| (
|
||||
$_POST['birthday_year'] != 0
|
||||
&& $_POST['birthday_year'] < (date("Y") - 100)
|
||||
)
|
||||
|| $_POST['birthday_year'] > date("Y")) {
|
||||
$renderData['page']['message'] = 'Your birthdate is invalid.';
|
||||
$renderData['page']['success'] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the values aren't fucked with
|
||||
if ((
|
||||
$_POST['birthday_day'] < 1
|
||||
&& $_POST['birthday_month'] > 0
|
||||
)
|
||||
|| (
|
||||
$_POST['birthday_day'] > 0
|
||||
&& $_POST['birthday_month'] < 1)
|
||||
) {
|
||||
$renderData['page']['message'] = 'Only setting a day or month is disallowed.';
|
||||
$renderData['page']['success'] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the values aren't fucked with
|
||||
if ($_POST['birthday_year'] > 0
|
||||
&& (
|
||||
$_POST['birthday_day'] < 1
|
||||
|| $_POST['birthday_month'] < 1
|
||||
)
|
||||
) {
|
||||
$renderData['page']['message'] = 'Only setting a year is disallowed.';
|
||||
$renderData['page']['success'] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
$birthdate = implode(
|
||||
'-',
|
||||
[$_POST['birthday_year'], $_POST['birthday_month'], $_POST['birthday_day']]
|
||||
);
|
||||
|
||||
DB::table('users')
|
||||
->where('user_id', $currentUser->id)
|
||||
->update([
|
||||
'user_birthday' => $birthdate,
|
||||
]);
|
||||
}
|
||||
break;
|
||||
|
||||
// Site Options
|
||||
case 'options':
|
||||
// Get profile fields and create storage var
|
||||
$fields = Users::getOptionFields();
|
||||
|
||||
// Delete all option fields for this user
|
||||
DB::table('user_optionfields')
|
||||
->where('user_id', $currentUser->id)
|
||||
->delete();
|
||||
|
||||
// Go over each field
|
||||
foreach ($fields as $field) {
|
||||
// Make sure the user has sufficient permissions to complete this action
|
||||
if (!$currentUser->permission(constant('Sakura\Perms\Site::' . $field['option_permission']))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($_POST['option_' . $field['option_id']])
|
||||
&& !empty($_POST['option_' . $field['option_id']])) {
|
||||
DB::table('user_optionfields')
|
||||
->insert([
|
||||
'user_id' => $currentUser->id,
|
||||
'field_name' => $field['option_id'],
|
||||
'field_value' => $_POST['option_' . $field['option_id']],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set render data
|
||||
$renderData['page'] = [
|
||||
'redirect' => $redirect,
|
||||
'message' => 'Changed your options!',
|
||||
'success' => 1,
|
||||
];
|
||||
break;
|
||||
|
||||
// Usertitle
|
||||
case 'usertitle':
|
||||
// Check permissions
|
||||
|
@ -799,7 +660,7 @@ if (ActiveUser::$user->id) {
|
|||
'menu' => true,
|
||||
],
|
||||
'profile' => [
|
||||
'title' => 'Edit Profile',
|
||||
'title' => 'Profile',
|
||||
'description' => [
|
||||
'These are the external account links etc.
|
||||
on your profile, shouldn\'t need any additional explanation for this one.',
|
||||
|
@ -808,7 +669,7 @@ if (ActiveUser::$user->id) {
|
|||
'menu' => true,
|
||||
],
|
||||
'options' => [
|
||||
'title' => 'Site Options',
|
||||
'title' => 'Options',
|
||||
'description' => [
|
||||
'These are a few personalisation options for the site while you\'re logged in.',
|
||||
],
|
||||
|
@ -837,44 +698,7 @@ if (ActiveUser::$user->id) {
|
|||
'menu' => true,
|
||||
],
|
||||
],
|
||||
]/*,
|
||||
'messages' => [
|
||||
'title' => 'Messages',
|
||||
'modes' => [
|
||||
'inbox' => [
|
||||
'title' => 'Inbox',
|
||||
'description' => [
|
||||
'The list of messages you\'ve received.',
|
||||
],
|
||||
'access' => $currentUser->permission(Site::USE_MESSAGES),
|
||||
'menu' => true,
|
||||
],
|
||||
'sent' => [
|
||||
'title' => 'Sent',
|
||||
'description' => [
|
||||
'The list of messages you\'ve sent to other users.',
|
||||
],
|
||||
'access' => $currentUser->permission(Site::USE_MESSAGES),
|
||||
'menu' => true,
|
||||
],
|
||||
'compose' => [
|
||||
'title' => 'Compose',
|
||||
'description' => [
|
||||
'Write a new message.',
|
||||
],
|
||||
'access' => $currentUser->permission(Site::SEND_MESSAGES),
|
||||
'menu' => true,
|
||||
],
|
||||
'read' => [
|
||||
'title' => 'Read',
|
||||
'description' => [
|
||||
'Read a message.',
|
||||
],
|
||||
'access' => $currentUser->permission(Site::USE_MESSAGES),
|
||||
'menu' => false,
|
||||
],
|
||||
],
|
||||
]*/,
|
||||
'notifications' => [
|
||||
'title' => 'Notifications',
|
||||
'modes' => [
|
||||
|
@ -949,7 +773,7 @@ if (ActiveUser::$user->id) {
|
|||
'title' => 'Account',
|
||||
'modes' => [
|
||||
'email' => [
|
||||
'title' => 'E-mail Address',
|
||||
'title' => 'E-mail address',
|
||||
'description' => [
|
||||
'You e-mail address is used for password recovery and stuff like that, we won\'t spam you ;).',
|
||||
],
|
||||
|
@ -966,7 +790,7 @@ if (ActiveUser::$user->id) {
|
|||
'menu' => true,
|
||||
],
|
||||
'usertitle' => [
|
||||
'title' => 'Usertitle',
|
||||
'title' => 'Title',
|
||||
'description' => [
|
||||
'That little piece of text displayed under your username on your profile.',
|
||||
],
|
||||
|
@ -1011,7 +835,7 @@ if (ActiveUser::$user->id) {
|
|||
'menu' => true,
|
||||
],
|
||||
'deactivate' => [
|
||||
'title' => 'Deactivate Account',
|
||||
'title' => 'Deactivate',
|
||||
'description' => [
|
||||
'You can deactivate your account here if you want to leave :(.',
|
||||
],
|
||||
|
@ -1069,39 +893,6 @@ if (ActiveUser::$user->id) {
|
|||
|
||||
// Section specific
|
||||
switch ($category . '.' . $mode) {
|
||||
// Profile
|
||||
case 'general.profile':
|
||||
$renderData['profile'] = [
|
||||
'fields' => Users::getProfileFields(),
|
||||
'months' => [
|
||||
1 => 'January',
|
||||
2 => 'February',
|
||||
3 => 'March',
|
||||
4 => 'April',
|
||||
5 => 'May',
|
||||
6 => 'June',
|
||||
7 => 'July',
|
||||
8 => 'August',
|
||||
9 => 'September',
|
||||
10 => 'October',
|
||||
11 => 'November',
|
||||
12 => 'December',
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
||||
// Options
|
||||
case 'general.options':
|
||||
$renderData['options'] = [
|
||||
'fields' => Users::getOptionFields(),
|
||||
];
|
||||
break;
|
||||
|
||||
// PM inbox
|
||||
case 'messages.inbox':
|
||||
$renderData['messages'] = [];
|
||||
break;
|
||||
|
||||
// Avatar and background sizes
|
||||
case 'appearance.avatar':
|
||||
case 'appearance.background':
|
||||
|
@ -1112,7 +903,7 @@ if (ActiveUser::$user->id) {
|
|||
'min_width' => Config::get($mode . '_min_width'),
|
||||
'min_height' => Config::get($mode . '_min_height'),
|
||||
'max_size' => Config::get($mode . '_max_fsize'),
|
||||
'max_size_view' => Utils::getByteSymbol(Config::get($mode . '_max_fsize')),
|
||||
'max_size_view' => byte_symbol(Config::get($mode . '_max_fsize')),
|
||||
];
|
||||
break;
|
||||
|
||||
|
|
|
@ -94,8 +94,11 @@ Router::group(['prefix' => 'members', 'before' => 'loginCheck'], function () {
|
|||
});
|
||||
|
||||
// User
|
||||
Router::get('/u/{id}', 'UserController@profile', 'user.profile');
|
||||
Router::get('/u/{id}/header', 'FileController@header', 'user.header');
|
||||
Router::group(['prefix' => 'u'], function () {
|
||||
Router::get('/{id}', 'UserController@profile', 'user.profile');
|
||||
Router::get('/{id}/report', 'UserController@report', 'user.report');
|
||||
Router::get('/{id}/header', 'FileController@header', 'user.header');
|
||||
});
|
||||
|
||||
// Notifications
|
||||
Router::group(['prefix' => 'notifications'], function () {
|
||||
|
@ -152,7 +155,9 @@ Router::group(['prefix' => 'settings', 'before' => 'loginCheck'], function () {
|
|||
|
||||
Router::get('/home', 'Settings.GeneralController@home', 'settings.general.home');
|
||||
Router::get('/profile', 'Settings.GeneralController@profile', 'settings.general.profile');
|
||||
Router::post('/profile', 'Settings.GeneralController@profile', 'settings.general.profile');
|
||||
Router::get('/options', 'Settings.GeneralController@options', 'settings.general.options');
|
||||
Router::post('/options', 'Settings.GeneralController@options', 'settings.general.options');
|
||||
});
|
||||
|
||||
// Friends section
|
||||
|
|
32
sakura.php
32
sakura.php
|
@ -8,7 +8,7 @@
|
|||
namespace Sakura;
|
||||
|
||||
// Define Sakura version
|
||||
define('SAKURA_VERSION', 20160331);
|
||||
define('SAKURA_VERSION', 20160401);
|
||||
|
||||
// Define Sakura Path
|
||||
define('ROOT', __DIR__ . '/');
|
||||
|
@ -30,33 +30,18 @@ if (version_compare(phpversion(), '7.0.0', '<')) {
|
|||
|
||||
// Check if the composer autoloader exists
|
||||
if (!file_exists(ROOT . 'vendor/autoload.php')) {
|
||||
throw new \Exception('Autoloader not found, did you run composer?');
|
||||
throw new \Exception('Autoloader not found, did you run composer install?');
|
||||
}
|
||||
|
||||
// Require composer libraries
|
||||
require_once ROOT . 'vendor/autoload.php';
|
||||
|
||||
// Setup the autoloader
|
||||
spl_autoload_register(function ($className) {
|
||||
// Replace \ with /
|
||||
$className = str_replace('\\', '/', $className);
|
||||
|
||||
// Create a throwaway count variable
|
||||
$i = 1;
|
||||
|
||||
// Replace the sakura namespace with the libraries directory
|
||||
$className = str_replace('Sakura/', 'libraries/', $className, $i);
|
||||
|
||||
// Require the file
|
||||
require_once ROOT . $className . '.php';
|
||||
});
|
||||
|
||||
// Set Error handler
|
||||
set_error_handler(['Sakura\Utils', 'errorHandler']);
|
||||
|
||||
// Load the local configuration
|
||||
Config::init(ROOT . 'config/config.ini');
|
||||
|
||||
// Set Error handler
|
||||
set_error_handler('error_handler');
|
||||
|
||||
// Change error reporting according to the dev configuration
|
||||
error_reporting(Config::local('dev', 'show_errors') ? -1 : 0);
|
||||
|
||||
|
@ -110,12 +95,9 @@ ActiveUser::init(
|
|||
// Create the Urls object
|
||||
$urls = new Urls();
|
||||
|
||||
// Prepare the name of the template to load
|
||||
$templateName = Config::get('site_style');
|
||||
|
||||
if (!defined('SAKURA_NO_TPL')) {
|
||||
// Start templating engine
|
||||
Template::set($templateName);
|
||||
Template::set(Config::get('site_style'));
|
||||
|
||||
// Set base page rendering data
|
||||
Template::vars([
|
||||
|
@ -128,8 +110,6 @@ if (!defined('SAKURA_NO_TPL')) {
|
|||
'showChangelog' => Config::local('dev', 'show_changelog'),
|
||||
],
|
||||
|
||||
'resources' => Config::get('content_path') . '/data/' . $templateName,
|
||||
|
||||
'currentPage' => $_SERVER['REQUEST_URI'] ?? null,
|
||||
'referrer' => $_SERVER['HTTP_REFERER'] ?? null,
|
||||
],
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
<li class="settings"><a title="Change your settings" href="{{ urls.format('SETTINGS_INDEX') }}">Settings</a></li>
|
||||
{% else %}
|
||||
<li class="{% if user.checkFriends(profile.id) == 2 %}mutualFriend{% elseif user.checkFriends(profile.id) == 1 %}pendingFriend{% else %}addFriend{% endif %}"><a href="{% if user.checkFriends(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 %}">{% if user.checkFriends(profile.id) == 0 %}Add friend{% else %}Friends{% endif %}</a></li>
|
||||
<li class="report"><a href="{{ urls.format('USER_REPORT', [profile.id]) }}">Report</a></li>
|
||||
<li class="report"><a href="{{ route('user.report', profile.id) }}">Report</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{% if comment.userData.id == user.id %}
|
||||
<li><a href="javascript:void(0);" onclick="commentDelete({{ comment.id }});" class="clean fa fa-trash-o" title="Delete"></a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ urls.format('USER_REPORT', [comment.userData.id]) }}" class="clean fa fa-exclamation-circle" title="Report"></a></li>
|
||||
<li><a href="{{ route('user.report', comment.userData.id) }}" class="clean fa fa-exclamation-circle" title="Report"></a></li>
|
||||
{% endif %}
|
||||
<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="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>
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
{% 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) %}
|
||||
<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-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>
|
||||
<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="{{ route('user.report', post.poster.id) }}"></a>
|
||||
{% endif %}
|
||||
<a class="fa fa-reply" title="Quote this post" href="javascript:void(0);" onclick="quotePost({{ post.id }});"></a>
|
||||
</div>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
"siteName": "{{ config('sitename') }}",
|
||||
"content": "{{ config('content_path') }}",
|
||||
"resources": "{{ sakura.resources }}",
|
||||
"recaptchaEnabled": "{{ config('recaptcha') }}",
|
||||
|
||||
"minUserLen": {{ config('username_min_length') }},
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
{% set friends = user.friends(1)|batch(12) %}
|
||||
|
||||
{% set paginationPages = friends %}
|
||||
{% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'listing']) }}{% endset %}
|
||||
|
||||
{% block css %}
|
||||
<style type="text/css">
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% if friends|length %}
|
||||
<div class="friends-list">
|
||||
{% for friend in friends[get.page|default(1) - 1] %}
|
||||
<div class="friend-container" id="friendslist-friend-{{ friend.id }}">
|
||||
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
|
||||
<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>
|
||||
</a>
|
||||
<div class="friends-list-actions">
|
||||
<a class="remove fill fa fa-remove" title="Remove friend" href="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
{% if friends|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any friends yet!</h1>
|
||||
{% endif %}
|
|
@ -1,38 +0,0 @@
|
|||
{% set friends = user.friends(-1)|batch(12) %}
|
||||
|
||||
{% set paginationPages = friends %}
|
||||
{% set paginationUrl %}{{ urls.format('SETTING_MODE', ['friends', 'requests']) }}{% endset %}
|
||||
|
||||
{% block css %}
|
||||
<style type="text/css">
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% if friends|length %}
|
||||
<div class="friends-list">
|
||||
{% for friend in friends[get.page|default(1) - 1] %}
|
||||
<div class="friend-container" id="friend-{{ friend.id }}">
|
||||
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
|
||||
<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>
|
||||
</a>
|
||||
<div class="friends-list-actions">
|
||||
<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="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
{% if friends|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any pending requests!</h1>
|
||||
{% endif %}
|
|
@ -1,20 +0,0 @@
|
|||
<div style="margin: 5px;">
|
||||
<h1 class="stylised">Common Tasks</h1>
|
||||
<h2>Profile</h2>
|
||||
<ul>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['appearance', 'avatar']) }}" class="default">Change Avatar</a></li>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['appearance', 'userpage']) }}" class="default">Change Userpage</a></li>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['appearance', 'signature']) }}" class="default">Change Signature</a></li>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['general', 'profile']) }}" class="default">Change Profile Details</a></li>
|
||||
</ul>
|
||||
<h2>Messaging</h2>
|
||||
<ul>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['messages', 'inbox']) }}" class="default">View Inbox</a></li>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['messages', 'compose']) }}" class="default">Send PM</a></li>
|
||||
</ul>
|
||||
<h2>Account</h2>
|
||||
<ul>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['advanced', 'sessions']) }}" class="default">Manage Active Sessions</a></li>
|
||||
<li><a href="{{ urls.format('SETTING_MODE', ['account', 'password']) }}" class="default">Change Password</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,31 +0,0 @@
|
|||
{% if options.fields %}
|
||||
<form enctype="multipart/form-data" method="post" action="{{ sakura.currentPage }}" id="optionsForm">
|
||||
<input type="hidden" name="sessid" value="{{ session_id() }}" />
|
||||
<input type="hidden" name="timestamp" value="{{ date().timestamp }}" />
|
||||
<input type="hidden" name="mode" value="options" />
|
||||
{% for field in options.fields %}
|
||||
<div class="profile-field">
|
||||
<div>
|
||||
<h2>{{ field.option_name }}</h2>
|
||||
<div style="font-size: .8em; line-height: 110%;">
|
||||
{{ field.option_description }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 8px 0;">
|
||||
<input type="{{ field.option_type }}" name="option_{{ field.option_id }}" class="inputStyling"{% if user.optionFields[field.option_id] %}{% if field.option_type == 'checkbox' and user.optionFields[field.option_id] %} checked="checked" value="option_{{ field.option_id }}"{% else %} value="{{ user.optionFields[field.option_id] }}"{% endif %}{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="profile-save">
|
||||
<input type="submit" value="Save" name="submit" class="inputStyling" />
|
||||
<input type="reset" value="Reset" name="reset" class="inputStyling" />
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function() {
|
||||
prepareAjaxForm('optionsForm', 'Changing Options...');
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">There are currently no changeable options.</h1>
|
||||
{% endif %}
|
|
@ -1,59 +0,0 @@
|
|||
{% set birthday = user.birthday|split('-') %}
|
||||
|
||||
<form enctype="multipart/form-data" method="post" action="{{ sakura.currentPage }}" id="editProfileForm">
|
||||
<input type="hidden" name="sessid" value="{{ session_id() }}" />
|
||||
<input type="hidden" name="timestamp" value="{{ date().timestamp }}" />
|
||||
<input type="hidden" name="mode" value="profile" />
|
||||
{% for field in profile.fields %}
|
||||
<div class="profile-field">
|
||||
<div>
|
||||
<h2>{{ field.field_name }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<input type="{{ field.field_type }}" name="profile_{{ field.field_identity }}" class="inputStyling" placeholder="{{ field.field_description }}"{% if user.profileFields[field.field_identity].value %}{% if field.field_type == 'checkbox' and user.profileFields[field.field_identity].value == 'true' %} checked="checked" value="profile_{{ field.field_identity }}"{% else %} value="{{ user.profileFields[field.field_identity].value }}"{% endif %}{% endif %} />
|
||||
</div>
|
||||
{% if field.field_additional %}
|
||||
{% for id,addit in field.field_additional %}
|
||||
<div>
|
||||
<input type="{{ addit[0] }}" id="{{ id }}" name="profile_additional_{{ id }}"{% if user.profileFields[field.field_identity][id] %}{% if addit[0] == 'checkbox' and user.profileFields[field.field_identity][id] == true %} checked="checked"{% else %} value="{{ user.profileFields[field.field_identity][id] }}"{% endif %}{% endif %} />
|
||||
<label for="{{ id }}" style="font-size: 10px;">{{ addit[1]|raw }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="profile-field birthday">
|
||||
<div>
|
||||
<h2>Birthday</h2>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
Day: <select name="birthday_day">
|
||||
<option value="0"{% if not birthday[2] %} selected="selected"{% endif %}>--</option>
|
||||
{% for i in 1..31 %}
|
||||
<option value="{{ i }}"{% if birthday[2] == i %} selected="selected"{% endif %}>{{ i }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
Month: <select name="birthday_month">
|
||||
<option value="0"{% if not birthday[1] %} selected="selected"{% endif %}>--</option>
|
||||
{% for i in 1..12 %}
|
||||
<option value="{{ i }}"{% if birthday[1] == i %} selected="selected"{% endif %}>{{ profile.months[i] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
Year: <select name="birthday_year">
|
||||
<option value="0"{% if not birthday[0] %} selected="selected"{% endif %}>----</option>
|
||||
{% for i in "now"|date('Y')..("now"|date('Y') - 100) %}
|
||||
<option value="{{ i }}"{% if birthday[0] == i %} selected="selected"{% endif %}>{{ i }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-save">
|
||||
<input type="submit" value="Save" name="submit" class="inputStyling" />
|
||||
<input type="reset" value="Reset" name="reset" class="inputStyling" />
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function() {
|
||||
prepareAjaxForm('editProfileForm', 'Updating Profile...');
|
||||
});
|
||||
</script>
|
|
@ -1,23 +0,0 @@
|
|||
{% if messages|length %}
|
||||
<table class="msgTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>From</th>
|
||||
<th>Subject</th>
|
||||
<th>Sent on</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for message in messages %}
|
||||
<tr>
|
||||
<td><a href="/u/{{ message.data.from.user.id }}" class="default" style="font-weight: 700; color: {{ message.data.from.user.colour }};">{{ message.data.from.user.username }}</a></td>
|
||||
<td><a href="/messages/read/{{ message.id }}" class="default">{{ message.subject }}</a></td>
|
||||
<td>{{ message.time|date(config('date_format')) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<h1 class="stylised"style="line-height: 1.8em; text-align: center;">Nothing to view!</h1>
|
||||
{% endif %}
|
||||
<h3 style="text-align: center;">Click Compose in the menu on the right side to write a new message!</h3>
|
|
@ -1,49 +0,0 @@
|
|||
{% set alerts = user.notifications(0, false)|batch(10) %}
|
||||
|
||||
{% set paginationPages = alerts %}
|
||||
{% set paginationUrl %}{{ urls.format('SETTING_MODE', ['notifications', 'history']) }}{% endset %}
|
||||
|
||||
{% block css %}
|
||||
<style type="text/css">
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% if alerts %}
|
||||
<div class="notification-history">
|
||||
{% for alert in alerts[get.page|default(1) - 1] %}
|
||||
<a id="notif-hist-{{ alert.id }}" class="clean {% if alert.read %}read{% endif %}"{% if alert.link %} href="{{ alert.link }}"{% endif %}>
|
||||
<div class="notif-hist-icon">
|
||||
{% if 'FONT:' in alert.image %}
|
||||
<div class="font-icon fa {{ alert.image|replace({'FONT:': ''}) }} fa-4x"></div>
|
||||
{% else %}
|
||||
<img src="{{ alert.image }}" alt="Notification" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="notif-hist-content">
|
||||
<div class="notif-hist-inside">
|
||||
<div class="notif-hist-title">
|
||||
{{ alert.title }}
|
||||
</div>
|
||||
<div class="notif-hist-text">
|
||||
{{ alert.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="notif-hist-time">
|
||||
<time datetime="{{ alert.time|date('r') }}">{{ alert.time|date(config('date_format')) }}</time>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if alerts|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any notifications in your history!</h1>
|
||||
{% endif %}
|
3
templates/yuuno/settings/advanced/master.twig
Normal file
3
templates/yuuno/settings/advanced/master.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% extends 'settings/master.twig' %}
|
||||
|
||||
{% set category = 'Advanced' %}
|
51
templates/yuuno/settings/advanced/sessions.twig
Normal file
51
templates/yuuno/settings/advanced/sessions.twig
Normal file
|
@ -0,0 +1,51 @@
|
|||
{% extends 'settings/advanced/master.twig' %}
|
||||
|
||||
{% set mode = 'Home' %}
|
||||
|
||||
{% block description %}
|
||||
<p>Welcome to the Settings Panel! From here you can monitor, view and update your profile and preferences.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block settingsContent %}
|
||||
<table class="settings-table">
|
||||
<thead>
|
||||
<tr><th style="width: 100px;">IP</th><th>Useragent</th><th style="width: 120px;">Login time</th><th></th></tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr><th>IP</th><th>Useragent</th><th>Login time</th><th></th></tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
{% for s in sessions %}
|
||||
<tr {% if s.session_key == session.sessionId %} class="current-session"{% endif %}>
|
||||
<td>
|
||||
{{ s.user_ip }}
|
||||
</td>
|
||||
<td>
|
||||
{{ s.user_agent }}
|
||||
</td>
|
||||
<td>
|
||||
<time datetime="{{ s.session_start|date('r') }}">{{ s.session_start|date(config('date_format')) }}</time>
|
||||
</td>
|
||||
<td style="width: 90px;">
|
||||
<form method="post" action="/settings.php?cat=advanced&mode=sessions">
|
||||
<input type="hidden" name="sessid" value="{{ session_id() }}" />
|
||||
<input type="hidden" name="timestamp" value="{{ date().timestamp }}" />
|
||||
<input type="hidden" name="sessionid" value="{{ s.session_id }}" />
|
||||
<input type="hidden" name="mode" value="sessions" />
|
||||
<button class="inputStyling small" name="submit">Kill</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="profile-save">
|
||||
<form method="post" action="/settings.php?cat=advanced&mode=sessions">
|
||||
<input type="hidden" name="sessid" value="{{ session_id() }}" />
|
||||
<input type="hidden" name="timestamp" value="{{ date().timestamp }}" />
|
||||
<input type="hidden" name="sessionid" value="all" />
|
||||
<input type="hidden" name="mode" value="sessions" />
|
||||
<button class="inputStyling" name="submit">Kill all active sessions</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
39
templates/yuuno/settings/friends/listing.twig
Normal file
39
templates/yuuno/settings/friends/listing.twig
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends 'settings/general/master.twig' %}
|
||||
|
||||
{% set friends = user.friends(1)|batch(12) %}
|
||||
|
||||
{% set paginationPages = friends %}
|
||||
{% set paginationUrl %}{{ route('settings.friends.listing') }}{% endset %}
|
||||
|
||||
{% set mode = 'Listing' %}
|
||||
|
||||
{% block description %}
|
||||
<p>Manage your friends.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block settingsContent %}
|
||||
{% if friends|length %}
|
||||
<div class="friends-list">
|
||||
{% for friend in friends[get.page|default(1) - 1] %}
|
||||
<div class="friend-container" id="friendslist-friend-{{ friend.id }}">
|
||||
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
|
||||
<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>
|
||||
</a>
|
||||
<div class="friends-list-actions">
|
||||
<a class="remove fill fa fa-remove" title="Remove friend" href="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
{% if friends|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any friends yet!</h1>
|
||||
{% endif %}
|
||||
{% endblock %}
|
11
templates/yuuno/settings/friends/master.twig
Normal file
11
templates/yuuno/settings/friends/master.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% extends 'settings/master.twig' %}
|
||||
|
||||
{% set category = 'Friends' %}
|
||||
|
||||
{% block css %}
|
||||
<style type="text/css">
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
40
templates/yuuno/settings/friends/requests.twig
Normal file
40
templates/yuuno/settings/friends/requests.twig
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends 'settings/general/master.twig' %}
|
||||
|
||||
{% set friends = user.friends(-1)|batch(12) %}
|
||||
|
||||
{% set paginationPages = friends %}
|
||||
{% set paginationUrl %}{{ route('settings.friends.requests') }}{% endset %}
|
||||
|
||||
{% set mode = 'Requests' %}
|
||||
|
||||
{% block description %}
|
||||
<p>Handle friend requests.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block settingsContent %}
|
||||
{% if friends|length %}
|
||||
<div class="friends-list">
|
||||
{% for friend in friends[get.page|default(1) - 1] %}
|
||||
<div class="friend-container" id="friend-{{ friend.id }}">
|
||||
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
|
||||
<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>
|
||||
</a>
|
||||
<div class="friends-list-actions">
|
||||
<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="javascript:void(0);" onclick="removeFriend({{ friend.id }});"></a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
{% if friends|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any pending requests!</h1>
|
||||
{% endif %}
|
||||
{% endblock %}
|
33
templates/yuuno/settings/general/options.twig
Normal file
33
templates/yuuno/settings/general/options.twig
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% extends 'settings/general/master.twig' %}
|
||||
|
||||
{% set mode = 'Options' %}
|
||||
|
||||
{% block description %}
|
||||
<p>These are a few personalisation options for the site while you're logged in.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block settingsContent %}
|
||||
{% if fields %}
|
||||
<form enctype="multipart/form-data" method="post" action="{{ route('settings.general.options') }}">
|
||||
{% for field in fields %}
|
||||
<div class="profile-field">
|
||||
<div>
|
||||
<h2>{{ field.name }}</h2>
|
||||
<div style="font-size: .8em; line-height: 110%;">
|
||||
{{ field.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 8px 0;">
|
||||
<input type="{{ field.type }}" name="option_{{ field.id }}" class="inputStyling"{% if user.optionFields[field.id] %}{% if field.type == 'checkbox' and user.optionFields[field.id] %} checked="checked" value="option_{{ field.id }}"{% else %} value="{{ user.optionFields[field.id] }}"{% endif %}{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="profile-save">
|
||||
<button name="session" value="{{ session_id() }}" class="inputStyling">Save</button>
|
||||
<button type="reset" class="inputStyling">Reset</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">There are currently no changeable options.</h1>
|
||||
{% endif %}
|
||||
{% endblock %}
|
76
templates/yuuno/settings/general/profile.twig
Normal file
76
templates/yuuno/settings/general/profile.twig
Normal file
|
@ -0,0 +1,76 @@
|
|||
{% extends 'settings/general/master.twig' %}
|
||||
|
||||
{% set mode = 'Profile' %}
|
||||
|
||||
{% block description %}
|
||||
<p>These are the external account links etc. on your profile, shouldn't need any additional explanation for this one.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% set months = {
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
} %}
|
||||
|
||||
{% set birthday = user.birthday|split('-') %}
|
||||
|
||||
{% block settingsContent %}
|
||||
<form enctype="multipart/form-data" method="post" action="{{ route('settings.general.profile') }}">
|
||||
{% for field in fields %}
|
||||
<div class="profile-field" id="{{ field.id }}">
|
||||
<div>
|
||||
<h2>{{ field.name }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<input type="{{ field.type }}" name="profile_{{ field.id }}" class="inputStyling" placeholder="{{ field.description }}"{% if user.profileFields[field.id].value %}{% if field.type == 'checkbox' and user.profileFields[field.id].value == 'true' %} checked="checked" value="profile_{{ field.id }}"{% else %} value="{{ user.profileFields[field.id].value }}"{% endif %}{% endif %} />
|
||||
</div>
|
||||
{% if field.additional %}
|
||||
{% for id,addit in field.additional %}
|
||||
<div>
|
||||
<input type="{{ addit[0] }}" id="{{ id }}" name="profile_additional_{{ id }}"{% if user.profileFields[field.id][id] %}{% if addit[0] == 'checkbox' and user.profileFields[field.id][id] == true %} checked="checked"{% else %} value="{{ user.profileFields[field.id][id] }}"{% endif %}{% endif %} />
|
||||
<label for="{{ id }}" style="font-size: 10px;">{{ addit[1]|raw }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="profile-field birthday">
|
||||
<div>
|
||||
<h2>Birthday</h2>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
Day: <select name="birthday_day">
|
||||
<option value="0"{% if not birthday[2] %} selected="selected"{% endif %}>--</option>
|
||||
{% for i in 1..31 %}
|
||||
<option value="{{ i }}"{% if birthday[2] == i %} selected="selected"{% endif %}>{{ i }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
Month: <select name="birthday_month">
|
||||
<option value="0"{% if not birthday[1] %} selected="selected"{% endif %}>--</option>
|
||||
{% for i in 1..12 %}
|
||||
<option value="{{ i }}"{% if birthday[1] == i %} selected="selected"{% endif %}>{{ months[i] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
Year: <select name="birthday_year">
|
||||
<option value="0"{% if not birthday[0] %} selected="selected"{% endif %}>----</option>
|
||||
{% for i in "now"|date('Y')..("now"|date('Y') - 100) %}
|
||||
<option value="{{ i }}"{% if birthday[0] == i %} selected="selected"{% endif %}>{{ i }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-save">
|
||||
<button name="session" value="{{ session_id() }}" class="inputStyling">Save</button>
|
||||
<button type="reset" class="inputStyling">Reset</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
59
templates/yuuno/settings/notifications/history.twig
Normal file
59
templates/yuuno/settings/notifications/history.twig
Normal file
|
@ -0,0 +1,59 @@
|
|||
{% extends 'settings/notifications/master.twig' %}
|
||||
|
||||
{% set mode = 'History' %}
|
||||
|
||||
{% block description %}
|
||||
<p>The history of notifications that have been sent to you.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% set alerts = user.notifications(0, false)|batch(10) %}
|
||||
|
||||
{% set paginationPages = alerts %}
|
||||
{% set paginationUrl %}{{ route('settings.notifications.history') }}{% endset %}
|
||||
|
||||
{% block css %}
|
||||
<style type="text/css">
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block settingsContent %}
|
||||
{% if alerts %}
|
||||
<div class="notification-history">
|
||||
{% for alert in alerts[get.page|default(1) - 1] %}
|
||||
<a id="notif-hist-{{ alert.id }}" class="clean {% if alert.read %}read{% endif %}"{% if alert.link %} href="{{ alert.link }}"{% endif %}>
|
||||
<div class="notif-hist-icon">
|
||||
{% if 'FONT:' in alert.image %}
|
||||
<div class="font-icon fa {{ alert.image|replace({'FONT:': ''}) }} fa-4x"></div>
|
||||
{% else %}
|
||||
<img src="{{ alert.image }}" alt="Notification" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="notif-hist-content">
|
||||
<div class="notif-hist-inside">
|
||||
<div class="notif-hist-title">
|
||||
{{ alert.title }}
|
||||
</div>
|
||||
<div class="notif-hist-text">
|
||||
{{ alert.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="notif-hist-time">
|
||||
<time datetime="{{ alert.time|date('r') }}">{{ alert.time|date(config('date_format')) }}</time>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if alerts|length > 1 %}
|
||||
<div>
|
||||
{% include 'elements/pagination.twig' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h1 class="stylised" style="margin: 2em auto; text-align: center;">You don't have any notifications in your history!</h1>
|
||||
{% endif %}
|
||||
{% endblock %}
|
3
templates/yuuno/settings/notifications/master.twig
Normal file
3
templates/yuuno/settings/notifications/master.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% extends 'settings/master.twig' %}
|
||||
|
||||
{% set category = 'Notifications' %}
|
|
@ -115,7 +115,7 @@
|
|||
{% 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 %}
|
||||
<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="{{ route('user.report', profile.id) }}"></a>
|
||||
{% endif %}
|
||||
{% if user.permission(constant('Sakura\\Perms\\Manage::CAN_RESTRICT_USERS'), constant('Sakura\\Perms::MANAGE')) %}
|
||||
<a class="fa fa-trash" title="Restrict {{ profile.username }}" href="?restrict={{ session_id() }}"></a>
|
||||
|
|
5
templates/yuuno/user/report.twig
Normal file
5
templates/yuuno/user/report.twig
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends 'global/master.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="stylised" style="text-align: center; margin: 2em auto;">I'll actually make reporting a thing, someday...</h1>
|
||||
{% endblock %}
|
278
utility.php
Normal file
278
utility.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
/*
|
||||
* A set of utility helper functions
|
||||
*/
|
||||
|
||||
use Sakura\Config;
|
||||
use Sakura\Net;
|
||||
|
||||
function clean_string($string, $lower = false, $noSpecial = false, $replaceSpecial = '')
|
||||
{
|
||||
// Run common sanitisation function over string
|
||||
$string = htmlentities($string, ENT_NOQUOTES | ENT_HTML401, Config::get('charset'));
|
||||
$string = stripslashes($string);
|
||||
$string = strip_tags($string);
|
||||
|
||||
// If set also make the string lowercase
|
||||
if ($lower) {
|
||||
$string = strtolower($string);
|
||||
}
|
||||
|
||||
// If set remove all characters that aren't a-z or 0-9
|
||||
if ($noSpecial) {
|
||||
$string = preg_replace('/[^a-z0-9]/', $replaceSpecial, $string);
|
||||
}
|
||||
|
||||
// Return clean string
|
||||
return $string;
|
||||
}
|
||||
|
||||
function check_mx_record($email)
|
||||
{
|
||||
// Get the domain from the e-mail address
|
||||
$domain = substr(strstr($email, '@'), 1);
|
||||
|
||||
// Check the MX record
|
||||
$record = checkdnsrr($domain, 'MX');
|
||||
|
||||
// Return the record data
|
||||
return $record;
|
||||
}
|
||||
|
||||
function get_country_code()
|
||||
{
|
||||
// Attempt to get country code using PHP's built in geo thing
|
||||
if (function_exists("geoip_country_code_by_name")) {
|
||||
try {
|
||||
$code = geoip_country_code_by_name(Net::ip());
|
||||
|
||||
// Check if $code is anything
|
||||
if ($code) {
|
||||
return $code;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the required header is set and return it
|
||||
if (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
|
||||
return $_SERVER['HTTP_CF_IPCOUNTRY'];
|
||||
}
|
||||
|
||||
// Return XX as a fallback
|
||||
return 'XX';
|
||||
}
|
||||
|
||||
function get_country_name($code)
|
||||
{
|
||||
// Catch XX
|
||||
if (strtolower($code) === 'xx') {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
// Catch proxy
|
||||
if (strtolower($code) === 'a1') {
|
||||
return 'Anonymous Proxy';
|
||||
}
|
||||
|
||||
return locale_get_display_region("-{$code}", 'en');
|
||||
}
|
||||
|
||||
function password_entropy($password)
|
||||
{
|
||||
// Decode utf-8 chars
|
||||
$password = utf8_decode($password);
|
||||
|
||||
// Count the amount of unique characters in the password string and calculate the entropy
|
||||
return count(count_chars($password, 1)) * log(256, 2);
|
||||
}
|
||||
|
||||
function byte_symbol($bytes)
|
||||
{
|
||||
// Return nothing if the input was 0
|
||||
if (!$bytes) {
|
||||
return "0 B";
|
||||
}
|
||||
|
||||
// Array with byte symbols
|
||||
$symbols = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
|
||||
// Calculate byte entity
|
||||
$exp = floor(log($bytes) / log(1024));
|
||||
|
||||
// Format the things
|
||||
$bytes = sprintf("%.2f " . $symbols[$exp], ($bytes / pow(1024, floor($exp))));
|
||||
|
||||
// Return the formatted string
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
function send_mail($to, $subject, $body)
|
||||
{
|
||||
// Initialise PHPMailer
|
||||
$mail = new PHPMailer;
|
||||
|
||||
// Set to SMTP
|
||||
$mail->isSMTP();
|
||||
|
||||
// Set the SMTP server host
|
||||
$mail->Host = Config::get('smtp_server');
|
||||
|
||||
// Do we require authentication?
|
||||
$mail->SMTPAuth = Config::get('smtp_auth');
|
||||
|
||||
// Do we encrypt as well?
|
||||
$mail->SMTPSecure = Config::get('smtp_secure');
|
||||
|
||||
// Set the port to the SMTP server
|
||||
$mail->Port = Config::get('smtp_port');
|
||||
|
||||
// If authentication is required log in as well
|
||||
if (Config::get('smtp_auth')) {
|
||||
$mail->Username = Config::get('smtp_username');
|
||||
$mail->Password = base64_decode(Config::get('smtp_password'));
|
||||
}
|
||||
|
||||
// Add a reply-to header
|
||||
$mail->addReplyTo(Config::get('smtp_replyto_mail'), Config::get('smtp_replyto_name'));
|
||||
|
||||
// Set a from address as well
|
||||
$mail->setFrom(Config::get('smtp_from_email'), Config::get('smtp_from_name'));
|
||||
|
||||
// Set the addressee
|
||||
foreach ($to as $email => $name) {
|
||||
$mail->addBCC($email, $name);
|
||||
}
|
||||
|
||||
// Subject line
|
||||
$mail->Subject = $subject;
|
||||
|
||||
// Set body
|
||||
$mail->Body = $body;
|
||||
|
||||
// Send the message
|
||||
$send = $mail->send();
|
||||
|
||||
// Clear the addressee list
|
||||
$mail->clearAddresses();
|
||||
|
||||
// If we got an error return the error
|
||||
if (!$send) {
|
||||
return $mail->ErrorInfo;
|
||||
}
|
||||
|
||||
// Else just return whatever
|
||||
return $send;
|
||||
}
|
||||
|
||||
function error_handler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
// Remove ROOT path from the error string and file location
|
||||
$errstr = str_replace(ROOT, '', $errstr);
|
||||
$errfile = str_replace(ROOT, '', $errfile);
|
||||
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
case E_USER_ERROR:
|
||||
$error = '<b>FATAL ERROR</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
$error = '<b>WARNING</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
case E_NOTICE:
|
||||
case E_USER_NOTICE:
|
||||
$error = '<b>NOTICE</b>: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
|
||||
break;
|
||||
|
||||
default:
|
||||
$error = '<b>Unknown error type</b> [' . $errno . ']: ' . $errstr . ' on line ' . $errline
|
||||
. ' in ' . $errfile;
|
||||
}
|
||||
|
||||
// Truncate all previous outputs
|
||||
ob_clean();
|
||||
ob_end_clean();
|
||||
|
||||
// Check for dev mode
|
||||
$detailed = Config::local('dev', 'show_errors');
|
||||
|
||||
// Build page
|
||||
$errorPage = '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Sakura Internal Error</title>
|
||||
<style type="text/css">
|
||||
body { margin: 0; padding: 0; background: #EEE; color: #000;
|
||||
font: 12px/20px Verdana, Arial, Helvetica, sans-serif; }
|
||||
h1, h2 { font-weight: 100; background: #CAA; padding: 8px 5px 10px;
|
||||
margin: 0; font-style: italic; font-family: serif; }
|
||||
h1 { border-radius: 8px 8px 0 0; }
|
||||
h2 { margin: 0 -10px; }
|
||||
.container { border: 1px solid #CAA; margin: 10px auto; background: #FFF;
|
||||
box-shadow: 2px 2px 1em #888; max-width: 1024px; border-radius: 10px; }
|
||||
.container .inner { padding: 0 10px; }
|
||||
.container .inner .error { background: #555; color: #EEE; border-left: 5px solid #C22;
|
||||
padding: 4px 6px; text-shadow: 0 1px 1px #888; white-space: pre-wrap;
|
||||
word-wrap: break-word; margin: 12px 0; border-radius: 5px; box-shadow: inset 0 0 1em #333; }
|
||||
.container .footer { border-top: 1px solid #CAA; font-size: x-small; padding: 0 5px 1px; }
|
||||
a { color: #77E; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
a:active { color: #E77; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>An error occurred while executing the script.</h1>
|
||||
<div class="inner">
|
||||
<p>To prevent potential security risks or data loss Sakura has stopped execution of the script.</p>';
|
||||
|
||||
if (isset($errid)) {
|
||||
$errorPage .= '<p>The error and surrounding data has been logged.</p>
|
||||
<h2>' . (!$detailed ? 'Report the following text to a staff member' : 'Logged as') . '</h2>
|
||||
<pre class="error">' . $errid . '</pre>';
|
||||
} else {
|
||||
$errorPage .= '<p>Sakura was not able to log this error which could mean that there was an error
|
||||
with the database connection. If you\'re the system administrator check the database credentials
|
||||
and make sure the server is running and if you\'re not please let the system administrator
|
||||
know about this error if it occurs again.</p>';
|
||||
}
|
||||
|
||||
if ($detailed) {
|
||||
$errorPage .= ' <h2>Summary</h2>
|
||||
<pre class="error">' . $error . '</pre>
|
||||
<h2>Backtraces</h2>';
|
||||
|
||||
foreach (debug_backtrace() as $num => $trace) {
|
||||
$errorPage .= '<h3>#' . $num . '</h3><pre class="error">';
|
||||
|
||||
foreach ($trace as $key => $val) {
|
||||
$errorPage .=
|
||||
str_pad(
|
||||
'[' . $key . ']',
|
||||
12
|
||||
) . '=> ' . (
|
||||
is_array($val) || is_object($val) ?
|
||||
json_encode($val) :
|
||||
$val
|
||||
) . "\r\n";
|
||||
}
|
||||
|
||||
$errorPage .= '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
$errorPage .= '</div>
|
||||
<div class="footer">
|
||||
Sakura r' . SAKURA_VERSION . '.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
// Die and display error message
|
||||
die($errorPage);
|
||||
}
|
Reference in a new issue