premium stuff

This commit is contained in:
flash 2016-03-27 23:18:57 +02:00
parent 9280706a0a
commit 6da8eb931d
24 changed files with 541 additions and 505 deletions

View file

@ -6,14 +6,6 @@
// Declare Namespace // Declare Namespace
namespace Sakura; namespace Sakura;
// Check if the script isn't executed by root
if (function_exists('posix_getuid')) {
if (posix_getuid() === 0) {
trigger_error('Running cron as root is disallowed for security reasons.', E_USER_ERROR);
exit;
}
}
// Define that this page won't require templating // Define that this page won't require templating
define('SAKURA_NO_TPL', true); define('SAKURA_NO_TPL', true);
@ -35,9 +27,13 @@ DB::table('notifications')
// Get expired premium accounts // Get expired premium accounts
$expiredPremium = DB::table('premium') $expiredPremium = DB::table('premium')
->where('premium_expire', '<', time()) ->where('premium_expire', '<', time())
->get(); ->get(['user_id']);
// Process expired premium accounts, make this not stupid in the future foreach ($expiredPremium as $premium) {
foreach ($expiredPremium as $expired) { DB::table('premium')
Users::updatePremiumMeta($expired->user_id); ->where('user_id', $premium->user_id)
->delete();
User::construct($premium->user_id)
->isPremium();
} }

View file

@ -139,7 +139,7 @@ class BBcode
$parsed = nl2br(self::$bbcode->getAsHtml()); $parsed = nl2br(self::$bbcode->getAsHtml());
$parsed = Utils::fixCodeTags($parsed); $parsed = self::fixCodeTags($parsed);
$parsed = self::parseEmoticons($parsed); $parsed = self::parseEmoticons($parsed);
return $parsed; return $parsed;
@ -178,4 +178,37 @@ class BBcode
return self::$bbcode->getAsText(); return self::$bbcode->getAsText();
} }
/**
* Clean up the contents of <code> tags.
*
* @param string $text Dirty
*
* @return string Clean
*/
public static function fixCodeTags($text)
{
$parts = explode('<code>', $text);
$newStr = '';
if (count($parts) > 1) {
foreach ($parts as $p) {
$parts2 = explode('</code>', $p);
if (count($parts2) > 1) {
$code = str_replace('<br />', '', $parts2[0]);
$code = str_replace('<br/>', '', $code);
$code = str_replace('<br>', '', $code);
$code = str_replace('<', '&lt;', $code);
$newStr .= '<code>' . $code . '</code>';
$newStr .= $parts2[1];
} else {
$newStr .= $p;
}
}
} else {
$newStr = $text;
}
return $newStr;
}
} }

View file

