feat: reporting, lots of admin improvements

This commit is contained in:
Floorb 2023-05-13 20:05:10 -04:00
parent 708eb9be6f
commit c4d653b13f
20 changed files with 414 additions and 445 deletions

View file

@ -5,31 +5,64 @@ use PonePaste\Models\User;
use PonePaste\Models\Paste; use PonePaste\Models\Paste;
class AbilityHelper { class AbilityHelper {
private const DESTRUCTIVE_ACTIONS = [ private array $modelToActions = [];
'edit', 'delete'
];
private User | null $user; private User | null $user;
public function __construct(User | null $user) { public function __construct(User | null $user) {
$this->user = $user; $this->user = $user;
$this->setupAllowedActions();
} }
public function can(string $action, mixed $subject) : bool { public function can(string $action, mixed $subject) : bool {
$is_destructive = in_array($action, self::DESTRUCTIVE_ACTIONS); if ($this->user && $this->user->admin) {
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;
}
return true; return true;
} }
if (is_a($subject, 'PonePaste\\Models\\User')) { return $this->modelToActions[$subject::class][$action]($this->user, $subject);
return !$is_destructive || ($this->user !== null && $subject->id === $this->user->id);
// $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;
} }
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);
},
'edit' => function(User | null $user, Paste $paste) {
return $user !== null && $user->id === $paste->user_id;
},
'hide' => function(User | null $user, Paste $paste) {
return $user !== null && $user->admin;
},
'delete' => function(User | null $user, Paste $paste) {
return $user !== null && $user->id === $paste->user_id;
}
];
$this->modelToActions['PonePaste\\Models\\User'] = [
'view' => function(User | null $user, User $subject) {
return true;
},
'edit' => function(User | null $user, User $subject) {
return $user !== null && $user->id === $subject->id;
},
];
} }
} }

View file

@ -31,6 +31,10 @@ class Paste extends Model {
return $this->belongsToMany(User::class, 'user_favourites'); return $this->belongsToMany(User::class, 'user_favourites');
} }
public function reports() {
return $this->hasMany(Report::class);
}
public function replaceTags(array $tags) { public function replaceTags(array $tags) {
$this->tags()->detach(); $this->tags()->detach();
@ -63,6 +67,7 @@ class Paste extends Model {
return Paste::with('user') return Paste::with('user')
->orderBy('created_at', 'DESC') ->orderBy('created_at', 'DESC')
->where('visible', self::VISIBILITY_PUBLIC) ->where('visible', self::VISIBILITY_PUBLIC)
->where('is_hidden', false)
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))") ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
->limit($count)->get(); ->limit($count)->get();
} }
@ -71,6 +76,7 @@ class Paste extends Model {
return Paste::with('user') return Paste::with('user')
->orderBy('updated_at', 'DESC') ->orderBy('updated_at', 'DESC')
->where('visible', self::VISIBILITY_PUBLIC) ->where('visible', self::VISIBILITY_PUBLIC)
->where('is_hidden', false)
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))") ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
->limit($count)->get(); ->limit($count)->get();
} }
@ -79,6 +85,7 @@ class Paste extends Model {
return Paste::with('user') return Paste::with('user')
->orderBy('views') ->orderBy('views')
->where('visible', self::VISIBILITY_PUBLIC) ->where('visible', self::VISIBILITY_PUBLIC)
->where('is_hidden', false)
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))") ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
->limit($count)->get(); ->limit($count)->get();
} }
@ -87,6 +94,7 @@ class Paste extends Model {
return Paste::with('user') return Paste::with('user')
->whereRaw('MONTH(created_at) = MONTH(NOW())') ->whereRaw('MONTH(created_at) = MONTH(NOW())')
->where('visible', self::VISIBILITY_PUBLIC) ->where('visible', self::VISIBILITY_PUBLIC)
->where('is_hidden', false)
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))") ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
->orderBy('views') ->orderBy('views')
->limit($count)->get(); ->limit($count)->get();
@ -94,9 +102,10 @@ class Paste extends Model {
public static function getRandom(int $count = 10) : Collection { public static function getRandom(int $count = 10) : Collection {
return Paste::with('user') return Paste::with('user')
->orderByRaw('RAND()')
->where('visible', self::VISIBILITY_PUBLIC) ->where('visible', self::VISIBILITY_PUBLIC)
->where('is_hidden', false)
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))") ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
->orderByRaw('RAND()')
->limit($count)->get(); ->limit($count)->get();
} }
} }

