mirror of
https://github.com/Neetpone/ponepaste.git
synced 2025-03-12 06:30:07 +01:00
feat: add moderator role system
This commit is contained in:
parent
c4d653b13f
commit
799de10f50
14 changed files with 76 additions and 211 deletions
|
@ -5,11 +5,9 @@ return array (
|
|||
array (
|
||||
'title' => 'PonePaste',
|
||||
'description' => 'PonePaste can store green',
|
||||
'baseurl' => 'ponepaste.local/',
|
||||
'keywords' => '',
|
||||
'site_name' => 'PonePaste',
|
||||
'email' => '',
|
||||
'additional_scripts' => '',
|
||||
'email' => ''
|
||||
),
|
||||
'interface' =>
|
||||
array (
|
||||
|
|
|
@ -14,54 +14,44 @@ class AbilityHelper {
|
|||
}
|
||||
|
||||
public function can(string $action, mixed $subject) : bool {
|
||||
if ($this->user && $this->user->admin) {
|
||||
if ($this->user !== null
|
||||
&& $this->user->role == User::ROLE_ADMIN) { // Admins can do anything
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->modelToActions[$subject::class][$action]($this->user, $subject);
|
||||
|
||||
// $is_destructive = in_array($action, self::DESTRUCTIVE_ACTIONS);
|
||||
//
|
||||
// if (is_a($subject, 'PonePaste\\Models\\Paste')) {
|
||||
// if (((int) $subject->visible === Paste::VISIBILITY_PRIVATE) || $is_destructive) {
|
||||
// return $this->user !== null && $subject->user_id === $this->user->id;
|
||||
// }
|
||||
//
|
||||
// if ($subject->is_hidden) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// if (is_a($subject, 'PonePaste\\Models\\User')) {
|
||||
// return !$is_destructive || ($this->user !== null && $subject->id === $this->user->id);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
}
|
||||
|
||||
private function setupAllowedActions() : void {
|
||||
$this->modelToActions['PonePaste\\Models\\Paste'] = [
|
||||
'view' => function(User | null $user, Paste $paste) {
|
||||
return ((int) $paste->visible !== Paste::VISIBILITY_PRIVATE && !$paste->is_hidden) || ($user !== null && $user->id === $paste->user_id);
|
||||
$publicly_visible = ((int) $paste->visible !== Paste::VISIBILITY_PRIVATE) && !$paste->is_hidden;
|
||||
|
||||
return $publicly_visible // Everyone can see public pastes
|
||||
|| ($user !== null && $user->id === $paste->user_id) // Creators of pastes can see their own private pastes
|
||||
|| $user->role >= User::ROLE_MODERATOR; // Moderators and above can see all pastes
|
||||
},
|
||||
'edit' => function(User | null $user, Paste $paste) {
|
||||
return $user !== null && $user->id === $paste->user_id;
|
||||
return $user !== null
|
||||
&& $user->id === $paste->user_id; // Creators of non-anonymous pastes can edit their own pastes
|
||||
},
|
||||
'hide' => function(User | null $user, Paste $paste) {
|
||||
return $user !== null && $user->admin;
|
||||
return $user !== null
|
||||
&& $user->role >= User::ROLE_MODERATOR; // Moderators and above can hide pastes
|
||||
},
|
||||
'delete' => function(User | null $user, Paste $paste) {
|
||||
return $user !== null && $user->id === $paste->user_id;
|
||||
return $user !== null
|
||||
&& ($user->id === $paste->user_id // Creators of pastes can delete their own pastes
|
||||
|| $user->role >= User::ROLE_ADMIN); // Admins can delete all pastes
|
||||
}
|
||||
];
|
||||
$this->modelToActions['PonePaste\\Models\\User'] = [
|
||||
'view' => function(User | null $user, User $subject) {
|
||||
return true;
|
||||
return true; // Everyone can view users
|
||||
},
|
||||
'edit' => function(User | null $user, User $subject) {
|
||||
return $user !== null && $user->id === $subject->id;
|
||||
return $user !== null
|
||||
&& $user->id === $subject->id; // Users can edit their own profiles
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ namespace PonePaste\Models;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class User extends Model {
|
||||
public const ROLE_MODERATOR = 1;
|
||||
public const ROLE_ADMIN = 2;
|
||||
|
||||
protected $table = 'users';
|
||||
protected $fillable = [
|
||||
'username', 'password', 'recovery_code_hash'
|
||||
|
|
|
@ -249,10 +249,8 @@ $site_info = getSiteInfo();
|
|||
$global_site_info = $site_info['site_info'];
|
||||
$row = $site_info['site_info'];
|
||||
$title = trim($row['title']);
|
||||
$baseurl = paste_protocol() . rtrim(trim($row['baseurl']), '/');
|
||||
$site_name = trim($row['site_name']);
|
||||
$email = trim($row['email']);
|
||||
$additional_scripts = trim($row['additional_scripts']);
|
||||
|
||||
// Setup theme
|
||||
$default_theme = 'bulma';
|
||||
|
|
|
@ -147,7 +147,7 @@ function truncate(string $input, int $maxWords, int $maxChars) : string {
|
|||
}
|
||||
|
||||
function embedView($paste_id, $p_title, $content, $title) : bool {
|
||||
global $baseurl;
|
||||
$baseurl = pp_site_url();
|
||||
|
||||
$stats = false;
|
||||
if ($content) {
|
||||
|
@ -238,6 +238,10 @@ function paste_protocol() : string {
|
|||
return !empty($_SERVER['HTTPS']) ? 'https://' : 'http://';
|
||||
}
|
||||
|
||||
function pp_site_url() : string {
|
||||
return paste_protocol() . $_SERVER['HTTP_HOST'];
|
||||
}
|
||||
|
||||
/* get rid of unintended wildcards in a parameter to LIKE queries; not a security issue, just unexpected behaviour. */
|
||||
function escapeLikeQuery(string $query) : string {
|
||||
return str_replace(['\\', '_', '%'], ['\\\\', '\\_', '\\%'], $query);
|
||||
|
|
|
@ -18,7 +18,7 @@ function updateAdminHistory(User $admin, int $action) : void {
|
|||
$log->save();
|
||||
}
|
||||
|
||||
if ($current_user === null || !$current_user->admin) {
|
||||
if ($current_user === null || $current_user->role < User::ROLE_MODERATOR) {
|
||||
header('Location: ..');
|
||||
die();
|
||||
}
|
||||
|
@ -37,4 +37,14 @@ if (isset($_GET['logout'])) {
|
|||
exit();
|
||||
}
|
||||
|
||||
function checkAdminAccess(int $role) {
|
||||
global $current_user;
|
||||
|
||||
if ($current_user === null || $current_user->role < $role) {
|
||||
flashError('You do not have access to this page.');
|
||||
header('Location: /admin/');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
$flashes = getFlashes();
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
<?php
|
||||
|
||||
use PonePaste\Models\AdminLog;
|
||||
use PonePaste\Models\User;
|
||||
|
||||
define('IN_PONEPASTE', 1);
|
||||
require_once('common.php');
|
||||
|
||||
checkAdminAccess(User::ROLE_ADMIN);
|
||||
|
||||
const CONFIG_FILE_PATH = '../../config/site.php';
|
||||
|
||||
function updateConfiguration(string $path, array $new_config) : void {
|
||||
|
@ -33,11 +36,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
$new_site_info = [
|
||||
'title' => trim($data['title']),
|
||||
'description' => trim($data['description']),
|
||||
'baseurl' => trim($data['baseurl']),
|
||||
'keywords' => trim($data['keywords']),
|
||||
'site_name' => trim($data['site_name']),
|
||||
'email' => trim($data['email']),
|
||||
'additional_scripts' => trim($data['additional_scripts'])
|
||||
'email' => trim($data['email'])
|
||||
];
|
||||
|
||||
$current_config['site_info'] = $new_site_info;
|
||||
|
@ -167,22 +168,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label form-label" for="site_info_baseurl">Domain
|
||||
name</label>
|
||||
<div class="col-sm-1" style="padding:5px;">
|
||||
<span class="badge">
|
||||
<?= !empty($_SERVER['HTTPS']) ? 'https://' : 'http://' ?>;
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<input type="text" class="form-control" name="site_info[baseurl]"
|
||||
id="site_info_baseurl"
|
||||
placeholder="eg: ponepaste.org (no trailing slash)"
|
||||
value="<?= pp_html_escape($current_site_info['baseurl']); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label form-label"
|
||||
for="site_info_description">Site Description</label>
|
||||
|
@ -214,18 +199,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label form-label"
|
||||
for="site_info_additional_scripts">Additional Site
|
||||
Scripts</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="additional_scripts"
|
||||
id="site_info_additional_scripts"
|
||||
name="site_info[additional_scripts]"
|
||||
rows="8"><?= pp_html_escape($current_site_info['title']); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="action" value="site_info"/>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -15,7 +15,7 @@ function updateAdminHistory(User $admin, int $action) : void {
|
|||
$log->save();
|
||||
}
|
||||
|
||||
if ($current_user === null || !$current_user->admin) {
|
||||
if ($current_user === null || $current_user->role < User::ROLE_MODERATOR) {
|
||||
header('Location: ..');
|
||||
die();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<?php
|
||||
$menu_options = [
|
||||
['name' => 'Dashboard', 'icon' => 'fa-home', 'path' => '/admin/dashboard.php'],
|
||||
['name' => 'Configuration', 'icon' => 'fa-cogs', 'path' => '/admin/configuration.php'],
|
||||
['name' => 'Admin Password', 'icon' => 'fa-user', 'path' => '/admin/admin.php'],
|
||||
['name' => 'Reports', 'icon' => 'fa-flag', 'path' => '/admin/reports.php'],
|
||||
['name' => 'Pastes', 'icon' => 'fa-clipboard', 'path' => '/admin/pastes.php'],
|
||||
['name' => 'Users', 'icon' => 'fa-users', 'path' => '/admin/users.php']
|
||||
|
||||
use PonePaste\Models\User;
|
||||
|
||||
$menu_options = [
|
||||
['name' => 'Dashboard', 'icon' => 'fa-home', 'path' => '/admin/dashboard.php', 'access' => User::ROLE_MODERATOR],
|
||||
['name' => 'Configuration', 'icon' => 'fa-cogs', 'path' => '/admin/configuration.php', 'access' => User::ROLE_ADMIN],
|
||||
['name' => 'Admin Password', 'icon' => 'fa-user', 'path' => '/admin/admin.php', 'access' => User::ROLE_MODERATOR],
|
||||
['name' => 'Reports', 'icon' => 'fa-flag', 'path' => '/admin/reports.php', 'access' => User::ROLE_MODERATOR],
|
||||
['name' => 'Pastes', 'icon' => 'fa-clipboard', 'path' => '/admin/pastes.php', 'access' => User::ROLE_ADMIN],
|
||||
['name' => 'Users', 'icon' => 'fa-users', 'path' => '/admin/users.php', 'access' => User::ROLE_ADMIN]
|
||||
];
|
||||
$current_path = $_SERVER['PHP_SELF'];
|
||||
?>
|
||||
|
@ -13,12 +16,14 @@
|
|||
<div class="col-md-12">
|
||||
<ul class="panel quick-menu clearfix">
|
||||
<?php foreach ($menu_options as $option): ?>
|
||||
<li class="col-xs-3 col-sm-2 col-md-1 <?= ($option['path'] === $current_path) ? 'menu-active' : '' ?>">
|
||||
<a href="<?= $option['path']; ?>">
|
||||
<i class="fa <?= $option['icon']; ?>"></i>
|
||||
<?= $option['name']; ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($current_user->role >= $option['access']): ?>
|
||||
<li class="col-xs-3 col-sm-2 col-md-1 <?= ($option['path'] === $current_path) ? 'menu-active' : '' ?>">
|
||||
<a href="<?= $option['path']; ?>">
|
||||
<i class="fa <?= $option['icon']; ?>"></i>
|
||||
<?= $option['name']; ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,9 @@ define('IN_PONEPASTE', 1);
|
|||
require_once(__DIR__ . '/common.php');
|
||||
|
||||
use PonePaste\Models\Paste;
|
||||
use PonePaste\Models\User;
|
||||
|
||||
checkAdminAccess(User::ROLE_ADMIN);
|
||||
|
||||
list($per_page, $current_page) = pp_setup_pagination();
|
||||
|
||||
|
|
|
@ -5,14 +5,13 @@ use PonePaste\Models\User;
|
|||
define('IN_PONEPASTE', 1);
|
||||
require_once(__DIR__ . '/common.php');
|
||||
|
||||
checkAdminAccess(User::ROLE_ADMIN);
|
||||
|
||||
list($per_page, $current_page) = pp_setup_pagination();
|
||||
|
||||
$total_users = User::count();
|
||||
$all_users = User::limit($per_page)->offset($current_page * $per_page)->get();
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -24,7 +23,6 @@ $all_users = User::limit($per_page)->offset($current_page * $per_page)->get();
|
|||
<link href="css/datatables.min.css" rel="stylesheet" type="text/css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="top" class="clearfix">
|
||||
<!-- Start App Logo -->
|
||||
<div class="applogo">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/* prevent inclusion of arbitrary files */
|
||||
|
||||
use PonePaste\Models\Report;
|
||||
use PonePaste\Models\User;
|
||||
|
||||
$template_candidates = scandir(__DIR__);
|
||||
if (!in_array($page_template . '.php', $template_candidates)) {
|
||||
|
@ -81,7 +82,7 @@ $flashes = getFlashes();
|
|||
</span>
|
||||
<span>Events</span>
|
||||
</a>
|
||||
<?php if ($current_user !== null && $current_user->admin): ?>
|
||||
<?php if ($current_user !== null && $current_user->role >= User::ROLE_MODERATOR): ?>
|
||||
<?php $has_reports = Report::where(['open' => true])->count() > 0; ?>
|
||||
<a class="button navbar-item mx-2" href="/admin" <?= $has_reports ? 'style="color: red;"' : '' ?>>
|
||||
<span class="icon has-text-info">
|
||||
|
|
|
@ -4,122 +4,6 @@
|
|||
<div class="bd-main-container container">
|
||||
<div class="bd-duo">
|
||||
<div class="bd-lead">
|
||||
<!-- Guests -->
|
||||
<?php if ($site_disable_guests) { // Site permissions
|
||||
?>
|
||||
<section class="hero is-medium">
|
||||
<div class="">
|
||||
<article class="message is-info">
|
||||
<div class="message-header">
|
||||
<p>Site News:</p>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
<div class="content is-normal">
|
||||
<ul>
|
||||
<li>Ponepaste has now a favorite system. You can now favorite pastes and
|
||||
bookmark them on your user page under "Favorites"</li>
|
||||
<li>Report function and UI has been overhauled.</li>
|
||||
<li>The archive page has now been updated, tags are now clickable for a
|
||||
faster search.</li>
|
||||
<li>Tags UI has been overhauled. Tags containing "SAFE" and "NSFW" will
|
||||
appear green and red.</li>
|
||||
<li>When Creating paste the tag input box has been updated with a new visual
|
||||
style.</li>
|
||||
<li>Tags are now being canonized, if you see your tags change, it's just the
|
||||
admin working in the background</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="container">
|
||||
<div class="columns is-multiline is-mobile">
|
||||
<div class="column">
|
||||
<div class="panel-body">
|
||||
<div class="list-widget pagination-content">
|
||||
<?php
|
||||
$res = getRandomPastes($conn, 10);
|
||||
foreach ($res
|
||||
|
||||
as $index => $row) {
|
||||
$title = Trim($row['title']);
|
||||
$titlehov = ($row['title']);
|
||||
$p_member = Trim($row['member']);
|
||||
$p_id = Trim($row['id']);
|
||||
$p_date = Trim($row['date']);
|
||||
$p_time = Trim($row['now_time']);
|
||||
$nowtime = time();
|
||||
$oldtime = $p_time;
|
||||
$title = truncate($title, 24, 60);
|
||||
?>
|
||||
|
||||
<p class="no-margin">
|
||||
<?php
|
||||
if (PP_MOD_REWRITE) {
|
||||
echo '<header class="bd-category-header my-1">
|
||||
<a data-tooltip="' . $titlehov . '" href="' . $p_id . '" title="' . $title . '">' . $title . ' </a>
|
||||
<a class="icon is-pulled-right has-tooltip-arrow has-tooltip-left-mobile has-tooltip-bottom-desktop has-tooltip-left-until-widescreen" data-tooltip="' . $p_time . '">
|
||||
<i class="far fa-clock has-text-grey" aria-hidden="true"></i>
|
||||
</a>
|
||||
<p class="subtitle is-7">' . 'by ' . '
|
||||
<i><a href="https://Ponepaste.org/user/' . $p_member . '">' . $p_member . '</a></i>
|
||||
</p>' .
|
||||
'</header>';
|
||||
} else {
|
||||
echo '<a href="' . $p_id . '" title="' . $titlehov . '">' . ucfirst($title) . '</a>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="panel-body">
|
||||
<div class="list-widget pagination-content">
|
||||
<?php
|
||||
$res = getRandomPastes($conn, 10);
|
||||
foreach ($res
|
||||
|
||||
as $index => $row) {
|
||||
$title = Trim($row['title']);
|
||||
$titlehov = ($row['title']);
|
||||
$p_member = Trim($row['member']);
|
||||
$p_id = Trim($row['id']);
|
||||
$p_date = Trim($row['date']);
|
||||
$p_time = Trim($row['now_time']);
|
||||
$nowtime = time();
|
||||
$oldtime = $p_time;
|
||||
|
||||
$title = truncate($title, 24, 60);
|
||||
?>
|
||||
|
||||
<p class="no-margin">
|
||||
<?php
|
||||
if (PP_MOD_REWRITE) {
|
||||
echo '<header class="bd-category-header my-1">
|
||||
<a data-tooltip="' . $titlehov . '" href="' . $p_id . '" title="' . $title . '">' . $title . ' </a>
|
||||
<a class="icon is-pulled-right has-tooltip-arrow has-tooltip-left-mobile has-tooltip-bottom-desktop has-tooltip-left-until-widescreen" data-tooltip="' . $p_time . '">
|
||||
<i class="far fa-clock has-text-grey" aria-hidden="true"></i>
|
||||
</a>
|
||||
<p class="subtitle is-7">' . 'by ' . '
|
||||
<i><a href="https://Ponepaste.org/user/' . $p_member . '">' . $p_member . '</a></i>
|
||||
</p>' .
|
||||
'</header>';
|
||||
} else {
|
||||
echo '<a href="' . $p_id . '" title="' . $titlehov . '">' . ucfirst($title) . '</a>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php } else { ?>
|
||||
<!-- Paste Panel -->
|
||||
<?php if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if (isset($error)) { ?>
|
||||
|
@ -138,12 +22,11 @@
|
|||
<!-- Title -->
|
||||
<div class="level-item is-pulled-left" style="margin-right: 5px;">
|
||||
<p class="control has-icons-left">
|
||||
<input type="text" class="input" name="title" onchange="getFileName()"
|
||||
placeholder="Title"
|
||||
<input type="text" class="input" name="title" placeholder="Title"
|
||||
value="<?php echo (isset($_POST['title'])) ? pp_html_escape($_POST['title']) : ''; ?>">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fa fa-font"></i>
|
||||
</span>
|
||||
<i class="fa fa-font"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- Format -->
|
||||
|
@ -165,7 +48,7 @@
|
|||
</div>
|
||||
<div class="level-item is-pulled-left mx-1">
|
||||
<a class="button" onclick="highlight(document.getElementById('code')); return false;"><i
|
||||
class="fas fa-indent"></i> Highlight</a>
|
||||
class="fas fa-indent"></i> Highlight</a>
|
||||
</div>
|
||||
<div class="level-item is-pulled-left mx-1">
|
||||
<input class="button is-info" type="submit" name="submit" id="submit" value="Paste"/>
|
||||
|
@ -218,11 +101,11 @@
|
|||
?>
|
||||
<select name="paste_expire_date">
|
||||
|
||||
<?= optionsForSelect(
|
||||
['Never', 'View Once', '10 minutes', '1 hour', '1 day', '1 week', '2 weeks', '1 month'],
|
||||
['N', 'self', '0Y0M0DT0H10M', '1H', '1D', '1W', '2W', '1M'],
|
||||
$post_expire
|
||||
); ?>
|
||||
<?= optionsForSelect(
|
||||
['Never', 'View Once', '10 minutes', '1 hour', '1 day', '1 week', '2 weeks', '1 month'],
|
||||
['N', 'self', '0Y0M0DT0H10M', '1H', '1D', '1W', '2W', '1M'],
|
||||
$post_expire
|
||||
); ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -319,7 +202,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
|
|||
title="Full Screen"></i></a>
|
||||
<div class="panel-embed my-5 is-hidden">
|
||||
<input type="text" class="input has-background-white-ter has-text-grey"
|
||||
value='<?php echo '<script src="' . $protocol . $baseurl;
|
||||
value='<?php echo '<script src="' . pp_site_url() . '/';
|
||||
if (PP_MOD_REWRITE) {
|
||||
echo 'embed/';
|
||||
} else {
|
||||
|
|
Loading…
Add table
Reference in a new issue