@ -28,17 +28,28 @@ use Sakura\Utils;
*/ */
class AuthController extends Controller class AuthController extends Controller
{ {
protected function touchRateLimit($user, $mode = 0) /**
* Touch the login rate limit.
*
* @param $user int The ID of the user that attempted to log in.
* @param $sucess bool Whether the login attempt was successful.
*/
protected function touchRateLimit($user, $success = false)
{ {
DB::table('login_attempts') DB::table('login_attempts')
->insert([ ->insert([
'attempt_success' => $mode, 'attempt_success' => $success ? 1 : 0,
'attempt_timestamp' => time(), 'attempt_timestamp' => time(),
'attempt_ip' => Net::pton(Net::IP()), 'attempt_ip' => Net::pton(Net::ip()),
'user_id' => $user, 'user_id' => $user,
]); ]);
} }
/**
* End the current session.
*
* @return string
*/
public function logout() public function logout()
{ {
// Check if user is logged in // Check if user is logged in
@ -65,11 +76,21 @@ class AuthController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Get the login page.
*
* @return string
*/
public function loginGet() public function loginGet()
{ {
return Template::render('auth/login'); return Template::render('auth/login');
} }
/**
* Do a login attempt.
*
* @return string
*/
public function loginPost() public function loginPost()
{ {
// Preliminarily set login to failed // Preliminarily set login to failed
@ -91,7 +112,7 @@ class AuthController extends Controller
// Check if we haven't hit the rate limit // Check if we haven't hit the rate limit
$rates = DB::table('login_attempts') $rates = DB::table('login_attempts')
->where('attempt_ip', Net::pton(Net::IP())) ->where('attempt_ip', Net::pton(Net::ip()))
->where('attempt_timestamp', '>', time() - 1800) ->where('attempt_timestamp', '>', time() - 1800)
->where('attempt_success', '0') ->where('attempt_success', '0')
->count(); ->count();
@ -172,7 +193,7 @@ class AuthController extends Controller
Config::get('cookie_path') Config::get('cookie_path')
); );
$this->touchRateLimit($user->id, 1); $this->touchRateLimit($user->id, true);
$success = 1; $success = 1;
@ -189,12 +210,17 @@ class AuthController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Get the registration page.
*
* @return string
*/
public function registerGet() public function registerGet()
{ {
// Attempt to check if a user has already registered from the current IP // Attempt to check if a user has already registered from the current IP
$getUserIP = DB::table('users') $getUserIP = DB::table('users')
->where('register_ip', Net::pton(Net::IP())) ->where('register_ip', Net::pton(Net::ip()))
->orWhere('last_ip', Net::pton(Net::IP())) ->orWhere('last_ip', Net::pton(Net::ip()))
->get(); ->get();
if ($getUserIP) { if ($getUserIP) {
@ -207,6 +233,11 @@ class AuthController extends Controller
return Template::render('auth/register'); return Template::render('auth/register');
} }
/**
* Do a registration attempt.
*
* @return string
*/
public function registerPost() public function registerPost()
{ {
// Preliminarily set registration to failed // Preliminarily set registration to failed
@ -366,6 +397,11 @@ class AuthController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Do a activation attempt.
*
* @return string
*/
public function activate() public function activate()
{ {
// Preliminarily set activation to failed // Preliminarily set activation to failed
@ -426,11 +462,21 @@ class AuthController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Get the reactivation request form.
*
* @return string
*/
public function reactivateGet() public function reactivateGet()
{ {
return Template::render('auth/reactivate'); return Template::render('auth/reactivate');
} }
/**
* Do a reactivation preparation attempt.
*
* @return string
*/
public function reactivatePost() public function reactivatePost()
{ {
// Preliminarily set registration to failed // Preliminarily set registration to failed
@ -498,11 +544,21 @@ class AuthController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Get the password reset forum.
*
* @return string
*/
public function resetPasswordGet() public function resetPasswordGet()
{ {
return Template::render('auth/resetpassword'); return Template::render('auth/resetpassword');
} }
/**
* Do a password reset attempt.
*
* @return string
*/
public function resetPasswordPost() public function resetPasswordPost()
{ {
// Preliminarily set action to failed // Preliminarily set action to failed

View file

@ -8,9 +8,9 @@
namespace Sakura\Controllers; namespace Sakura\Controllers;
use Sakura\Config; use Sakura\Config;
use Sakura\User;
use Sakura\File; use Sakura\File;
use Sakura\Perms\Site; use Sakura\Perms\Site;
use Sakura\User;
/** /**
* File controller, handles user uploads like avatars. * File controller, handles user uploads like avatars.
@ -20,18 +20,28 @@ use Sakura\Perms\Site;
*/ */
class FileController extends Controller class FileController extends Controller
{ {
private function serveImage($data, $mime, $name) /**
* The base for serving a file.
*
* @return string
*/
private function serve($data, $mime, $name)
{ {
// Add original filename // Add original filename
header('Content-Disposition: inline; filename="' . $name . '"'); header("Content-Disposition: inline; filename={$name}");
// Set content type // Set content type
header('Content-Type: ' . $mime); header("Content-Type: {$mime}");
// Return image data // Return image data
return $data; return $data;
} }
/**
* Attempt to get an avatar.
*
* @return string
*/
public function avatar($id = 0) public function avatar($id = 0)
{ {
global $templateName; global $templateName;
@ -72,26 +82,32 @@ class FileController extends Controller
$user = User::construct($id); $user = User::construct($id);
if ($user->permission(Site::DEACTIVATED)) { if ($user->permission(Site::DEACTIVATED)) {
return $this->serveImage($deactive['data'], $deactive['mime'], $deactive['name']); return $this->serve($deactive['data'], $deactive['mime'], $deactive['name']);
} }
if ($user->checkBan() || $user->permission(Site::RESTRICTED)) { if ($user->checkBan()
return $this->serveImage($banned['data'], $banned['mime'], $banned['name']); || $user->permission(Site::RESTRICTED)) {
return $this->serve($banned['data'], $banned['mime'], $banned['name']);
} }
if (!$user->avatar) { if (!$user->avatar) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$serve = new File($user->avatar); $serve = new File($user->avatar);
if (!$serve->id) { if (!$serve->id) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
return $this->serveImage($serve->data, $serve->mime, $serve->name); return $this->serve($serve->data, $serve->mime, $serve->name);
} }
/**
* Attempt to get a background.
*
* @return string
*/
public function background($id = 0) public function background($id = 0)
{ {
global $templateName; global $templateName;
@ -104,24 +120,32 @@ class FileController extends Controller
]; ];
if (!$id) { if (!$id) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$user = User::construct($id); $user = User::construct($id);
if ($user->permission(Site::DEACTIVATED) || $user->checkBan() || $user->permission(Site::RESTRICTED) || !$user->background) { if ($user->permission(Site::DEACTIVATED)
return $this->serveImage($none['data'], $none['mime'], $none['name']); || $user->checkBan()
|| $user->permission(Site::RESTRICTED)
|| !$user->background) {
return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$serve = new File($user->background); $serve = new File($user->background);
if (!$serve->id) { if (!$serve->id) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
return $this->serveImage($serve->data, $serve->mime, $serve->name); return $this->serve($serve->data, $serve->mime, $serve->name);
} }
/**
* Attempt to get a profile header.
*
* @return string
*/
public function header($id = 0) public function header($id = 0)
{ {
global $templateName; global $templateName;
@ -134,21 +158,24 @@ class FileController extends Controller
]; ];
if (!$id) { if (!$id) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$user = User::construct($id); $user = User::construct($id);
if ($user->permission(Site::DEACTIVATED) || $user->checkBan() || $user->permission(Site::RESTRICTED) || !$user->header) { if ($user->permission(Site::DEACTIVATED)
return $this->serveImage($none['data'], $none['mime'], $none['name']); || $user->checkBan()
|| $user->permission(Site::RESTRICTED)
|| !$user->header) {
return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$serve = new File($user->header); $serve = new File($user->header);
if (!$serve->id) { if (!$serve->id) {
return $this->serveImage($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
return $this->serveImage($serve->data, $serve->mime, $serve->name); return $this->serve($serve->data, $serve->mime, $serve->name);
} }
} }

View file

@ -29,7 +29,7 @@ class ForumController extends Controller
/** /**
* Serves the forum index. * Serves the forum index.
* *
* @return mixed HTML for the forum index. * @return string HTML for the forum index.
*/ */
public function index() public function index()
{ {
@ -119,6 +119,11 @@ class ForumController extends Controller
return Template::render('forum/index'); return Template::render('forum/index');
} }
/**
* Get a forum page.
*
* @return string
*/
public function forum($id = 0) public function forum($id = 0)
{ {
global $currentUser; global $currentUser;
@ -182,6 +187,11 @@ class ForumController extends Controller
return Template::render('forum/viewforum'); return Template::render('forum/viewforum');
} }
/**
* Mark a forum as read.
*
* @return string
*/
public function markForumRead($id = 0) public function markForumRead($id = 0)
{ {
global $currentUser; global $currentUser;
@ -246,6 +256,11 @@ class ForumController extends Controller
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* View a thread.
*
* @return string
*/
public function thread($id = 0) public function thread($id = 0)
{ {
global $currentUser; global $currentUser;
@ -283,6 +298,11 @@ class ForumController extends Controller
return Template::render('forum/viewtopic'); return Template::render('forum/viewtopic');
} }
/**
* Moderate a thread.
*
* @return string
*/
public function threadModerate($id = 0) public function threadModerate($id = 0)
{ {
global $currentUser; global $currentUser;
@ -409,14 +429,17 @@ class ForumController extends Controller
} }
// Set the variables // Set the variables
Template::vars([ Template::vars(compact('message', 'redirect'));
'page' => compact('message', 'redirect'),
]);
// Print page contents // Print page contents
return Template::render('global/information'); return Template::render('global/information');
} }
/**
* Redirect to the position of a post in a thread.
*
* @return mixed
*/
public function post($id = 0) public function post($id = 0)
{ {
global $currentUser; global $currentUser;
@ -462,6 +485,11 @@ class ForumController extends Controller
return header("Location: {$threadLink}#p{$post->id}"); return header("Location: {$threadLink}#p{$post->id}");
} }
/**
* Get the raw text of a post.
*
* @return string
*/
public function postRaw($id = 0) public function postRaw($id = 0)
{ {
global $currentUser; global $currentUser;
@ -485,6 +513,11 @@ class ForumController extends Controller
return $post->text; return $post->text;
} }
/**
* Reply to a thread.
*
* @return string
*/
public function threadReply($id = 0) public function threadReply($id = 0)
{ {
global $currentUser; global $currentUser;
@ -571,6 +604,11 @@ class ForumController extends Controller
return header("Location: {$postLink}"); return header("Location: {$postLink}");
} }
/**
* Create a thread.
*
* @return string
*/
public function createThread($id = 0) public function createThread($id = 0)
{ {
global $currentUser; global $currentUser;
@ -664,6 +702,11 @@ class ForumController extends Controller
return Template::render('forum/viewtopic'); return Template::render('forum/viewtopic');
} }
/**
* Edit a post.
*
* @return string
*/
public function editPost($id = 0) public function editPost($id = 0)
{ {
global $currentUser; global $currentUser;
@ -781,6 +824,11 @@ class ForumController extends Controller
return header("Location: {$postLink}"); return header("Location: {$postLink}");
} }
/**
* Delete a post.
*
* @return string
*/
public function deletePost($id = 0) public function deletePost($id = 0)
{ {
global $currentUser; global $currentUser;

View file

@ -17,9 +17,14 @@ use Sakura\BBcode;
*/ */
class HelperController extends Controller class HelperController extends Controller
{ {
/**
* Parsed BBcode from a post request
*
* @return string The parsed BBcode
*/
public function bbcodeParse() public function bbcodeParse()
{ {
$text = isset($_POST['text']) ? $_POST['text'] : null; $text = isset($_POST['text']) ? $_POST['text'] : '';
$text = BBcode::toHTML($text); $text = BBcode::toHTML($text);

View file

@ -12,7 +12,6 @@ use Sakura\DB;
use Sakura\News; use Sakura\News;
use Sakura\Template; use Sakura\Template;
use Sakura\User; use Sakura\User;
use Sakura\Users;
/** /**
* Meta page controllers (sections that aren't big enough to warrant a dedicated controller). * Meta page controllers (sections that aren't big enough to warrant a dedicated controller).
@ -29,6 +28,38 @@ class MetaController extends Controller
*/ */
public function index() public function index()
{ {
// Get the newest user
$newestUserId = DB::table('users')
->where('rank_main', '!=', Config::get('restricted_rank_id'))
->where('rank_main', '!=', Config::get('deactive_rank_id'))
->orderBy('user_id', 'desc')
->limit(1)
->get(['user_id']);
$newestUser = User::construct($newestUserId ? $newestUserId[0]->user_id : 0);
// Get all the currently online users
$timeRange = time() - Config::get('max_online_time');
// Create a storage variable
$onlineUsers = [];
// Get all online users
$getOnline = DB::table('users')
->where('user_last_online', '>', $timeRange)
->get(['user_id']);
$getOnline = array_column($getOnline, 'user_id');
foreach ($getOnline as $user) {
$user = User::construct($user);
// Do a second check
if (!$user->isOnline()) {
continue;
}
$onlineUsers[$user->id] = $user;
}
// Merge index specific stuff with the global render data // Merge index specific stuff with the global render data
Template::vars([ Template::vars([
'news' => new News(Config::get('site_news_category')), 'news' => new News(Config::get('site_news_category')),
@ -38,14 +69,14 @@ class MetaController extends Controller
->where('password_algo', '!=', 'disabled') ->where('password_algo', '!=', 'disabled')
->whereNotIn('rank_main', [1, 10]) ->whereNotIn('rank_main', [1, 10])
->count(), ->count(),
'newestUser' => User::construct(Users::getNewestUserId()), 'newestUser' => $newestUser,
'lastRegDate' => date_diff( 'lastRegDate' => date_diff(
date_create(date('Y-m-d', User::construct(Users::getNewestUserId())->registered)), date_create(date('Y-m-d', $newestUser->registered)),
date_create(date('Y-m-d')) date_create(date('Y-m-d'))
)->format('%a'), )->format('%a'),
'topicCount' => DB::table('topics')->count(), 'topicCount' => DB::table('topics')->count(),
'postCount' => DB::table('posts')->count(), 'postCount' => DB::table('posts')->count(),
'onlineUsers' => Users::checkAllOnline(), 'onlineUsers' => $onlineUsers,
], ],
]); ]);
@ -153,14 +184,6 @@ class MetaController extends Controller
*/ */
public function search() public function search()
{ {
// Set parse variables
Template::vars([
'page' => [
'title' => 'Search',
],
]);
// Print page contents
return Template::render('main/search'); return Template::render('main/search');
} }
} }

View file

@ -9,12 +9,10 @@ namespace Sakura\Controllers;
use Exception; use Exception;
use Sakura\Config; use Sakura\Config;
use Sakura\Template;
use Sakura\User;
use Sakura\Users;
use Sakura\Utils;
use Sakura\Payments; use Sakura\Payments;
use Sakura\Perms\Site; use Sakura\Perms\Site;
use Sakura\Router;
use Sakura\Template;
/** /**
* Premium pages controller. * Premium pages controller.
@ -24,144 +22,164 @@ use Sakura\Perms\Site;
*/ */
class PremiumController extends Controller class PremiumController extends Controller
{ {
/**
* The amount of premium a user received per period.
*/
const PERIOD_PER_PAYMENT = 2628000;
/**
* Constructor.
*/
public function __construct()
{
Payments::init();
}
/**
* Returns the premium purchase index.
*
* @return mixed
*/
public function index() public function index()
{ {
global $currentUser, $urls; global $currentUser;
// Switch between modes (we only allow this to be used by logged in user) $price = Config::get('premium_price_per_month');
if (isset($_REQUEST['mode']) $amountLimit = Config::get('premium_amount_max');
&& Users::checkLogin()
&& $currentUser->permission(Site::OBTAIN_PREMIUM)) {
// Initialise Payments class
if (!Payments::init()) {
header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
} else {
switch ($_REQUEST['mode']) {
// Create the purchase
case 'purchase':
// Compare time and session so we know the link isn't forged
if (!isset($_REQUEST['time'])
|| $_REQUEST['time'] < time() - 1000) {
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
}
// Match session ids for the same reason Template::vars(compact('price', 'amountLimit'));
if (!isset($_REQUEST['session'])
|| $_REQUEST['session'] != session_id()) {
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
}
// Half if shit isn't gucci
if (!isset($_POST['months'])
|| !is_numeric($_POST['months'])
|| (int) $_POST['months'] < 1
|| (int) $_POST['months'] > Config::get('premium_amount_max')) {
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
} else {
// Calculate the total
$total = (float) Config::get('premium_price_per_month') * (int) $_POST['months'];
$total = number_format($total, 2, '.', '');
// Generate item name
$itemName = Config::get('sitename')
. ' Premium - '
. (string) $_POST['months']
. ' month'
. ((int) $_POST['months'] == 1 ? '' : 's');
// Attempt to create a transaction
if ($transaction = Payments::createTransaction(
$total,
$itemName,
Config::get('sitename') . ' Premium Purchase',
'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . Config::get('url_main') . $urls->format('SITE_PREMIUM')
)) {
// Store the amount of months in the global session array
$_SESSION['premiumMonths'] = (int) $_POST['months'];
return header('Location: ' . $transaction);
} else {
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
}
}
// Finalising the purchase
case 'finish':
// Check if the success GET request is set and is true
if (isset($_GET['success'])
&& isset($_GET['paymentId'])
&& isset($_GET['PayerID'])
&& isset($_SESSION['premiumMonths'])) {
// Attempt to complete the transaction
try {
$finalise = Payments::completeTransaction($_GET['paymentId'], $_GET['PayerID']);
} catch (Exception $e) {
return trigger_error('Something went horribly wrong.', E_USER_ERROR);
}
// Attempt to complete the transaction
if ($finalise) {
// Make the user premium
Users::addUserPremium($currentUser->id, (2628000 * $_SESSION['premiumMonths']));
Users::updatePremiumMeta($currentUser->id);
Utils::updatePremiumTracker(
$currentUser->id,
((float) Config::get('premium_price_per_month') * $_SESSION['premiumMonths']),
$currentUser->username
. ' bought premium for '
. $_SESSION['premiumMonths']
. ' month'
. ($_SESSION['premiumMonths'] == 1 ? '' : 's')
. '.'
);
// Redirect to the complete
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?mode=complete');
}
}
return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true');
case 'complete':
// Set parse variables
Template::vars([
'page' => [
'expiration' => ($prem = $currentUser->isPremium()[2]) !== null ? $prem : 0,
],
]);
// Print page contents
return Template::render('main/premiumcomplete');
default:
return header('Location: ' . $urls->format('SITE_PREMIUM'));
}
}
}
// Set parse variables
Template::vars([
'page' => [
'fail' => isset($_GET['fail']),
'price' => Config::get('premium_price_per_month'),
'current' => $currentUser->isPremium(),
'amount_max' => Config::get('premium_amount_max'),
],
]);
// Print page contents
return Template::render('main/support'); return Template::render('main/support');
} }
public function tracker() /**
* Handles a purchase request.
*
* @return mixed
*/
public function purchase()
{ {
// Set parse variables global $currentUser;
Template::vars([
'tracker' => Utils::getPremiumTrackerData(),
]);
// Print page contents // Get values from post
return Template::render('main/supporttracker'); $session = isset($_POST['session']) ? $_POST['session'] : '';
$months = isset($_POST['months']) ? $_POST['months'] : 0;
// Check if the session is valid
if ($session !== session_id()
|| $currentUser->permission(Site::DEACTIVATED)
|| !$currentUser->permission(Site::OBTAIN_PREMIUM)) {
$message = "You are not allowed to get premium!";
$redirect = Router::route('premium.index');
Template::vars(compact('message', 'redirect'));
return Template::render('global/information');
}
// Fetch the limit
$amountLimit = Config::get('premium_amount_max');
// Check months
if ($months < 1
|| $months > $amountLimit) {
$message = "An incorrect amount of months was specified, stop messing with the source.";
$redirect = Router::route('premium.index');
Template::vars(compact('message', 'redirect'));
return Template::render('global/information');
}
$pricePerMonth = Config::get('premium_price_per_month');
$total = number_format($pricePerMonth * $months, 2, '.', '');
$siteName = Config::get('sitename');
$multiMonths = $months !== 1 ? 's' : '';
$siteUrl = 'http'
. (isset($_SERVER['HTTPS']) ? 's' : '')
. "://{$_SERVER['SERVER_NAME']}"
. ($_SERVER['SERVER_PORT'] != 80 ? ":{$_SERVER['SERVER_PORT']}" : '');
$handlerRoute = Router::route('premium.handle');
$itemName = "{$siteName} Premium - {$months} month{$multiMonths}";
$transactionName = "{$siteName} premium purchase";
$handlerUrl = "{$siteUrl}{$handlerRoute}";
// Create the transaction
$transaction = Payments::createTransaction(
$total,
$itemName,
$transactionName,
$handlerUrl
);
// Attempt to create a transaction
if (!$transaction) {
$message = "Something went wrong while preparing the transaction.";
$redirect = Router::route('premium.index');
Template::vars(compact('message', 'redirect'));
return Template::render('global/information');
}
// Store the amount of months in the global session array
$_SESSION['premiumMonths'] = (int) $months;
return header("Location: {$transaction}");
}
/**
* Handles the data returned by PayPal.
*
* @return mixed
*/
public function handle()
{
global $currentUser;
$success = isset($_GET['success']);
$payment = isset($_GET['paymentId']) ? $_GET['paymentId'] : null;
$payer = isset($_GET['PayerID']) ? $_GET['PayerID'] : null;
$months = isset($_SESSION['premiumMonths']) ? $_SESSION['premiumMonths'] : null;
$successRoute = Router::route('premium.complete');
$failRoute = Router::route('premium.index') . "?fail=true";
if (!$success
|| !$payment
|| !$payer
|| !$months) {
return header("Location: {$failRoute}");
}
// Attempt to complete the transaction
try {
$finalise = Payments::completeTransaction($_GET['paymentId'], $_GET['PayerID']);
} catch (Exception $e) {
$finalise = false;
}
if (!$finalise) {
return header("Location: {$failRoute}");
}
$pricePerMonth = Config::get('premium_price_per_month');
$currentUser->addPremium(self::PERIOD_PER_PAYMENT * $months);
return header("Location: {$successRoute}");
}
/**
* Presents the user with a thank you <3.
*
* @return mixed
*/
public function complete()
{
return Template::render('main/premiumcomplete');
} }
} }

View file

@ -49,46 +49,18 @@ class UserController extends Controller
// Redirect if so // Redirect if so
if ($check) { if ($check) {
Template::vars([ $message = "This user changed their username! Redirecting you to their new profile.";
'page' => [ $redirect = Router::route('user.profile', $check[0]->user_id);
'message' => 'The user this profile belongs to changed their username, you are being redirected.',
'redirect' => Router::route('user.profile', $check[0]->user_id), Template::vars(compact('message', 'redirect'));
],
]);
// Print page contents // Print page contents
return Template::render('global/information'); return Template::render('global/information');
} }
} }
// Check if we're trying to restrict
if (isset($_GET['restrict']) && $_GET['restrict'] == session_id() && $currentUser->permission(\Sakura\Perms\Manage::CAN_RESTRICT_USERS, \Sakura\Perms::MANAGE)) {
// Check restricted status
$restricted = $profile->permission(\Sakura\Perms\Site::RESTRICTED);
if ($restricted) {
$profile->removeRanks([Config::get('restricted_rank_id')]);
$profile->addRanks([2]);
} else {
$profile->addRanks([Config::get('restricted_rank_id')]);
$profile->removeRanks(array_keys($profile->ranks));
}
Template::vars([
'page' => [
'message' => 'Toggled the restricted status of the user.',
'redirect' => Router::route('user.profile', $profile->id),
],
]);
// Print page contents
return Template::render('global/information');
}
// Set parse variables // Set parse variables
Template::vars([ Template::vars(compact('profile'));
'profile' => $profile,
]);
// Print page contents // Print page contents
return Template::render('main/profile'); return Template::render('main/profile');
@ -128,17 +100,21 @@ class UserController extends Controller
// Get the active rank // Get the active rank
$rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : 2); $rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : 2);
// Get members per page
$membersPerPage = Config::get('members_per_page');
// Set parse variables // Set parse variables
Template::vars([ Template::vars(compact('ranks', 'rank', 'membersPerPage'));
'ranks' => $ranks,
'rank' => $rank,
'membersPerPage' => Config::get('members_per_page'),
]);
// Render the template // Render the template
return Template::render('main/memberlist'); return Template::render('main/memberlist');
} }
/**
* Get the notification JSON object for the currently authenticated user.
*
* @return string The JSON object.
*/
public function notifications() public function notifications()
{ {
// TODO: add friend on/offline messages // TODO: add friend on/offline messages
@ -153,6 +129,13 @@ class UserController extends Controller
); );
} }
/**
* Mark a notification as read.
*
* @param int The ID of the notification.
*
* @return string Not entirely set on this one yet but 1 for success and 0 for fail.
*/
public function markNotification($id = 0) public function markNotification($id = 0)
{ {
global $currentUser; global $currentUser;

View file

@ -168,7 +168,7 @@ class Post
'topic_id' => $thread->id, 'topic_id' => $thread->id,
'forum_id' => $thread->forum, 'forum_id' => $thread->forum,
'poster_id' => $poster->id, 'poster_id' => $poster->id,
'poster_ip' => Net::IP(), 'poster_ip' => Net::ip(),
'post_time' => time(), 'post_time' => time(),
'post_subject' => $subject, 'post_subject' => $subject,
'post_text' => $text, 'post_text' => $text,
@ -206,7 +206,7 @@ class Post
'topic_id' => $thread->id, 'topic_id' => $thread->id,
'forum_id' => $thread->forum, 'forum_id' => $thread->forum,
'poster_id' => $this->poster->id, 'poster_id' => $this->poster->id,
'poster_ip' => Net::pton(Net::IP()), 'poster_ip' => Net::pton(Net::ip()),
'post_time' => $this->time, 'post_time' => $this->time,
'post_subject' => $this->subject, 'post_subject' => $this->subject,
'post_text' => $this->text, 'post_text' => $this->text,

View file

@ -20,7 +20,7 @@ class Net
* *
* @return string The IP. * @return string The IP.
*/ */
public static function IP() public static function ip()
{ {
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '::1'; return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '::1';
} }
@ -163,7 +163,7 @@ class Net
{ {
// Generate an address from the mask // Generate an address from the mask
$addr = str_repeat("f", $mask / 4); $addr = str_repeat("f", $mask / 4);
// Append uneven bit // Append uneven bit
switch ($mask % 4) { switch ($mask % 4) {
case 1: case 1:
@ -178,10 +178,10 @@ class Net
$addr .= 'e'; $addr .= 'e';
break; break;
} }
// Pad the address with zeroes // Pad the address with zeroes
$addr = str_pad($addr, 32, '0'); $addr = str_pad($addr, 32, '0');
// Pack the address // Pack the address
$addr = pack('H*', $addr); $addr = pack('H*', $addr);

View file

@ -98,7 +98,7 @@ class Session
DB::table('sessions') DB::table('sessions')
->insert([ ->insert([
'user_id' => $this->userId, 'user_id' => $this->userId,
'user_ip' => Net::pton(Net::IP()), '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' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'),
'session_key' => $session, 'session_key' => $session,
'session_start' => time(), 'session_start' => time(),
@ -141,7 +141,7 @@ class Session
if ($ipCheck) { if ($ipCheck) {
// Split both IPs up // Split both IPs up
$sessionIP = explode('.', $session[0]->user_ip); $sessionIP = explode('.', $session[0]->user_ip);
$userIP = explode('.', Net::IP()); $userIP = explode('.', Net::ip());
// Take 1 off the ipCheck variable so it's equal to the array keys // Take 1 off the ipCheck variable so it's equal to the array keys
$ipCheck = $ipCheck - 1; $ipCheck = $ipCheck - 1;

View file

@ -9,6 +9,7 @@ namespace Sakura;
use Sakura\Perms; use Sakura\Perms;
use Sakura\Perms\Site; use Sakura\Perms\Site;
use stdClass;
/** /**
* Everything you'd ever need from a specific user. * Everything you'd ever need from a specific user.
@ -268,8 +269,8 @@ class User
'password_iter' => $password[1], 'password_iter' => $password[1],
'email' => $emailClean, 'email' => $emailClean,
'rank_main' => 0, 'rank_main' => 0,
'register_ip' => Net::pton(Net::IP()), 'register_ip' => Net::pton(Net::ip()),
'last_ip' => Net::pton(Net::IP()), 'last_ip' => Net::pton(Net::ip()),
'user_registered' => time(), 'user_registered' => time(),
'user_last_online' => 0, 'user_last_online' => 0,
'user_country' => Utils::getCountryCode(), 'user_country' => Utils::getCountryCode(),
@ -913,37 +914,99 @@ class User
} }
/** /**
* Does this user have premium? * Add premium in seconds.
* *
* @return array Premium status information. * @param int $seconds The amount of seconds.
*
* @return int The new expiry date.
*/ */
public function isPremium() public function addPremium($seconds)
{ {
// Check if there's already a record of premium for this user in the database
// Check if the user has static premium $getUser = DB::table('premium')
if ($this->permission(Site::STATIC_PREMIUM)) {
return [2, 0, time() + 1];
}
// Attempt to retrieve the premium record from the database
$getRecord = DB::table('premium')
->where('user_id', $this->id) ->where('user_id', $this->id)
->get(); ->get();
// If nothing was returned just return false // Calculate the (new) start and expiration timestamp
if (empty($getRecord)) { $start = $getUser ? $getUser[0]->premium_start : time();
return [0]; $expire = $getUser ? $getUser[0]->premium_expire + $seconds : time() + $seconds;
// If the user already exists do an update call, otherwise an insert call
if ($getUser) {
DB::table('premium')
->where('user_id', $this->id)
->update([
'premium_expire' => $expire,
]);
} else {
DB::table('premium')
->insert([
'user_id' => $this->id,
'premium_start' => $start,
'premium_expire' => $expire,
]);
} }
$getRecord = $getRecord[0]; // Return the expiration timestamp
return $expire;
}
// Check if the Tenshi hasn't expired /**
if ($getRecord->premium_expire < time()) { * Does this user have premium?
return [0, $getRecord->premium_start, $getRecord->premium_expire]; *
* @return int Returns the premium expiration date.
*/
public function isPremium()
{
// Get rank IDs from the db
$premiumRank = (int) Config::get('premium_rank_id');
$defaultRank = (int) Config::get('default_rank_id');
// Fetch expiration date
$expire = $this->premiumInfo()->expire;
// Check if the user has static premium
if (!$expire
&& $this->permission(Site::STATIC_PREMIUM)) {
$expire = time() + 1;
} }
// Else return the start and expiration date // Check if the user has premium and isn't in the premium rank
return [1, $getRecord->premium_start, $getRecord->premium_expire]; if ($expire
&& !$this->hasRanks([$premiumRank])) {
// Add the premium rank
$this->addRanks([$premiumRank]);
// Set it as default
if ($this->mainRankId == $defaultRank) {
$this->setMainRank($premiumRank);
}
} elseif (!$expire
&& $this->hasRanks([$premiumRank])) {
$this->removeRanks([$premiumRank]);
if ($this->mainRankId == $premiumRank) {
$this->setMainRank($defaultRank);
}
}
return $expire;
}
public function premiumInfo()
{
// Attempt to retrieve the premium record from the database
$check = DB::table('premium')
->where('user_id', $this->id)
->where('premium_expire', '>', time())
->get();
$return = new stdClass;
$return->start = $check ? $check[0]->premium_start : 0;
$return->expire = $check ? $check[0]->premium_expire : 0;
return $return;
} }
/** /**

View file

@ -47,22 +47,6 @@ class Users
// Check if the session exists and check if the user is activated // Check if the session exists and check if the user is activated
if ($sessionValid == 0 || $user->permission(Site::DEACTIVATED)) { if ($sessionValid == 0 || $user->permission(Site::DEACTIVATED)) {
// Unset User ID
setcookie(
Config::get('cookie_prefix') . 'id',
0,
time() - 60,
Config::get('cookie_path')
);
// Unset Session ID
setcookie(
Config::get('cookie_prefix') . 'session',
'',
time() - 60,
Config::get('cookie_path')
);
return false; return false;
} }
@ -92,9 +76,6 @@ class Users
'user_last_online' => time(), 'user_last_online' => time(),
]); ]);
// Update the premium meta
self::updatePremiumMeta($uid);
// If everything went through return true // If everything went through return true
return [$uid, $sid]; return [$uid, $sid];
} }
@ -250,120 +231,4 @@ class Users
// Return the yeahs // Return the yeahs
return $fields; return $fields;
} }
/**
* Get all online users.
*
* @return array Array containing User instances.
*/
public static function checkAllOnline()
{
// Assign time - 500 to a variable
$time = time() - Config::get('max_online_time');
$return = [];
// Get all online users in the past 5 minutes
$getAll = DB::table('users')
->where('user_last_online', '>', $time)
->get();
foreach ($getAll as $user) {
$return[] = User::construct($user->user_id);
}
// Return all the online users
return $return;
}
/**
* Add premium time to a user.
*
* @param int $id The user ID.
* @param int $seconds The amount of extra seconds.
*
* @return array|double|int The new expiry date.
*/
public static function addUserPremium($id, $seconds)
{
// Check if there's already a record of premium for this user in the database
$getUser = DB::table('premium')
->where('user_id', $id)
->count();
// Calculate the (new) start and expiration timestamp
$start = isset($getUser['premium_start']) ? $getUser['premium_start'] : time();
$expire = isset($getUser['premium_expire']) ? $getUser['premium_expire'] + $seconds : time() + $seconds;
// If the user already exists do an update call, otherwise an insert call
if (empty($getUser)) {
DB::table('premium')
->insert([
'user_id' => $id,
'premium_start' => $start,
'premium_expire' => $expire,
]);
} else {
DB::table('premium')
->where('user_id', $id)
->update('premium_expire', $expire);
}
// Return the expiration timestamp
return $expire;
}
/**
* Process premium meta data.
*
* @param int $id The user ID.
*/
public static function updatePremiumMeta($id)
{
// Get the ID for the premium user rank from the database
$premiumRank = Config::get('premium_rank_id');
$excepted = Config::get('restricted_rank_id');
// Create user object
$user = User::construct($id);
// Run the check
$check = $user->isPremium();
// Check if the user has premium
if ($check[0] && !array_key_exists($excepted, $user->ranks)) {
// If so add the rank to them
$user->addRanks([$premiumRank]);
// Check if the user's default rank is standard user and update it to premium
if ($user->mainRankId == 2) {
$user->setMainRank($premiumRank);
}
} elseif (!$check[0]) {
// Remove the expired entry
DB::table('premium')
->where('user_id', $user->id)
->delete();
// Else remove the rank from them
$user->removeRanks([$premiumRank]);
}
}
/**
* Get the newest member's ID.
*
* @return int The user ID.
*/
public static function getNewestUserId()
{
$get = DB::table('users')
->where('rank_main', '!=', Config::get('restricted_rank_id'))
->where('rank_main', '!=', Config::get('deactive_rank_id'))
->orderBy('user_id', 'desc')
->limit(1)
->get(['user_id']);
return $get ? $get[0]->user_id : 0;
}
} }

View file

@ -263,7 +263,7 @@ class Utils
// Attempt to get country code using PHP's built in geo thing // Attempt to get country code using PHP's built in geo thing
if (function_exists("geoip_country_code_by_name")) { if (function_exists("geoip_country_code_by_name")) {
try { try {
$code = geoip_country_code_by_name(Net::IP()); $code = geoip_country_code_by_name(Net::ip());
// Check if $code is anything // Check if $code is anything
if ($code) { if ($code) {
@ -346,94 +346,4 @@ class Utils
// Return the formatted string // Return the formatted string
return $bytes; return $bytes;
} }
/**
* Get the premium tracker data.
*
* @return array The premium tracker data.
*/
public static function getPremiumTrackerData()
{
// Create data array
$data = [];
// Get database stuff
$table = DB::table('premium_log')
->orderBy('transaction_id', 'desc')
->get();
// Add raw table data to data array
$data['table'] = $table;
// Create balance entry
$data['balance'] = 0.0;
// users
$data['users'] = [];
// Calculate the thing
foreach ($table as $row) {
// Calculate balance
$data['balance'] = $data['balance'] + $row->transaction_amount;
// Add userdata to table
if (!array_key_exists($row->user_id, $data['users'])) {
$data['users'][$row->user_id] = User::construct($row->user_id);
}
}
// Return the data
return $data;
}
/**
* Add a new entry to the tracker.
*
* @param int $id The user ID.
* @param float $amount The amount of money.
* @param string $comment A little information.
*/
public static function updatePremiumTracker($id, $amount, $comment)
{
DB::table('premium_log')
->insert([
'user_id' => $id,
'transaction_amount' => $amount,
'transaction_date' => time(),
'transaction_comment' => $comment,
]);
}
/**
* Clean up the contents of <code> tags.
*
* @param string $text Dirty
*
* @return string Clean
*/
public static function fixCodeTags($text)
{
$parts = explode('<code>', $text);
$newStr = '';
if (count($parts) > 1) {
foreach ($parts as $p) {
$parts2 = explode('</code>', $p);
if (count($parts2) > 1) {
$code = str_replace('<br />', '', $parts2[0]);
$code = str_replace('<br/>', '', $code);
$code = str_replace('<br>', '', $code);
$code = str_replace('<', '&lt;', $code);
$newStr .= '<code>' . $code . '</code>';
$newStr .= $parts2[1];
} else {
$newStr .= $p;
}
}
} else {
$newStr = $text;
}
return $newStr;
}
} }

View file

@ -106,9 +106,11 @@ Router::get('/a/{id}', 'FileController@avatar', 'file.avatar');
Router::get('/bg/{id}', 'FileController@background', 'file.background'); Router::get('/bg/{id}', 'FileController@background', 'file.background');
// Premium // Premium
Router::group(['prefix' => 'support'], function () { Router::group(['prefix' => 'support', 'before' => 'loginCheck'], function () {
Router::get('/', 'PremiumController@index', 'premium.index'); Router::get('/', 'PremiumController@index', 'premium.index');
Router::get('/tracker', 'PremiumController@tracker', 'premium.tracker'); Router::get('/handle', 'PremiumController@handle', 'premium.handle');
Router::get('/complete', 'PremiumController@complete', 'premium.complete');
Router::post('/purchase', 'PremiumController@purchase', 'premium.purchase');
}); });
// Helpers // Helpers

View file

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

View file

@ -51,7 +51,7 @@
<div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('file.avatar', activePoster.id) }}); box-shadow: 0 0 5px #{% if activePoster.isOnline %}484{% else %}844{% endif %};"></div> <div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('file.avatar', activePoster.id) }}); box-shadow: 0 0 5px #{% if activePoster.isOnline %}484{% else %}844{% endif %};"></div>
<div class="user-container-info"> <div class="user-container-info">
<h1 style="color: {{ activePoster.colour }}; text-shadow: 0 0 7px {% if activePoster.colour != 'inherit' %}{{ activePoster.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if activePoster.getUsernameHistory %} title="Known as {{ activePoster.getUsernameHistory[0].username_old }} before {{ activePoster.getUsernameHistory[0].change_time|date(sakura.dateFormat) }}." {% endif %}>{{ activePoster.username }}</h1> <h1 style="color: {{ activePoster.colour }}; text-shadow: 0 0 7px {% if activePoster.colour != 'inherit' %}{{ activePoster.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if activePoster.getUsernameHistory %} title="Known as {{ activePoster.getUsernameHistory[0].username_old }} before {{ activePoster.getUsernameHistory[0].change_time|date(sakura.dateFormat) }}." {% endif %}>{{ activePoster.username }}</h1>
{% if activePoster.isPremium[0] %}<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;" /> {% endif %}<img src="{{ sakura.contentPath }}/images/flags/{{ activePoster.country|lower }}.png" alt="{{ activePoster.country }}" style="vertical-align: middle;" title="{{ activePoster.country(true) }}" /> <span style="font-size: .8em;">{{ activePoster.title }}</span> {% if activePoster.isPremium %}<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;" /> {% endif %}<img src="{{ sakura.contentPath }}/images/flags/{{ activePoster.country|lower }}.png" alt="{{ activePoster.country }}" style="vertical-align: middle;" title="{{ activePoster.country(true) }}" /> <span style="font-size: .8em;">{{ activePoster.title }}</span>
</div> </div>
</div> </div>
</a> </a>

View file

@ -86,7 +86,7 @@
{% endif %} {% endif %}
<div class="userdata"> <div class="userdata">
<div class="usertitle">{{ post.poster.title }}</div> <div class="usertitle">{{ post.poster.title }}</div>
<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi"{% if not post.poster.isPremium[0] %} style="opacity: 0;"{% endif %} /> <img src="{{ sakura.contentPath }}/images/flags/{{ post.poster.country|lower }}.png" alt="{{ post.poster.country(true) }}" />{% if post.poster.id == (thread.posts|first).poster.id %} <img src="{{ sakura.contentPath }}/images/op.png" alt="OP" title="Original Poster" />{% endif %} <img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi"{% if not post.poster.isPremium %} style="opacity: 0;"{% endif %} /> <img src="{{ sakura.contentPath }}/images/flags/{{ post.poster.country|lower }}.png" alt="{{ post.poster.country(true) }}" />{% if post.poster.id == (thread.posts|first).poster.id %} <img src="{{ sakura.contentPath }}/images/op.png" alt="OP" title="Original Poster" />{% endif %}
{% if session.checkLogin %} {% if session.checkLogin %}
<div class="actions"> <div class="actions">
{% if (user.id == post.poster.id and forum.permission(constant('Sakura\\Perms\\Forum::EDIT_OWN'), user.id)) or forum.permission(constant('Sakura\\Perms\\Forum::EDIT_ANY'), user.id) %} {% if (user.id == post.poster.id and forum.permission(constant('Sakura\\Perms\\Forum::EDIT_OWN'), user.id)) or forum.permission(constant('Sakura\\Perms\\Forum::EDIT_ANY'), user.id) %}
@ -141,7 +141,7 @@
<img src="{{ route('file.avatar', user.id) }}" alt="{{ user.username }}" class="avatar" style="box-shadow: 0 3px 7px #484;" /> <img src="{{ route('file.avatar', user.id) }}" alt="{{ user.username }}" class="avatar" style="box-shadow: 0 3px 7px #484;" />
<div class="userdata"> <div class="userdata">
<div class="usertitle">{{ user.title }}</div> <div class="usertitle">{{ user.title }}</div>
<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi"{% if not user.isPremium[0] %} style="opacity: 0;"{% endif %} /> <img src="{{ sakura.contentPath }}/images/flags/{{ user.country|lower }}.png" alt="{{ user.country(true) }}" />{% if user.id == (thread.posts|first).poster.id %} <img src="{{ sakura.contentPath }}/images/op.png" alt="OP" title="Original Poster" />{% endif %} <img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi"{% if not user.isPremium %} style="opacity: 0;"{% endif %} /> <img src="{{ sakura.contentPath }}/images/flags/{{ user.country|lower }}.png" alt="{{ user.country(true) }}" />{% if user.id == (thread.posts|first).poster.id %} <img src="{{ sakura.contentPath }}/images/op.png" alt="OP" title="Original Poster" />{% endif %}
</div> </div>
</td> </td>
<td class="post-content"> <td class="post-content">

View file

@ -7,8 +7,8 @@
<div> <div>
<h1>Information</h1> <h1>Information</h1>
<hr class="default" /> <hr class="default" />
{{ page.message }} {{ message }}
{% if page.redirect %}<br /><a href="{{ page.redirect }}" class="default">Click here if you aren't being redirected.</a>{% endif %} {% if redirect %}<br /><a href="{{ redirect }}" class="default">Click here if you aren't being redirected.</a>{% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -10,8 +10,14 @@
<meta name="msapplication-TileColor" content="#9475b2" /> <meta name="msapplication-TileColor" content="#9475b2" />
<meta name="msapplication-TileImage" content="/content/images/icons/ms-icon-144x144.png" /> <meta name="msapplication-TileImage" content="/content/images/icons/ms-icon-144x144.png" />
<meta name="theme-color" content="#9475B2" /> <meta name="theme-color" content="#9475B2" />
{% if page.redirect %}
<meta http-equiv="refresh" content="{{ page.redirectTimeout ? page.redirectTimeout : '3' }}; URL={{ page.redirect }}" /> {# want to start moving away from page.etc but older files are a thing #}
{% if message is not defined %}{% set message = page.message %}{% endif %}
{% if redirect is not defined %}{% set redirect = page.redirect %}{% endif %}
{% if redirectTimeout is not defined %}{% set redirectTimeout = page.redirectTimeout %}{% endif %}
{% if redirect %}
<meta http-equiv="refresh" content="{{ redirectTimeout ? redirectTimeout : '3' }}; URL={{ redirect }}" />
{% endif %} {% endif %}
<link rel="apple-touch-icon" sizes="57x57" href="/content/images/icons/apple-icon-57x57.png" /> <link rel="apple-touch-icon" sizes="57x57" href="/content/images/icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/content/images/icons/apple-icon-60x60.png" /> <link rel="apple-touch-icon" sizes="60x60" href="/content/images/icons/apple-icon-60x60.png" />
@ -63,7 +69,7 @@
notifyUI({ notifyUI({
"title": "An error has occurred!", "title": "An error has occurred!",
"text": "There was a problem while executing the JavaScript code for this page: " + msg + ", URL: " + url + ", Line: " + line + ", Column: " + col + ". Please report this to a developer.", "text": "There was a problem while executing the JavaScript code for this page: " + msg + ", URL: " + url + ", Line: " + line + ", Column: " + col + ". Please report this to a developer.",
"img": "FONT:fa-warning" "image": "FONT:fa-warning"
}); });
} }
</script> </script>

View file

@ -6,6 +6,6 @@
<div class="content standalone" style="text-align: center;"> <div class="content standalone" style="text-align: center;">
<h1 class="stylised" style="margin: 1em auto;">Thank you for your contribution!</h1> <h1 class="stylised" style="margin: 1em auto;">Thank you for your contribution!</h1>
<h1 class="fa fa-heart stylised" style="font-size: 20em;"></h1> <h1 class="fa fa-heart stylised" style="font-size: 20em;"></h1>
<h3>Your Tenshi will expire on {{ page.expiration|date(sakura.dateFormat) }}.</h3> <h3>Your Tenshi will expire on {{ user.isPremium|date(sakura.dateFormat) }}.</h3>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -81,7 +81,7 @@
<div class="default-avatar-setting new-profile-avatar" style="background-image: url({{ route('file.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};"></div> <div class="default-avatar-setting new-profile-avatar" style="background-image: url({{ route('file.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};"></div>
<div class="new-profile-username"> <div class="new-profile-username">
<h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(sakura.dateFormat) }}." {% endif %}>{{ profile.username }}</h1> <h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(sakura.dateFormat) }}." {% endif %}>{{ profile.username }}</h1>
{% if profile.isPremium[0] %}<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;" /> {% endif %}<img src="{{ sakura.contentPath }}/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}" /> <span style="font-size: .8em;">{{ profile.title }}</span> {% if profile.isPremium %}<img src="{{ sakura.contentPath }}/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;" /> {% endif %}<img src="{{ sakura.contentPath }}/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}" /> <span style="font-size: .8em;">{{ profile.title }}</span>
</div> </div>
<div class="new-profile-dates"> <div class="new-profile-dates">
<b>Joined</b> <time>{{ profile.registered|date(sakura.dateFormat) }}</time> <b>Joined</b> <time>{{ profile.registered|date(sakura.dateFormat) }}</time>

View file

@ -2,8 +2,10 @@
{% block title %}Support {{ sakura.siteName }}{% endblock %} {% block title %}Support {{ sakura.siteName }}{% endblock %}
{% set persistentPremium = user.permission(constant('Sakura\\Perms\\Site::STATIC_PREMIUM')) %}
{% block content %} {% block content %}
{% if page.fail %} {% if get.fail %}
<div class="headerNotify"> <div class="headerNotify">
<h1>The payment failed or was cancelled!</h1> <h1>The payment failed or was cancelled!</h1>
<p>Something went wrong while processing the transaction, your PayPal account wasn't charged.</p> <p>Something went wrong while processing the transaction, your PayPal account wasn't charged.</p>
@ -13,15 +15,14 @@
<div class="head">Support {{ sakura.siteName }}</div> <div class="head">Support {{ sakura.siteName }}</div>
<div style="font-size: .9em; margin-bottom: 10px;"> <div style="font-size: .9em; margin-bottom: 10px;">
<p>In order to keep the site, its services and improvements on it going I need money but I'm not that big of a fan of asking for money without giving anything special in return thus Tenshi exists. Tenshi is the name for our supporter rank which gives you access to an extra set of features (which are listed further down on this page). With your help we can keep adding new stuff, get new hardware and keep the site awesome!</p> <p>In order to keep the site, its services and improvements on it going I need money but I'm not that big of a fan of asking for money without giving anything special in return thus Tenshi exists. Tenshi is the name for our supporter rank which gives you access to an extra set of features (which are listed further down on this page). With your help we can keep adding new stuff, get new hardware and keep the site awesome!</p>
<h3><a href="{{ route('premium.tracker') }}" class="default">Ever wonder what happens on the financial side of things? View the donation tracker!</a></h3>
</div> </div>
{% if page.current[0] %} {% if user.isPremium %}
<div class="sectionHeader"> <div class="sectionHeader">
Your current Tenshi tag Your current Tenshi tag
</div> </div>
<div style="margin-bottom: 10px;"> <div style="margin-bottom: 10px;">
<h3>{% if page.current[0] == 2 %}Your rank has persistent Tenshi.{% else %}Your Tenshi tag is valid till {{ page.current[2]|date(sakura.dateFormat) }}.{% endif %}</h3> <h3>{% if persistentPremium %}Your rank has persistent Tenshi.{% else %}Your Tenshi tag is valid till {{ user.premiumInfo.expire|date(sakura.dateFormat) }}.{% endif %}</h3>
<progress value="{{ page.current[0] == 2 ? 100 : (100 - (((php.time - page.current[1]) / (page.current[2] - page.current[1])) * 100)) }}" max="100" style="width: 100%"></progress> <progress value="{{ persistentPremium ? 100 : (100 - (((php.time - user.premiumInfo.start) / (user.premiumInfo.expire - user.premiumInfo.start)) * 100)) }}" max="100" style="width: 100%"></progress>
</div> </div>
{% endif %} {% endif %}
<div class="sectionHeader"> <div class="sectionHeader">
@ -91,7 +92,7 @@
</div> </div>
{% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %} {% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %}
<div class="slider"> <div class="slider">
<input class="inputStyling" type="range" min="1" max="{{ page.amount_max }}" value="1" onchange="document.getElementById('monthsNo').value = this.value; document.getElementById('monthNoBtn').innerHTML = this.value; document.getElementById('monthsTrailingS').innerHTML = (this.value == 1 ? '' : 's'); document.getElementById('totalAmount').innerHTML = (this.value * {{ page.price }}).formatMoney(2);" /> <input class="inputStyling" type="range" min="1" max="{{ amountLimit }}" value="1" onchange="document.getElementById('monthsNo').value = this.value; document.getElementById('monthNoBtn').innerHTML = this.value; document.getElementById('monthsTrailingS').innerHTML = (this.value == 1 ? '' : 's'); document.getElementById('totalAmount').innerHTML = (this.value * {{ price }}).formatMoney(2);" />
</div> </div>
<div class="checkout" style="line-height: 28px;"> <div class="checkout" style="line-height: 28px;">
<div style="float: left;"> <div style="float: left;">
@ -109,14 +110,14 @@
{% endif %} {% endif %}
</div> </div>
{% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %} {% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %}
<form action="{{ route('premium.index') }}" method="post" id="purchaseForm" class="hidden"> <form action="{{ route('premium.purchase') }}" method="post" id="purchaseForm" class="hidden">
<input type="hidden" name="mode" value="purchase" /> <input type="hidden" name="mode" value="purchase" />
<input type="hidden" name="time" value="{{ php.time }}" /> <input type="hidden" name="time" value="{{ php.time }}" />
<input type="hidden" name="session" value="{{ php.sessionid }}" /> <input type="hidden" name="session" value="{{ php.sessionid }}" />
<input type="hidden" name="months" id="monthsNo" value="1" /> <input type="hidden" name="months" id="monthsNo" value="1" />
</form> </form>
<script type="text/javascript"> <script type="text/javascript">
window.onload = function() { document.getElementById('totalAmount').innerHTML = ({{ page.price }}).formatMoney(2); }; window.onload = function() { document.getElementById('totalAmount').innerHTML = ({{ price }}).formatMoney(2); };
</script> </script>
{% endif %} {% endif %}
{% endblock %} {% endblock %}