New private image extension
This commit is contained in:
parent
6d16c52367
commit
1fdd5bf575
6 changed files with 390 additions and 3 deletions
|
@ -96,7 +96,10 @@ abstract class Permissions
|
|||
public const CRON_ADMIN = "cron_admin";
|
||||
public const APPROVE_IMAGE = "approve_image";
|
||||
public const APPROVE_COMMENT = "approve_comment";
|
||||
|
||||
|
||||
public const SET_PRIVATE_IMAGE = "set_private_image";
|
||||
public const SET_OTHERS_PRIVATE_IMAGES = "set_others_private_images";
|
||||
|
||||
public const BULK_IMPORT = "bulk_import";
|
||||
public const BULK_EXPORT = "bulk_export";
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ new UserClass("user", "base", [
|
|||
Permissions::EDIT_FAVOURITES => true,
|
||||
Permissions::SEND_PM => true,
|
||||
Permissions::READ_PM => true,
|
||||
Permissions::SET_PRIVATE_IMAGE => true,
|
||||
]);
|
||||
|
||||
new UserClass("hellbanned", "user", [
|
||||
|
@ -200,6 +201,9 @@ new UserClass("admin", "base", [
|
|||
|
||||
Permissions::BULK_IMPORT =>true,
|
||||
Permissions::BULK_EXPORT =>true,
|
||||
|
||||
Permissions::SET_PRIVATE_IMAGE => true,
|
||||
Permissions::SET_OTHERS_PRIVATE_IMAGES => true,
|
||||
]);
|
||||
|
||||
@include_once "data/config/user-classes.conf.php";
|
||||
|
|
12
ext/private_image/info.php
Normal file
12
ext/private_image/info.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
class PrivateImageInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "private_image";
|
||||
|
||||
public $key = self::KEY;
|
||||
public $name = "Private Image";
|
||||
public $authors = ["Matthew Barbour"=>"matthew@darkholme.net"];
|
||||
public $license = self::LICENSE_WTFPL;
|
||||
public $description = "Allows users to mark images as private, which prevents other users from seeing them.";
|
||||
}
|
286
ext/private_image/main.php
Normal file
286
ext/private_image/main.php
Normal file
|
@ -0,0 +1,286 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
abstract class PrivateImageConfig
|
||||
{
|
||||
const VERSION = "ext_private_image_version";
|
||||
const USER_SET_DEFAULT = "user_private_image_set_default";
|
||||
const USER_VIEW_DEFAULT = "user_private_image_view_default";
|
||||
}
|
||||
|
||||
class PrivateImage extends Extension
|
||||
{
|
||||
/** @var PrivateImageTheme */
|
||||
protected $theme;
|
||||
|
||||
public function onInitExt(InitExtEvent $event)
|
||||
{
|
||||
global $config;
|
||||
|
||||
Image::$bool_props[] = "private ";
|
||||
}
|
||||
|
||||
public function onInitUserConfig(InitUserConfigEvent $event)
|
||||
{
|
||||
$event->user_config->set_default_bool(PrivateImageConfig::USER_SET_DEFAULT, false);
|
||||
$event->user_config->set_default_bool(PrivateImageConfig::USER_VIEW_DEFAULT, true);
|
||||
}
|
||||
|
||||
public function onUserOptionsBuilding(UserOptionsBuildingEvent $event)
|
||||
{
|
||||
global $user, $user_config;
|
||||
|
||||
$event->add_html(
|
||||
$this->theme->get_user_options(
|
||||
$user,
|
||||
$user_config->get_bool(PrivateImageConfig::USER_SET_DEFAULT),
|
||||
$user_config->get_bool(PrivateImageConfig::USER_VIEW_DEFAULT),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
global $page, $user, $user_config;
|
||||
|
||||
if ($event->page_matches("privatize_image") && $user->can(Permissions::SET_PRIVATE_IMAGE)) {
|
||||
// Try to get the image ID
|
||||
$image_id = int_escape($event->get_arg(0));
|
||||
if (empty($image_id)) {
|
||||
$image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null;
|
||||
}
|
||||
if (empty($image_id)) {
|
||||
throw new SCoreException("Can not make image private: No valid Image ID given.");
|
||||
}
|
||||
$image = Image::by_id($image_id);
|
||||
if ($image==null) {
|
||||
throw new SCoreException("Image not found.");
|
||||
}
|
||||
if ($image->owner_id!=$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
throw new SCoreException("Cannot set another user's image to private.");
|
||||
}
|
||||
|
||||
self::privatize_image($image_id);
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("post/view/" . $image_id));
|
||||
}
|
||||
|
||||
if ($event->page_matches("publicize_image")) {
|
||||
// Try to get the image ID
|
||||
$image_id = int_escape($event->get_arg(0));
|
||||
if (empty($image_id)) {
|
||||
$image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null;
|
||||
}
|
||||
if (empty($image_id)) {
|
||||
throw new SCoreException("Can not make image public: No valid Image ID given.");
|
||||
}
|
||||
$image = Image::by_id($image_id);
|
||||
if ($image==null) {
|
||||
throw new SCoreException("Image not found.");
|
||||
}
|
||||
if ($image->owner_id!=$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
throw new SCoreException("Cannot set another user's image to private.");
|
||||
}
|
||||
|
||||
self::publicize_image($image_id);
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("post/view/".$image_id));
|
||||
}
|
||||
|
||||
if ($event->page_matches("user_admin")) {
|
||||
if (!$user->check_auth_token()) {
|
||||
return;
|
||||
}
|
||||
switch ($event->get_arg(0)) {
|
||||
case "private_image":
|
||||
if (!array_key_exists("id", $_POST) || empty($_POST["id"])) {
|
||||
return;
|
||||
}
|
||||
$id = intval($_POST["id"]);
|
||||
if ($id != $user->id) {
|
||||
throw new SCoreException("Cannot change another user's settings");
|
||||
}
|
||||
$set_default = array_key_exists("set_default", $_POST);
|
||||
$view_default = array_key_exists("view_default", $_POST);
|
||||
|
||||
$user_config->set_bool(PrivateImageConfig::USER_SET_DEFAULT, $set_default);
|
||||
$user_config->set_bool(PrivateImageConfig::USER_VIEW_DEFAULT, $view_default);
|
||||
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("user"));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function onDisplayingImage(DisplayingImageEvent $event)
|
||||
{
|
||||
global $user, $page, $config;
|
||||
|
||||
if ($event->image->private===true && $event->image->owner_id!=$user->id && !$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("post/list"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const SEARCH_REGEXP = "/^private:(yes|no)/";
|
||||
public function onSearchTermParse(SearchTermParseEvent $event)
|
||||
{
|
||||
global $user, $database, $user_config;
|
||||
$show_private = $user_config->get_bool(PrivateImageConfig::USER_VIEW_DEFAULT);
|
||||
|
||||
$matches = [];
|
||||
|
||||
if (is_null($event->term) && $this->no_private_query($event->context)) {
|
||||
if ($show_private) {
|
||||
$event->add_querylet(
|
||||
new Querylet(
|
||||
$database->scoreql_to_sql("private = SCORE_BOOL_N OR owner_id = :private_owner_id"),
|
||||
["private_owner_id"=>$user->id]
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$event->add_querylet(
|
||||
new Querylet(
|
||||
$database->scoreql_to_sql("private = SCORE_BOOL_N")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($event->term)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) {
|
||||
$query = "private = ";
|
||||
$params = [];
|
||||
|
||||
if ($matches[1] == "no") {
|
||||
$query .= "SCORE_BOOL_N";
|
||||
} else {
|
||||
$query .= "SCORE_BOOL_Y";
|
||||
|
||||
// Admins can view others private images, but they have to specify the user
|
||||
if (!$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES) ||
|
||||
!UserPage::has_user_query($event->context)) {
|
||||
$query .= " AND owner_id = :private_owner_id";
|
||||
$params["private_owner_id"] = $user->id;
|
||||
}
|
||||
}
|
||||
$event->add_querylet(new Querylet($database->scoreql_to_sql($query), $params));
|
||||
}
|
||||
}
|
||||
|
||||
public function onHelpPageBuilding(HelpPageBuildingEvent $event)
|
||||
{
|
||||
if ($event->key===HelpPages::SEARCH) {
|
||||
$block = new Block();
|
||||
$block->header = "Private Images";
|
||||
$block->body = $this->theme->get_help_html();
|
||||
$event->add_block($block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function no_private_query(array $context): bool
|
||||
{
|
||||
foreach ($context as $term) {
|
||||
if (preg_match(self::SEARCH_REGEXP, $term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function privatize_image($image_id)
|
||||
{
|
||||
global $database, $user;
|
||||
|
||||
$database->execute(
|
||||
"UPDATE images SET private = :true WHERE id = :id AND private = :false",
|
||||
["id"=>$image_id, "true"=>true, "false"=>$database->scoresql_value_prepare(false)]
|
||||
);
|
||||
}
|
||||
|
||||
public static function publicize_image($image_id)
|
||||
{
|
||||
global $database;
|
||||
|
||||
$database->execute(
|
||||
"UPDATE images SET private = :false WHERE id = :id AND private = :true",
|
||||
["id"=>$image_id, "true"=>true, "false"=>$database->scoresql_value_prepare(false)]
|
||||
);
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||
{
|
||||
global $user, $config;
|
||||
if ($user->can(Permissions::SET_PRIVATE_IMAGE) && $user->id==$event->image->owner_id) {
|
||||
$event->add_part($this->theme->get_image_admin_html($event->image));
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageAddition(ImageAdditionEvent $event)
|
||||
{
|
||||
global $user_config;
|
||||
if ($user_config->get_bool(PrivateImageConfig::USER_SET_DEFAULT)) {
|
||||
self::privatize_image($event->image->id);
|
||||
}
|
||||
}
|
||||
|
||||
public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event)
|
||||
{
|
||||
global $user, $config;
|
||||
|
||||
if ($user->can(Permissions::SET_PRIVATE_IMAGE)) {
|
||||
$event->add_action("bulk_privatize_image", "Make Private");
|
||||
$event->add_action("bulk_publicize_image", "Make Public");
|
||||
}
|
||||
}
|
||||
|
||||
public function onBulkAction(BulkActionEvent $event)
|
||||
{
|
||||
global $page, $user;
|
||||
|
||||
switch ($event->action) {
|
||||
case "bulk_privatize_image":
|
||||
if ($user->can(Permissions::SET_PRIVATE_IMAGE)) {
|
||||
$total = 0;
|
||||
foreach ($event->items as $image) {
|
||||
if ($image->owner_id==$user->id ||
|
||||
$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
self::privatize_image($image->id);
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
$page->flash("Made $total items private");
|
||||
}
|
||||
break;
|
||||
case "bulk_publicize_image":
|
||||
$total = 0;
|
||||
foreach ($event->items as $image) {
|
||||
if ($image->owner_id==$user->id ||
|
||||
$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
self::publicize_image($image->id);
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
$page->flash("Made $total items public");
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function onDatabaseUpgrade(DatabaseUpgradeEvent $event)
|
||||
{
|
||||
global $database;
|
||||
|
||||
if ($this->get_version(PrivateImageConfig::VERSION) < 1) {
|
||||
$database->execute($database->scoreql_to_sql(
|
||||
"ALTER TABLE images ADD COLUMN private SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N"
|
||||
));
|
||||
|
||||
$database->execute("CREATE INDEX images_private_idx ON images(private)");
|
||||
$this->set_version(PrivateImageConfig::VERSION, 1);
|
||||
}
|
||||
}
|
||||
}
|
68
ext/private_image/theme.php
Normal file
68
ext/private_image/theme.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php declare(strict_types=1);
|
||||
use function MicroHTML\BR;
|
||||
use function MicroHTML\BUTTON;
|
||||
use function MicroHTML\INPUT;
|
||||
|
||||
class PrivateImageTheme extends Themelet
|
||||
{
|
||||
public function get_image_admin_html(Image $image)
|
||||
{
|
||||
if ($image->private===false) {
|
||||
$html = SHM_SIMPLE_FORM(
|
||||
'privatize_image/'.$image->id,
|
||||
INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image->id]),
|
||||
SHM_SUBMIT("Make Private")
|
||||
);
|
||||
} else {
|
||||
$html = SHM_SIMPLE_FORM(
|
||||
'publicize_image/'.$image->id,
|
||||
INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image->id]),
|
||||
SHM_SUBMIT("Make Public")
|
||||
);
|
||||
}
|
||||
|
||||
return (string)$html;
|
||||
}
|
||||
|
||||
|
||||
public function get_help_html()
|
||||
{
|
||||
return '<p>Search for images that are private/public.</p>
|
||||
<div class="command_example">
|
||||
<pre>private:yes</pre>
|
||||
<p>Returns images that are private, restricted to yourself if you are not an admin.</p>
|
||||
</div>
|
||||
<div class="command_example">
|
||||
<pre>private:no</pre>
|
||||
<p>Returns images that are public.</p>
|
||||
</div>
|
||||
';
|
||||
}
|
||||
|
||||
public function get_user_options(User $user, bool $set_by_default, bool $view_by_default): string
|
||||
{
|
||||
$html = "
|
||||
<p>".make_form(make_link("user_admin/private_image"))."
|
||||
<input type='hidden' name='id' value='$user->id'>
|
||||
<table style='width: 300px;'>
|
||||
<tbody>
|
||||
<tr><th colspan='2'>Private Images</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label><input type='checkbox' name='set_default' value='true' " .($set_by_default ? 'checked=checked': ''). " />Mark images private by default</label>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td>
|
||||
<label><input type='checkbox' name='view_default' value='true' " .($view_by_default ? 'checked=checked': ''). " />View private images by default</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr><td><input type='submit' value='Save'></td></tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</form>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
}
|
|
@ -350,6 +350,20 @@ class UserPage extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public const USER_SEARCH_REGEX = "/^(?:poster|user)[=|:](.*)$/i";
|
||||
public const USER_ID_SEARCH_REGEX = "/^(?:poster|user)_id[=|:]([0-9]+)$/i";
|
||||
|
||||
public static function has_user_query(array $context): bool
|
||||
{
|
||||
foreach ($context as $term) {
|
||||
if (preg_match(self::USER_SEARCH_REGEX, $term)||
|
||||
preg_match(self::USER_ID_SEARCH_REGEX, $term)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onSearchTermParse(SearchTermParseEvent $event)
|
||||
{
|
||||
global $user;
|
||||
|
@ -359,10 +373,10 @@ class UserPage extends Extension
|
|||
}
|
||||
|
||||
$matches = [];
|
||||
if (preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) {
|
||||
if (preg_match(self::USER_SEARCH_REGEX, $event->term, $matches)) {
|
||||
$user_id = User::name_to_id($matches[1]);
|
||||
$event->add_querylet(new Querylet("images.owner_id = $user_id"));
|
||||
} elseif (preg_match("/^(?:poster|user)_id[=|:]([0-9]+)$/i", $event->term, $matches)) {
|
||||
} elseif (preg_match(self::USER_ID_SEARCH_REGEX, $event->term, $matches)) {
|
||||
$user_id = int_escape($matches[1]);
|
||||
$event->add_querylet(new Querylet("images.owner_id = $user_id"));
|
||||
} elseif ($user->can(Permissions::VIEW_IP) && preg_match("/^(?:poster|user)_ip[=|:]([0-9\.]+)$/i", $event->term, $matches)) {
|
||||
|
|
Reference in a new issue