View file

@ -0,0 +1,22 @@
<?php
namespace PonePaste\Models;
use Illuminate\Database\Eloquent\Model;
class Report extends Model {
protected $table = 'reports';
protected $fillable = [
'paste_id',
'user_id',
'reason',
'open'
];
public function user() {
return $this->belongsTo(User::class);
}
public function paste() {
return $this->belongsTo(Paste::class);
}
}

View file

@ -14,7 +14,7 @@ class User extends Model {
} }
public function favourites() { public function favourites() {
return $this->belongsToMany(Paste::class, 'user_favourites')->withPivot('f_time') return $this->belongsToMany(Paste::class, 'user_favourites')->withPivot('created_at')
->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))"); ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))");
} }

View file

@ -41,6 +41,14 @@ function urlForPaste(Paste $paste) : string {
return "/paste.php?id={$paste->id}"; return "/paste.php?id={$paste->id}";
} }
function urlForReport(Paste $paste) : string {
if (PP_MOD_REWRITE) {
return "/{$paste->id}/report";
}
return "/report.php?id={$paste->id}";
}
function urlForMember(User $user) : string { function urlForMember(User $user) : string {
if (PP_MOD_REWRITE) { if (PP_MOD_REWRITE) {
return '/user/' . urlencode($user->username); return '/user/' . urlencode($user->username);
@ -268,6 +276,7 @@ $total_page_views = PageView::select('tpage')->orderBy('id', 'desc')->first()->t
$total_unique_views = PageView::select('tvisit')->orderBy('id', 'desc')->first()->tvisit; $total_unique_views = PageView::select('tvisit')->orderBy('id', 'desc')->first()->tvisit;
$current_user = SessionHelper::currentUser(); $current_user = SessionHelper::currentUser();
$start = microtime(true);
function can(string $action, mixed $subject) : bool { function can(string $action, mixed $subject) : bool {
global $current_user; global $current_user;

View file

@ -37,3 +37,4 @@ if (isset($_GET['logout'])) {
exit(); exit();
} }
$flashes = getFlashes();

View file

@ -20,6 +20,11 @@ if ($current_user === null || !$current_user->admin) {
die(); die();
} }
if (isset($_SESSION['admin_login']) && $_SESSION['admin_login']) {
header('Location: dashboard.php');
exit();
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (pp_password_verify($_POST['password'], $current_user->admin_password_hash)) { if (pp_password_verify($_POST['password'], $current_user->admin_password_hash)) {
updateAdminHistory($current_user, AdminLog::ACTION_LOGIN); updateAdminHistory($current_user, AdminLog::ACTION_LOGIN);
@ -34,7 +39,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
} }
} }
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>

View file

@ -1,36 +1,25 @@
<?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']
];
$current_path = $_SERVER['PHP_SELF'];
?>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<ul class="panel quick-menu clearfix"> <ul class="panel quick-menu clearfix">
<li class="col-xs-3 col-sm-2 col-md-1 menu-active"> <?php foreach ($menu_options as $option): ?>
<a href="dashboard.php"><i class="fa fa-home"></i>Dashboard</a> <li class="col-xs-3 col-sm-2 col-md-1 <?= ($option['path'] === $current_path) ? 'menu-active' : '' ?>">
</li> <a href="<?= $option['path']; ?>">
<li class="col-xs-3 col-sm-2 col-md-1"> <i class="fa <?= $option['icon']; ?>"></i>
<a href="configuration.php"><i class="fa fa-cogs"></i>Configuration</a> <?= $option['name']; ?>
</li> </a>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="admin.php"><i class="fa fa-user"></i>Admin Account</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="reports.php"><i class="fa fa-flag"></i>Reports</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="pastes.php"><i class="fa fa-clipboard"></i>Pastes</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="users.php"><i class="fa fa-users"></i>Users</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="ipbans.php"><i class="fa fa-ban"></i>IP Bans</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="stats.php"><i class="fa fa-line-chart"></i>Statistics</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="sitemap.php"><i class="fa fa-map-signs"></i>Sitemap</a>
</li>
<li class="col-xs-3 col-sm-2 col-md-1">
<a href="tasks.php"><i class="fa fa-tasks"></i>Tasks</a>
</li> </li>
<?php endforeach; ?>
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -1,6 +1,17 @@
<?php <?php
define('IN_PONEPASTE', 1); define('IN_PONEPASTE', 1);
require_once('common.php'); require_once(__DIR__ . '/common.php');
use PonePaste\Models\Paste;
list($per_page, $current_page) = pp_setup_pagination();
$total_pastes = Paste::count();
$pastes = Paste::with('user')
->orderBy('id', 'desc')
->limit($per_page)
->offset($current_page * $per_page)
->get();
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -66,114 +77,6 @@ require_once('common.php');
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-widget"> <div class="panel panel-widget">
<?php
if (isset($_GET['details'])) {
$detail_id = htmlentities(Trim($_GET['details']));
$query = "SELECT * FROM pastes WHERE id='$detail_id'";
$result = mysqli_query($con, $query);
while ($row = mysqli_fetch_array($result)) {
$p_title = $row['title'];
$p_content = $row['content'];
$p_visible = $row['visible'];
$p_code = $row['code'];
$p_expiry = $row['expiry'];
$p_password = $row['password'];
$p_member = $row['member'];
$p_date = $row['date'];
$p_encrypt = $row['encrypt'];
$p_views = $row['views'];
$p_ip = $row['ip'];
}
if ($p_encrypt == "" || $p_encrypt == null || $p_encrypt == '0') {
$encrypt = "Not Encrypted";
} else {
$encrypt = "Encrypted";
}
if ($p_expiry == "NULL") {
$expiry = "Never";
} else {
$input_time = $p_expiry;
$current_time = mktime(date("H"), date("i"), date("s"), date("n"), date("j"), date("Y"));
if ($input_time < $current_time) {
$expiry = "Paste is expired";
} else {
$expiry = "Paste is not expired";
}
}
if ($p_password == 'NONE') {
$pass = "Not protected";
} else {
$pass = "Password protected paste";
}
if ($p_visible == '0') {
$visible = "Public";
} elseif ($p_visible == '1') {
$visible = "Unlisted";
} elseif ($p_visible == '2') {
$visible = "Private";
} else {
$visible = "Something went wrong";
}
?>
<div class="panel-title">
Details of Paste ID <?php echo $detail_id; ?>
</div>
<div class="panel-body table-responsive">
<table class="table display dataTable">
<tbody>
<tr>
<td> Username</td>
<td> <?php echo $p_member; ?> </td>
</tr>
<tr>
<td> Paste Title</td>
<td> <?php echo $p_title; ?> </td>
</tr>
<tr>
<td> Visibility</td>
<td> <?php echo $visible; ?> </td>
</tr>
<tr>
<td> Password</td>
<td> <?php echo $pass; ?> </td>
</tr>
<tr>
<td> Views</td>
<td> <?php echo $p_views; ?> </td>
</tr>
<tr>
<td> IP</td>
<td> <?php echo $p_ip; ?> </td>
</tr>
<tr>
<td> Syntax Highlighting</td>
<td> <?php echo $p_code; ?> </td>
</tr>
<tr>
<td> Expiration</td>
<td> <?php echo $expiry; ?> </td>
</tr>
<tr>
<td> Encrypted Paste</td>
<td> <?php echo $encrypt; ?></td>
</tr>
</tbody>
</table>
</div>
<?php } else { ?>
<div class="panel-body"> <div class="panel-body">
<div class="panel-title"> <div class="panel-title">
Manage Pastes Manage Pastes
@ -186,22 +89,33 @@ require_once('common.php');
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
<th>Title</th>
<th>Username</th> <th>Username</th>
<th>IP</th> <th>IP</th>
<th>Visibility</th> <th>Visibility</th>
<th>More Details</th>
<th>View Paste</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($pastes as $paste): ?>
<tr>
<td>
<a href="<?= urlForPaste($paste) ?>">
<?= pp_html_escape($paste->id); ?>
</a>
</td>
<td><?= pp_html_escape($paste->title); ?></td>
<td><?= pp_html_escape($paste->user->username); ?></td>
<td><?= pp_html_escape($paste->ip); ?></td>
<td><?= pp_html_escape($paste->visible); ?></td>
</tr>
<?php endforeach; ?>
</tbody> </tbody>
</table> </table>
</div> </div>
<?php } ?>
</div>
</div> </div>
<?= paginate($current_page, $per_page, $total_pastes); ?>
</div> </div>
<!-- End Admin Settings --> <!-- End Admin Settings -->
</div> </div>
@ -212,8 +126,8 @@ require_once('common.php');
</div> </div>
<!-- End Footer --> <!-- End Footer -->
</div> </div>
<!-- End content --> <!-- End content -->
</body> </body>
</html> </html>

View file

@ -1,24 +1,23 @@
<?php <?php
session_start(); define('IN_PONEPASTE', 1);
require_once(__DIR__ . '/common.php');
if (!isset($_SESSION['login'])) { use PonePaste\Models\Report;
header("Location: .");
exit(); if (isset($_POST['close_report']) && isset($_POST['report_id'])) {
$report = Report::find((int) $_POST['report_id']);
if ($report) {
$report->open = false;
$report->save();
}
flashSuccess('Report has been closed.');
} }
if (isset($_GET['logout'])) { $reports_count = Report::count();
if (isset($_SESSION['login'])) $reports = Report::with('paste', 'user')
unset($_SESSION['login']); ->orderBy('id', 'desc')
->get();
session_destroy();
header("Location: .");
exit();
}
$date = date('jS F Y');
$ip = $_SERVER['REMOTE_ADDR'];
require_once('../includes/config.php');
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -62,155 +61,14 @@ require_once('../includes/config.php');
<?php include 'menu.php'; ?> <?php include 'menu.php'; ?>
<!-- End Menu --> <!-- End Menu -->
<?php
if (isset($_GET['remove'])) {
$delid = htmlentities(Trim($_GET['remove']));
$query = "DELETE FROM user_reports WHERE id=$delid";
$result = mysqli_query($con, $query);
if (mysqli_errno($con)) {
$msg = '<div class="paste-alert alert6" style="text-align: center;">
' . mysqli_error($con) . '
</div>';
} else {
$msg = '<div class="paste-alert alert3" style="text-align: center;">
Report Removed
</div>';
}
}
if (isset($_GET['delete'])) {
$delid = htmlentities(Trim($_GET['delete']));
$query = "DELETE FROM pastes WHERE id=$delid";
$result = mysqli_query($con, $query);
if (mysqli_errno($con)) {
$msg = '<div class="paste-alert alert6" style="text-align: center;">
' . mysqli_error($con) . '
</div>';
} else {
$msg = '<div class="paste-alert alert3" style="text-align: center;">
Report Removed
</div>';
}
}
?>
<!-- Start Pastes --> <!-- Start Pastes -->
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-widget"> <div class="panel panel-widget">
<?php
if (isset($_GET['details'])) {
$detail_id = htmlentities(Trim($_GET['details']));
$query = "SELECT * FROM pastes WHERE id='$detail_id'";
$result = mysqli_query($con, $query);
while ($row = mysqli_fetch_array($result)) {
$p_title = $row['title'];
$p_content = $row['content'];
$p_visible = $row['visible'];
$p_code = $row['code'];
$p_expiry = $row['expiry'];
$p_password = $row['password'];
$p_member = $row['member'];
$p_date = $row['date'];
$p_encrypt = $row['encrypt'];
$p_views = $row['views'];
$p_ip = $row['ip'];
}
if ($p_encrypt == "" || $p_encrypt == null || $p_encrypt == '0') {
$encrypt = "Not Encrypted";
} else {
$encrypt = "Encrypted";
}
if ($p_expiry == "NULL") {
$expiry = "Never";
} else {
$input_time = $p_expiry;
$current_time = mktime(date("H"), date("i"), date("s"), date("n"), date("j"), date("Y"));
if ($input_time < $current_time) {
$expiry = "Paste is expired";
} else {
$expiry = "Paste is not expired";
}
}
if ($p_password == 'NONE') {
$pass = "Not protected";
} else {
$pass = "Password protected paste";
}
if ($p_visible == '0') {
$visible = "Public";
} elseif ($p_visible == '1') {
$visible = "Unlisted";
} elseif ($p_visible == '2') {
$visible = "Private";
} else {
$visible = "Something went wrong";
}
?>
<div class="panel-title">
Details of Paste ID <?php echo $detail_id; ?>
</div>
<div class="panel-body table-responsive">
<table class="table display dataTable">
<tbody>
<tr>
<td> Username</td>
<td> <?php echo $p_member; ?> </td>
</tr>
<tr>
<td> Paste Title</td>
<td> <?php echo $p_title; ?> </td>
</tr>
<tr>
<td> Visibility</td>
<td> <?php echo $visible; ?> </td>
</tr>
<tr>
<td> Password</td>
<td> <?php echo $pass; ?> </td>
</tr>
<tr>
<td> Views</td>
<td> <?php echo $p_views; ?> </td>
</tr>
<tr>
<td> IP</td>
<td> <?php echo $p_ip; ?> </td>
</tr>
<tr>
<td> Syntax Highlighting</td>
<td> <?php echo $p_code; ?> </td>
</tr>
<tr>
<td> Expiration</td>
<td> <?php echo $expiry; ?> </td>
</tr>
<tr>
<td> Encrypted Paste</td>
<td> <?php echo $encrypt; ?></td>
</tr>
</tbody>
</table>
</div>
<?php } else { ?>
<div class="panel-body"> <div class="panel-body">
<?php outputFlashes($flashes); ?>
<div class="panel-title"> <div class="panel-title">
Manage Pastes Reports
</div> </div>
<?php if (isset($msg)) echo $msg; ?> <?php if (isset($msg)) echo $msg; ?>
@ -219,21 +77,45 @@ require_once('../includes/config.php');
id="pastesTable"> id="pastesTable">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>Time</th>
<th>User Reported</th> <th>Paste</th>
<th>Paste ID</th> <th>User</th>
<th>Reason</th> <th>Report Reason</th>
<th>More Details</th> <th>Close Report</th>
<th>View Paste</th>
<th>Delete</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($reports as $report): ?>
<tr class="<?= $report->open ? 'success' : 'danger' ?>">
<td><?= pp_html_escape($report->created_at); ?></td>
<td>
<a href="<?= urlForPaste($report->paste); ?>">
<?= pp_html_escape($report->paste->title); ?>
</a>
</td>
<td>
<a href="<?= urlForMember($report->user); ?>">
<?= pp_html_escape($report->user->username); ?>
</a>
</td>
<td><?= pp_html_escape($report->reason); ?></td>
<td>
<?php if ($report->open): ?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?> "/>
<input type="hidden" name="report_id" value="<?= $report->id ?> "/>
<input type="submit" name="close_report" value="Close"
class="btn btn-danger"/>
</form>
<?php else: ?>
Already Closed
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody> </tbody>
</table> </table>
</div> </div>
<?php } ?>
</div> </div>
</div> </div>
</div> </div>
@ -243,28 +125,10 @@ require_once('../includes/config.php');
<!-- Start Footer --> <!-- Start Footer -->
<div class="row footer"> <div class="row footer">
<div class="col-md-6 text-left">
<a href="https://github.com/jordansamuel/PASTE" target="_blank">Updates</a> &mdash; <a
href="https://github.com/jordansamuel/PASTE/issues" target="_blank">Bugs</a>
</div>
<div class="col-md-6 text-right">
Powered by <a href="https://phpaste.sourceforge.io" target="_blank">Paste</a>
</div>
</div> </div>
<!-- End Footer --> <!-- End Footer -->
</div> </div>
<!-- End content --> <!-- End content -->
<script type="text/javascript" language="javascript" class="init">
$(document).ready(function () {
$('#pastesTable').dataTable({
"processing": true,
"serverSide": true,
"ajax": "ajax_reports.php"
});
});
</script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
</body> </body>
</html> </html>

View file

@ -104,6 +104,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
$editing = isset($_POST['edit']); $editing = isset($_POST['edit']);
$deleting = isset($_POST['delete']);
$paste_title = trim($_POST['title']); $paste_title = trim($_POST['title']);

View file

@ -78,12 +78,17 @@ $paste_is_favourited = $current_user !== null && $current_user->favourites->wher
$is_private = $paste->visible === Paste::VISIBILITY_PRIVATE; $is_private = $paste->visible === Paste::VISIBILITY_PRIVATE;
if (!can('view', $paste)) { if (!can('view', $paste)) {
if ($paste->is_hidden) {
$error = 'This paste has been removed by the moderation team.';
} else {
$error = 'This is a private paste. If you created this paste, please log in to view it.'; $error = 'This is a private paste. If you created this paste, please log in to view it.';
}
goto Not_Valid_Paste; goto Not_Valid_Paste;
} }
/* Paste deletion */ /* Paste deletion */
if (isset($_POST['delete'])) { if (false && isset($_POST['delete'])) {
if (!can('delete', $paste)) { if (!can('delete', $paste)) {
$error = 'You cannot delete someone else\'s paste!'; $error = 'You cannot delete someone else\'s paste!';
goto Not_Valid_Paste; goto Not_Valid_Paste;
@ -95,6 +100,25 @@ if (isset($_POST['delete'])) {
die(); die();
} }
if (isset($_POST['hide'])) {
if (!can('hide', $paste)) {
$error = 'You do not have permission to hide this paste.';
goto Not_Valid_Paste;
}
$is_hidden = !$paste->is_hidden;
if ($is_hidden) {
$paste->reports()->update(['open' => false]);
}
$paste->is_hidden = $is_hidden;
$paste->save();
flashSuccess('Paste hidden.');
header('Location: /');
die();
}
/* Verify paste password */ /* Verify paste password */
$password_required = $p_password !== null && $p_password !== 'NONE'; $password_required = $p_password !== null && $p_password !== 'NONE';
$password_valid = true; $password_valid = true;

81
public/report.php Normal file
View file

@ -0,0 +1,81 @@
<?php
define('IN_PONEPASTE', 1);
require_once(__DIR__ . '/../includes/common.php');
use PonePaste\Models\Paste;
$error = null;
if ($current_user === null) {
header("Location: /login");
die();
}
$paste = Paste::find((int) $_REQUEST['id']);
if (!$paste) {
header('HTTP/1.1 404 Not Found');
$error = 'Not found';
goto done;
}
if (!can('view', $paste)) {
$error = 'This is a private paste. Why are you attempting to report it?';
goto done;
}
/* $password_ok_pastes is an array of IDs of pastes for which a correct password has already been entered this session. */
if (isset($_SESSION['password_ok'])) {
$password_ok_pastes = json_decode($_SESSION['password_ok']);
} else {
$password_ok_pastes = [];
}
$password_required = $paste->password !== null && $paste->password !== 'NONE';
if ($password_required && !in_array($paste->id, $password_ok_pastes)) {
$error = 'This is a passworded paste, but you have not entered the password for it.';
goto done;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verifyCsrfToken()) {
$error = 'Invalid CSRF token (do you have cookies enabled?)';
goto done;
}
// TODO: Check if the paste has already been reported.
if (empty($_POST['reason'])) {
$error = 'You must provide a report reason.';
goto done;
}
if ($paste->reports->where('open', true)->isNotEmpty()) {
$error = 'This paste has already been reported.';
goto done;
}
$paste->reports()->create([
'user_id' => $current_user->id,
'reason' => $_POST['reason'],
'open' => true
]);
flashSuccess('Paste successfully reported.');
header('Location: ' . urlForPaste($paste));
die();
}
$csrf_token = setupCsrfToken();
$page_template = 'report';
$page_title = 'Report Paste';
done:
if ($error) {
$page_title = 'Error';
$page_template = 'errors';
}
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');

View file

@ -8267,7 +8267,7 @@ a.has-text-link-dark:hover {
background-color: #2160c4 !important; background-color: #2160c4 !important;
} }
.has-text-info { .has-text-info {
color: #3298dc !important; color: #3298dc;
} }
a.has-text-info:focus, a.has-text-info:focus,
a.has-text-info:hover { a.has-text-info:hover {

File diff suppressed because one or more lines are too long

View file

@ -1,12 +1,13 @@
<?php <?php
/* prevent inclusion of arbitrary files */ /* prevent inclusion of arbitrary files */
use PonePaste\Models\Report;
$template_candidates = scandir(__DIR__); $template_candidates = scandir(__DIR__);
if (!in_array($page_template . '.php', $template_candidates)) { if (!in_array($page_template . '.php', $template_candidates)) {
die('Failed to find template'); die('Failed to find template');
} }
$start = microtime(true);
$flashes = getFlashes(); $flashes = getFlashes();
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -80,6 +81,15 @@ $flashes = getFlashes();
</span> </span>
<span>Events</span> <span>Events</span>
</a> </a>
<?php if ($current_user !== null && $current_user->admin): ?>
<?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">
<i class="fa <?= $has_reports ? 'fa-exclamation' : 'fa-toolbox' ?>" aria-hidden="true" <?= $has_reports ? 'style="color: red;"' : '' ?>></i>
</span>
<span>Admin</span>
</a>
<?php endif; ?>
<?php endif; /* !$site_is_private */ ?> <?php endif; /* !$site_is_private */ ?>
<div class="navbar-item has-dropdown is-hoverable"> <div class="navbar-item has-dropdown is-hoverable">

View file

@ -124,10 +124,11 @@
<?php if ($_SERVER['REQUEST_METHOD'] == 'POST') { <?php if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($error)) { ?> if (isset($error)) { ?>
<!-- Error Panel --> <!-- Error Panel -->
<i class="fa fa-exclamation-circle" aria-hidden=" true"></i> <?php echo $error; ?> <i class="fa fa-exclamation-circle" aria-hidden="true"></i> <?php echo $error; ?>
<?php } <?php }
} }
?> ?>
<?php outputFlashes($flashes); ?>
<h1 class="subtitle is-4"> <h1 class="subtitle is-4">
New Paste New Paste
</h1> </h1>

32
theme/bulma/report.php Normal file
View file

@ -0,0 +1,32 @@
<main class="bd-main">
<div class="bd-side-background"></div>
<div class="bd-main-container container">
<div class="bd-duo">
<div class="bd-lead">
<h1 class="title is-5">Reporting <b><?= pp_html_escape($paste->title) ?></b></h1>
<form method="post">
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="reason">Reason</label>
<div class="control has-icons-left has-icons-right">
<input type="text" class="input" name="reason" id="reason"
placeholder="Reason for reporting this paste" maxlength="255">
<span class="icon is-small is-left">
<i class="fas fa-info"></i>
</span>
</div>
</div>
<div class="field">
<?php if (isset($csrf_token)): ?>
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?>" />
<?php endif; ?>
<button type="submit" class="button is-info">Report</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</main>

View file

@ -176,8 +176,8 @@
<?php foreach ($profile_favs as $paste): ?> <?php foreach ($profile_favs as $paste): ?>
<?php <?php
$escaped_title = pp_html_escape(truncate($paste->title, 20, 50)); $escaped_title = pp_html_escape(truncate($paste->title, 20, 50));
$f_date = new DateTime($paste->pivot->f_time); $f_date = new DateTime($paste->pivot->created_at);
$update_date = new DateTime($paste->updated_at); $update_date = $paste->updated_at !== null ? new DateTime($paste->updated_at) : $f_date;
$delta = $update_date->diff(new DateTime(), true); $delta = $update_date->diff(new DateTime(), true);
$pasteJson = array_merge( $pasteJson = array_merge(
$paste->only('id', 'title', 'tags', 'views', 'created_at'), $paste->only('id', 'title', 'tags', 'views', 'created_at'),

View file

@ -56,54 +56,15 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
<div class="bd-duo"> <div class="bd-duo">
<div class="bd-lead"> <div class="bd-lead">
<div class="content panel"> <div class="content panel">
<article class="message is-danger" id="panel" style="display: none;"> <?php outputFlashes($flashes) ?>
<div class="message-header" style="margin-bottom: 0;">
<p style="margin-bottom: 1px;">Report Paste</p>
<button class="delete" onclick="closereport()" aria-label="delete"></button>
</div>
<div class="message-body">
<div class="columns">
<p>Reporting is currently non-functional. Please email admin ( a t ) ponepaste (.) org if this is a serious violation.</p>
<!--<div class="column">
<p>Please select how this paste violates a rule:</p>
</div>
<div class="column">
<form class="form-horizontal" id="reportpaste" name="preport" action="report.php"
method="POST">
<div class="select">
<select name="reasonrep">
<option>Select dropdown</option>
<option value="0">Not /mlp/ Related</option>
<option value="1">Links to Illegal Content</option>
<option value="2">Paste has personal information (Dox)</option>
</select>
</div>
</div>-->
</div>
</div>
<!--<div class="column">
<input type="hidden" name="reppasteid" value="<?php echo($paste->id); ?>">
<div>
<div style="text-align: center;">
<div id="reportbutton" class="column">
<input class="button is-danger is-fullwidth" type="submit" name="reportpaste"
id="report" value="Report Paste"/>
</div>
</div>
</div>
</div>
</form>-->
</article>
<div class="columns is-multiline"> <div class="columns is-multiline">
<div class="column is-4"> <div class="column is-4">
<span class="tag is-normal"><i class="fa fa-code fa-lg" <span class="tag is-normal"><i class="fa fa-code fa-lg"
aria-hidden="true"></i>&nbsp;&nbsp;<?php echo strtoupper(pp_html_escape($paste['code'])); ?></span> aria-hidden="true"></i>&nbsp;&nbsp;<?= strtoupper(pp_html_escape($paste['code'])); ?></span>
<span class="tag is-normal"><i class="fa fa-eye fa-lg" <span class="tag is-normal"><i class="fa fa-eye fa-lg"
aria-hidden="true"></i>&nbsp;&nbsp;<?php echo $paste['views']; ?></span> aria-hidden="true"></i>&nbsp;&nbsp;<?= pp_html_escape($paste['views']); ?></span>
<span class="tag is-normal"><i class="fa fa-star fa-lg" <span class="tag is-normal"><i class="fa fa-star fa-lg"
aria-hidden="true"></i>&nbsp;&nbsp;<?php echo $fav_count; ?></span> aria-hidden="true"></i>&nbsp;&nbsp;<?= pp_html_escape($fav_count); ?></span>
<br> <br>
<span class="tag is-normal"> <span class="tag is-normal">
<i class="fa fa-file-word fa-lg" aria-hidden="true"></i> <i class="fa fa-file-word fa-lg" aria-hidden="true"></i>
@ -116,7 +77,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
</span> </span>
<span class="tag is-normal"> <span class="tag is-normal">
<i class="fa fa-list-ol fa-lg" aria-hidden="true"></i>&nbsp;&nbsp; <i class="fa fa-list-ol fa-lg" aria-hidden="true"></i>&nbsp;&nbsp;
<?php echo substr_count($op_content, "\n") + 1; ?> <?= substr_count($op_content, "\n") + 1; ?>
</span> </span>
</div> </div>
<div class="column is-4 has-text-centered"> <div class="column is-4 has-text-centered">
@ -144,7 +105,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
<button type="submit" class="icon tool-icon button--no-style"><i class="fas fa-star fa-lg <?= $paste_is_favourited ? '' : 'has-text-grey' ?>" title="Favourite"></i></button> <button type="submit" class="icon tool-icon button--no-style"><i class="fas fa-star fa-lg <?= $paste_is_favourited ? '' : 'has-text-grey' ?>" title="Favourite"></i></button>
</form> </form>
<?php endif; ?> <?php endif; ?>
<a class="icon tool-icon flip" onclick="openreport()"><i <a class="icon tool-icon flip" href="<?= urlForReport($paste); ?>"><i
class="far fa-flag fa-lg has-text-grey" title="Report Paste"></i></a> class="far fa-flag fa-lg has-text-grey" title="Report Paste"></i></a>
<?php if ($paste['code'] != "pastedown") { ?> <?php if ($paste['code'] != "pastedown") { ?>
<a class="icon tool-icon" href="javascript:togglev();"><i <a class="icon tool-icon" href="javascript:togglev();"><i
@ -205,6 +166,20 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
<div id="paste" style="line-height: 18px;"><?= $p_content ?></div> <div id="paste" style="line-height: 18px;"><?= $p_content ?></div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php if (can('hide', $paste)): ?>
<div class="mod-tools">
<p>Moderation Tools</p>
<form method="post">
<?php if (isset($csrf_token)): ?>
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?>" />
<?php endif; ?>
<div class="field">
<input class="button is-small <?= $paste->is_hidden ? 'is-success' : 'is-danger' ?>" type="submit" name="hide" id="hide"
value="<?= $paste->is_hidden ? 'Unhide' : 'Hide' ?> Paste" />
</div>
</form>
</div>
<?php endif; ?>
<!-- Guests --> <!-- Guests -->
<?php if ($totalpastes !== 0 && !can('edit', $paste)) { ?> <?php if ($totalpastes !== 0 && !can('edit', $paste)) { ?>
<hr> <hr>
@ -255,7 +230,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
onclick="highlight(document.getElementById('code')); return false;"><i onclick="highlight(document.getElementById('code')); return false;"><i
class="fa fa-indent"></i>&nbsp;Highlight</a> class="fa fa-indent"></i>&nbsp;Highlight</a>
</div> </div>
<div class="level-item"> <div class="level-item mx-1">
<?php if (isset($csrf_token)): ?> <?php if (isset($csrf_token)): ?>
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?>" /> <input type="hidden" name="csrf_token" value="<?= $csrf_token ?>" />
<?php endif; ?> <?php endif; ?>