Pulling updates from upstream
This commit is contained in:
commit
f2ee60fac5
|
@ -44,7 +44,7 @@
|
|||
"shish/microcrud" : "^2.0",
|
||||
"shish/microhtml" : "^2.0",
|
||||
"shish/gqla" : "dev-main",
|
||||
"enshrined/svg-sanitize" : "^0.15",
|
||||
"enshrined/svg-sanitize" : "^0.16",
|
||||
|
||||
"bower-asset/jquery" : "^1.12",
|
||||
"bower-asset/jquery-timeago" : "^1.5",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -170,9 +170,9 @@ class Image
|
|||
* @return Image[]
|
||||
*/
|
||||
#[Query(name: "posts", type: "[Post!]!", args: ["tags" => "[string!]"])]
|
||||
public static function find_images(?int $start = 0, ?int $limit = null, array $tags=[]): array
|
||||
public static function find_images(?int $offset = 0, ?int $limit = null, array $tags=[]): array
|
||||
{
|
||||
$result = self::find_images_internal($start, $limit, $tags);
|
||||
$result = self::find_images_internal($offset, $limit, $tags);
|
||||
|
||||
$images = [];
|
||||
foreach ($result as $row) {
|
||||
|
@ -272,53 +272,6 @@ class Image
|
|||
return (int)ceil(Image::count_images($tags) / $config->get_int(IndexConfig::IMAGES));
|
||||
}
|
||||
|
||||
private static function terms_to_conditions(array $terms): array
|
||||
{
|
||||
$tag_conditions = [];
|
||||
$img_conditions = [];
|
||||
$stpen = 0; // search term parse event number
|
||||
$order = null;
|
||||
|
||||
/*
|
||||
* Turn a bunch of strings into a bunch of TagCondition
|
||||
* and ImgCondition objects
|
||||
*/
|
||||
$stpe = send_event(new SearchTermParseEvent($stpen++, null, $terms));
|
||||
if ($stpe->order) {
|
||||
$order = $stpe->order;
|
||||
} elseif (!empty($stpe->querylets)) {
|
||||
foreach ($stpe->querylets as $querylet) {
|
||||
$img_conditions[] = new ImgCondition($querylet, true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($terms as $term) {
|
||||
$positive = true;
|
||||
if (is_string($term) && !empty($term) && ($term[0] == '-')) {
|
||||
$positive = false;
|
||||
$term = substr($term, 1);
|
||||
}
|
||||
if (strlen($term) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stpe = send_event(new SearchTermParseEvent($stpen++, $term, $terms));
|
||||
if ($stpe->order) {
|
||||
$order = $stpe->order;
|
||||
} elseif (!empty($stpe->querylets)) {
|
||||
foreach ($stpe->querylets as $querylet) {
|
||||
$img_conditions[] = new ImgCondition($querylet, $positive);
|
||||
}
|
||||
} else {
|
||||
// if the whole match is wild, skip this
|
||||
if (str_replace("*", "", $term) != "") {
|
||||
$tag_conditions[] = new TagCondition($term, $positive);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$tag_conditions, $img_conditions, $order];
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessors & mutators
|
||||
*/
|
||||
|
@ -849,19 +802,24 @@ class Image
|
|||
): Querylet {
|
||||
global $config;
|
||||
|
||||
list($tag_conditions, $img_conditions, $order) = self::terms_to_conditions($terms);
|
||||
$order = ($order ?: "images.".$config->get_string(IndexConfig::ORDER));
|
||||
$tag_conditions = [];
|
||||
$img_conditions = [];
|
||||
$order = null;
|
||||
|
||||
$positive_tag_count = 0;
|
||||
$negative_tag_count = 0;
|
||||
foreach ($tag_conditions as $tq) {
|
||||
if ($tq->positive) {
|
||||
$positive_tag_count++;
|
||||
} else {
|
||||
$negative_tag_count++;
|
||||
}
|
||||
/*
|
||||
* Turn a bunch of strings into a bunch of TagCondition
|
||||
* and ImgCondition objects
|
||||
*/
|
||||
$stpen = 0; // search term parse event number
|
||||
foreach (array_merge([null], $terms) as $term) {
|
||||
$stpe = send_event(new SearchTermParseEvent($stpen++, $term, $terms));
|
||||
$order ??= $stpe->order;
|
||||
$img_conditions = array_merge($img_conditions, $stpe->img_conditions);
|
||||
$tag_conditions = array_merge($tag_conditions, $stpe->tag_conditions);
|
||||
}
|
||||
|
||||
$order = ($order ?: "images.".$config->get_string(IndexConfig::ORDER));
|
||||
|
||||
/*
|
||||
* Turn a bunch of Querylet objects into a base query
|
||||
*
|
||||
|
@ -875,7 +833,7 @@ class Image
|
|||
*/
|
||||
|
||||
// no tags, do a simple search
|
||||
if ($positive_tag_count === 0 && $negative_tag_count === 0) {
|
||||
if (count($tag_conditions) === 0) {
|
||||
$query = new Querylet("SELECT images.* FROM images WHERE 1=1");
|
||||
}
|
||||
|
||||
|
@ -883,21 +841,20 @@ class Image
|
|||
// and do the offset / limit there, which is 10x faster than fetching
|
||||
// all the image_tags and doing the offset / limit on the result.
|
||||
elseif (
|
||||
(
|
||||
($positive_tag_count === 1 && $negative_tag_count === 0)
|
||||
|| ($positive_tag_count === 0 && $negative_tag_count === 1)
|
||||
)
|
||||
count($tag_conditions) === 1
|
||||
&& empty($img_conditions)
|
||||
&& ($order == "id DESC" || $order == "images.id DESC")
|
||||
&& !is_null($offset)
|
||||
&& !is_null($limit)
|
||||
) {
|
||||
$in = $positive_tag_count === 1 ? "IN" : "NOT IN";
|
||||
$tc = $tag_conditions[0];
|
||||
$in = $tc->positive ? "IN" : "NOT IN";
|
||||
// IN (SELECT id FROM tags) is 100x slower than doing a separate
|
||||
// query and then a second query for IN(first_query_results)??
|
||||
$tag_array = self::tag_or_wildcard_to_ids($tag_conditions[0]->tag);
|
||||
$tag_array = self::tag_or_wildcard_to_ids($tc->tag);
|
||||
if (count($tag_array) == 0) {
|
||||
if ($positive_tag_count == 1) {
|
||||
// if wildcard expanded to nothing, take a shortcut
|
||||
if ($tc->positive) {
|
||||
$query = new Querylet("SELECT images.* FROM images WHERE 1=0");
|
||||
} else {
|
||||
$query = new Querylet("SELECT images.* FROM images WHERE 1=1");
|
||||
|
@ -923,7 +880,7 @@ class Image
|
|||
}
|
||||
}
|
||||
|
||||
// more than one positive tag, or more than zero negative tags
|
||||
// more than one tag, or more than zero other conditions, or a non-default sort order
|
||||
else {
|
||||
$positive_tag_id_array = [];
|
||||
$positive_wildcard_id_array = [];
|
||||
|
|
|
@ -118,4 +118,5 @@ abstract class Permissions
|
|||
public const BULK_IMPORT = "bulk_import";
|
||||
public const BULK_EXPORT = "bulk_export";
|
||||
public const BULK_DOWNLOAD = "bulk_download";
|
||||
public const BULK_PARENT_CHILD = "bulk_parent_child";
|
||||
}
|
||||
|
|
|
@ -184,6 +184,7 @@ new UserClass("admin", "base", [
|
|||
Permissions::EDIT_FEATURE => true,
|
||||
Permissions::BULK_EDIT_VOTE => true,
|
||||
Permissions::EDIT_OTHER_VOTE => true,
|
||||
Permissions::CREATE_VOTE => true,
|
||||
Permissions::VIEW_SYSINTO => true,
|
||||
|
||||
Permissions::HELLBANNED => false,
|
||||
|
@ -222,6 +223,7 @@ new UserClass("admin", "base", [
|
|||
Permissions::BULK_IMPORT =>true,
|
||||
Permissions::BULK_EXPORT =>true,
|
||||
Permissions::BULK_DOWNLOAD => true,
|
||||
Permissions::BULK_PARENT_CHILD => true,
|
||||
|
||||
Permissions::SET_PRIVATE_IMAGE => true,
|
||||
Permissions::SET_OTHERS_PRIVATE_IMAGES => true,
|
||||
|
|
|
@ -645,7 +645,7 @@ function _fatal_error(\Exception $e): void
|
|||
foreach ($t as $n => $f) {
|
||||
$c = $f['class'] ?? '';
|
||||
$t = $f['type'] ?? '';
|
||||
$a = implode(", ", array_map("Shimmie2\stringer", $f['args']));
|
||||
$a = implode(", ", array_map("Shimmie2\stringer", $f['args'] ?? []));
|
||||
print("$n: {$f['file']}({$f['line']}): {$c}{$t}{$f['function']}({$a})\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,13 @@ class Approval extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::APPROVE_IMAGE)) {
|
||||
$event->add_link("Pending Approval", make_link("/post/list/approved%3Ano/1"), 60);
|
||||
}
|
||||
}
|
||||
|
||||
public const SEARCH_REGEXP = "/^approved:(yes|no)/";
|
||||
public function onSearchTermParse(SearchTermParseEvent $event)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
var metatags = ['order:id', 'order:width', 'order:height', 'order:filesize', 'order:filename', 'order:favorites'];
|
||||
|
||||
$('[name="search"]').tagit({
|
||||
$('.autocomplete_tags').tagit({
|
||||
singleFieldDelimiter: ' ',
|
||||
beforeTagAdded: function(event, ui) {
|
||||
if(metatags.indexOf(ui.tagLabel) !== -1) {
|
||||
|
@ -93,8 +93,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
//Stop tags containing space.
|
||||
if(keyCode === 32) {
|
||||
e.preventDefault();
|
||||
var el = $('.ui-widget-content:focus');
|
||||
|
||||
$('.autocomplete_tags').tagit('createTag', $(this).val());
|
||||
//Find the correct element in a page with multiple tagit input boxes.
|
||||
$('.autocomplete_tags').each(function(_,n){
|
||||
if (n.parentNode.contains(el[0])){
|
||||
$(n.parentNode).find('.autocomplete_tags').tagit('createTag', el.val());
|
||||
}
|
||||
});
|
||||
$(this).autocomplete('close');
|
||||
} else if (keyCode === 9) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -17,6 +17,6 @@ class BrowserSearchInfo extends ExtensionInfo
|
|||
public string $description = "Allows the user to add a browser 'plugin' to search the site with real-time suggestions";
|
||||
public ?string $documentation =
|
||||
"Once installed, users with an opensearch compatible browser should see their search box light up with whatever \"click here to add a search engine\" notification they have
|
||||
|
||||
Some code (and lots of help) by Artanis (Erik Youngren <artanis.00@gmail.com>) from the 'tagger' extension - Used with permission";
|
||||
<br>
|
||||
<br>Some code (and lots of help) by Artanis (<a href='mailto:artanis.00@gmail.com'>Erik Youngren</a>) from the 'tagger' extension - Used with permission";
|
||||
}
|
||||
|
|
|
@ -13,5 +13,26 @@ class BulkActionsInfo extends ExtensionInfo
|
|||
public array $authors = ["Matthew Barbour"=>"matthew@darkholme.net"];
|
||||
public string $license = self::LICENSE_WTFPL;
|
||||
public string $description = "Provides query and selection-based bulk action support";
|
||||
public ?string $documentation = "Provides bulk action section in list view. Allows performing actions against a set of posts based on query or manual selection. Based on Mass Tagger by Christian Walde <walde.christian@googlemail.com>, contributions by Shish and Agasa.";
|
||||
public ?string $documentation = "Provides bulk action section in list view. Allows performing actions against a set of posts based on query or manual selection. Based on Mass Tagger by <a href='mailto:walde.christian@googlemail.com'>Christian Walde</a>, contributions by Shish and Agasa.
|
||||
<p>
|
||||
<p>
|
||||
<b>Delete</b>
|
||||
<br>Deletes all selected posts.
|
||||
</p>
|
||||
<p>
|
||||
<b>Tag</b>
|
||||
<br>Add the tags to all selected posts.
|
||||
<br><code>[background wallpaper]</code> + <code>[sky]</code> → <code>[background wallpaper sky]</code>
|
||||
<br>
|
||||
<br>Remove the tags from all selected posts.
|
||||
<br><code>[background wallpaper]</code> + <code>[-wallpaper]</code> → <code>[background]</code>
|
||||
<br>
|
||||
<br>Replace the tags in all selected posts.
|
||||
<br><code>[background wallpaper]</code> + <code>[sky]</code> → <code>[sky]</code>
|
||||
</p>
|
||||
<p>
|
||||
<b>Source</b>
|
||||
<br>Sets the source of all selected posts.
|
||||
</p>
|
||||
</p>";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class BulkParentChildInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "bulk_parent_child";
|
||||
|
||||
public string $key = self::KEY;
|
||||
public string $name = "Bulk Parent Child";
|
||||
public array $authors = ["Flatty"=>""];
|
||||
public string $license = self::LICENSE_WTFPL;
|
||||
public string $description = "Allows bulk setting of parent-child relationships, in order of manual selection";
|
||||
public array $dependencies = [BulkActionsInfo::KEY];
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class BulkParentChildConfig
|
||||
{
|
||||
}
|
||||
class BulkParentChildException extends BulkActionException
|
||||
{
|
||||
}
|
||||
|
||||
class BulkParentChild extends Extension
|
||||
{
|
||||
private const PARENT_CHILD_ACTION_NAME = "bulk_parent_child";
|
||||
|
||||
public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
|
||||
if ($user->can(Permissions::BULK_PARENT_CHILD)) {
|
||||
$event->add_action(BulkParentChild::PARENT_CHILD_ACTION_NAME, "Set Parent Child");
|
||||
}
|
||||
}
|
||||
|
||||
public function onBulkAction(BulkActionEvent $event)
|
||||
{
|
||||
global $user, $page, $config;
|
||||
if ($user->can(Permissions::BULK_PARENT_CHILD)&&
|
||||
($event->action == BulkParentChild::PARENT_CHILD_ACTION_NAME)) {
|
||||
$prev_id = null;
|
||||
foreach ($event->items as $image) {
|
||||
if ($prev_id != null) {
|
||||
send_event(new ImageRelationshipSetEvent($image->id, $prev_id));
|
||||
}
|
||||
$prev_id = $image->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -216,6 +216,13 @@ class CommentList extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onRobotsBuilding(RobotsBuildingEvent $event)
|
||||
{
|
||||
// comment lists change all the time, crawlers should
|
||||
// index individual image's comments
|
||||
$event->add_disallow("comment");
|
||||
}
|
||||
|
||||
private function onPageRequest_add()
|
||||
{
|
||||
global $user, $page;
|
||||
|
|
|
@ -37,7 +37,7 @@ class CommentListTheme extends Themelet
|
|||
|
||||
$page->set_title("Comments");
|
||||
$page->set_heading("Comments");
|
||||
$page->add_block(new Block("Navigation", $nav, "left"));
|
||||
$page->add_block(new Block("Navigation", $nav, "left", 0));
|
||||
$this->display_paginator($page, "comment/list", null, $page_number, $total_pages);
|
||||
|
||||
// parts for each image
|
||||
|
|
|
@ -16,10 +16,10 @@ class CustomHtmlHeadersInfo extends ExtensionInfo
|
|||
public string $description = "Allows admins to modify & set custom <head> content";
|
||||
public ?string $documentation =
|
||||
"When you go to board config you can find a block named Custom HTML Headers.
|
||||
In that block you can simply place any thing you can place within <head></head>
|
||||
|
||||
This can be useful if you want to add website tracking code or other javascript.
|
||||
NOTE: Only use if you know what you're doing.
|
||||
|
||||
You can also add your website name as prefix or suffix to the title of each page on your website.";
|
||||
<br>In that block you can simply place any thing you can place within <head></head>
|
||||
<br>
|
||||
<br>This can be useful if you want to add website tracking code or other javascript.
|
||||
<br>NOTE: Only use if you know what you're doing.
|
||||
<br>
|
||||
<br>You can also add your website name as prefix or suffix to the title of each page on your website.";
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class DanbooruApiInfo extends ExtensionInfo
|
|||
public array $authors = ["JJS"=>"jsutinen@gmail.com"];
|
||||
public string $description = "Allow Danbooru apps like Danbooru Uploader for Firefox to communicate with Shimmie";
|
||||
public ?string $documentation =
|
||||
"<p>Notes:
|
||||
"<b>Notes</b>:
|
||||
<br>danbooru API based on documentation from danbooru 1.0 -
|
||||
http://attachr.com/7569
|
||||
<br>I've only been able to test add_post and find_tags because I use the
|
||||
|
@ -24,34 +24,32 @@ class DanbooruApiInfo extends ExtensionInfo
|
|||
<li>find_posts - sort of works, filename is returned as the original filename and probably won't help when it comes to actually downloading it
|
||||
<li>find_tags - id, name, and after_id all work but the tags parameter is ignored just like danbooru 1.0 ignores it
|
||||
</ul>
|
||||
|
||||
CHANGELOG
|
||||
13-OCT-08 8:00PM CST - JJS
|
||||
Bugfix - Properly escape source attribute
|
||||
|
||||
17-SEP-08 10:00PM CST - JJS
|
||||
Bugfix for changed page name checker in PageRequestEvent
|
||||
|
||||
13-APR-08 10:00PM CST - JJS
|
||||
Properly escape the tags returned in find_tags and find_posts - Caught by ATravelingGeek
|
||||
Updated extension info to be a bit more clear about its purpose
|
||||
Deleted add_comment code as it didn't do anything anyway
|
||||
|
||||
01-MAR-08 7:00PM CST - JJS
|
||||
Rewrote to make it compatible with Shimmie trunk again (r723 at least)
|
||||
It may or may not support the new file handling stuff correctly, I'm only testing with images and the danbooru uploader for firefox
|
||||
|
||||
21-OCT-07 9:07PM CST - JJS
|
||||
Turns out I actually did need to implement the new parameter names
|
||||
for danbooru api v1.8.1. Now danbooruup should work when used with /api/danbooru/post/create.xml
|
||||
Also correctly redirects the url provided by danbooruup in the event
|
||||
of a duplicate image.
|
||||
|
||||
19-OCT-07 4:46PM CST - JJS
|
||||
Add compatibility with danbooru api v1.8.1 style urls
|
||||
for find_posts and add_post. NOTE: This does not implement
|
||||
the changes to the parameter names, it is simply a
|
||||
workaround for the latest danbooruup firefox extension.
|
||||
Completely compatibility will probably involve a rewrite with a different URL
|
||||
";
|
||||
<br><b>CHANGELOG</b>
|
||||
<br>13-OCT-08 8:00PM CST - JJS
|
||||
<br>Bugfix - Properly escape source attribute
|
||||
<br>
|
||||
<br>17-SEP-08 10:00PM CST - JJS
|
||||
<br>Bugfix for changed page name checker in PageRequestEvent
|
||||
<br>
|
||||
<br>13-APR-08 10:00PM CST - JJS
|
||||
<br>Properly escape the tags returned in find_tags and find_posts - Caught by ATravelingGeek
|
||||
<br>Updated extension info to be a bit more clear about its purpose
|
||||
<br>Deleted add_comment code as it didn't do anything anyway
|
||||
<br>
|
||||
<br>01-MAR-08 7:00PM CST - JJS
|
||||
<br>Rewrote to make it compatible with Shimmie trunk again (r723 at least)
|
||||
<br>It may or may not support the new file handling stuff correctly, I'm only testing with images and the danbooru uploader for firefox
|
||||
<br>
|
||||
<br>21-OCT-07 9:07PM CST - JJS
|
||||
<br>Turns out I actually did need to implement the new parameter names
|
||||
<br>for danbooru api v1.8.1. Now danbooruup should work when used with /api/danbooru/post/create.xml
|
||||
<br>Also correctly redirects the url provided by danbooruup in the event
|
||||
<br>of a duplicate image.
|
||||
<br>
|
||||
<br>19-OCT-07 4:46PM CST - JJS
|
||||
<br>Add compatibility with danbooru api v1.8.1 style urls
|
||||
<br>for find_posts and add_post. NOTE: This does not implement
|
||||
<br>the changes to the parameter names, it is simply a
|
||||
<br>workaround for the latest danbooruup firefox extension.
|
||||
<br>Completely compatibility will probably involve a rewrite with a different URL";
|
||||
}
|
||||
|
|
|
@ -10,6 +10,31 @@ use GraphQL\Error\DebugFlag;
|
|||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Utils\SchemaPrinter;
|
||||
|
||||
#[\GQLA\InputObjectType]
|
||||
class MetadataInput
|
||||
{
|
||||
public function __construct(
|
||||
#[\GQLA\Field]
|
||||
public string $tags,
|
||||
#[\GQLA\Field]
|
||||
public string $source,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\GQLA\Mutation]
|
||||
public static function update_post_metadata(int $post_id, MetadataInput $metadata): Image
|
||||
{
|
||||
global $user;
|
||||
$_POST['tag_edit__tags'] = $metadata->tags;
|
||||
$_POST['tag_edit__source'] = $metadata->source;
|
||||
$image = Image::by_id($post_id);
|
||||
if (!$image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
send_event(new ImageInfoSetEvent($image));
|
||||
}
|
||||
return Image::by_id($post_id);
|
||||
}
|
||||
}
|
||||
|
||||
class GraphQL extends Extension
|
||||
{
|
||||
public static function get_schema(): Schema
|
||||
|
@ -73,6 +98,7 @@ class GraphQL extends Extension
|
|||
$body['stats'] = get_debug_info_arr();
|
||||
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
|
||||
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
|
||||
// sleep(1);
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_mime("application/json");
|
||||
$page->set_data(\json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
|
|
@ -33,7 +33,7 @@ class ImageIO extends Extension
|
|||
|
||||
public const THUMBNAIL_TYPES = [
|
||||
'JPEG' => MimeType::JPEG,
|
||||
'WEBP (Not IE/Safari compatible)' => MimeType::WEBP
|
||||
'WEBP (Not IE compatible)' => MimeType::WEBP
|
||||
];
|
||||
|
||||
public function onInitExt(InitExtEvent $event)
|
||||
|
@ -196,6 +196,19 @@ class ImageIO extends Extension
|
|||
$event->image->delete();
|
||||
}
|
||||
|
||||
public function onCommand(CommandEvent $event)
|
||||
{
|
||||
if ($event->cmd == "help") {
|
||||
print "\tdelete <post id>\n";
|
||||
print "\t\tdelete a specific post\n\n";
|
||||
}
|
||||
if ($event->cmd == "delete") {
|
||||
$post_id = (int)$event->args[0];
|
||||
$image = Image::by_id($post_id);
|
||||
send_event(new ImageDeletionEvent($image));
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageReplace(ImageReplaceEvent $event)
|
||||
{
|
||||
try {
|
||||
|
|
|
@ -12,23 +12,48 @@ class SearchTermParseEvent extends Event
|
|||
{
|
||||
public int $id = 0;
|
||||
public ?string $term = null;
|
||||
public bool $positive = true;
|
||||
/** @var string[] */
|
||||
public array $context = [];
|
||||
/** @var Querylet[] */
|
||||
public array $querylets = [];
|
||||
/** @var ImgCondition[] */
|
||||
public array $img_conditions = [];
|
||||
/** @var TagCondition[] */
|
||||
public array $tag_conditions = [];
|
||||
public ?string $order = null;
|
||||
|
||||
public function __construct(int $id, string $term=null, array $context=[])
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if ($term == "-" || $term == "*") {
|
||||
throw new SearchTermParseException("'$term' is not a valid search term");
|
||||
}
|
||||
|
||||
$positive = true;
|
||||
if (is_string($term) && !empty($term) && ($term[0] == '-')) {
|
||||
$positive = false;
|
||||
$term = substr($term, 1);
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
$this->positive = $positive;
|
||||
$this->term = $term;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function add_querylet(Querylet $q)
|
||||
{
|
||||
$this->querylets[] = $q;
|
||||
$this->add_img_condition(new ImgCondition($q, $this->positive));
|
||||
}
|
||||
|
||||
public function add_img_condition(ImgCondition $c)
|
||||
{
|
||||
$this->img_conditions[] = $c;
|
||||
}
|
||||
|
||||
public function add_tag_condition(TagCondition $c)
|
||||
{
|
||||
$this->tag_conditions[] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -257,5 +257,16 @@ class Index extends Extension
|
|||
$seed = date("Ymd");
|
||||
$event->order = "RAND($seed)";
|
||||
}
|
||||
|
||||
// If we've reached this far, and nobody else has done anything with this term, then treat it as a tag
|
||||
if ($event->order == null && $event->img_conditions == [] && $event->tag_conditions == []) {
|
||||
$event->add_tag_condition(new TagCondition($event->term, $event->positive));
|
||||
}
|
||||
}
|
||||
|
||||
public function get_priority(): int
|
||||
{
|
||||
// we want to turn a search term into a TagCondition only if nobody did anything else with that term
|
||||
return 95;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
box-shadow: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
text-align: justify;
|
||||
text-align: left;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
|
|
@ -85,20 +85,20 @@ class Media extends Extension
|
|||
{
|
||||
$sb = $event->panel->create_new_block("Media Engines");
|
||||
|
||||
// if (self::imagick_available()) {
|
||||
// try {
|
||||
// $image = new Imagick(realpath('tests/favicon.png'));
|
||||
// $image->clear();
|
||||
// $sb->add_label("ImageMagick detected");
|
||||
// } catch (ImagickException $e) {
|
||||
// $sb->add_label("<b style='color:red'>ImageMagick not detected</b>");
|
||||
// }
|
||||
// } else {
|
||||
// if (self::imagick_available()) {
|
||||
// try {
|
||||
// $image = new Imagick(realpath('tests/favicon.png'));
|
||||
// $image->clear();
|
||||
// $sb->add_label("ImageMagick detected");
|
||||
// } catch (ImagickException $e) {
|
||||
// $sb->add_label("<b style='color:red'>ImageMagick not detected</b>");
|
||||
// }
|
||||
// } else {
|
||||
$sb->start_table();
|
||||
$sb->add_table_header("Commands");
|
||||
|
||||
$sb->add_text_option(MediaConfig::CONVERT_PATH, "convert", true);
|
||||
// }
|
||||
// }
|
||||
|
||||
$sb->add_text_option(MediaConfig::FFMPEG_PATH, "ffmpeg", true);
|
||||
$sb->add_text_option(MediaConfig::FFPROBE_PATH, "ffprobe", true);
|
||||
|
@ -202,8 +202,8 @@ class Media extends Extension
|
|||
|
||||
break;
|
||||
case MediaEngine::IMAGICK:
|
||||
// if (self::imagick_available()) {
|
||||
// } else {
|
||||
// if (self::imagick_available()) {
|
||||
// } else {
|
||||
self::image_resize_convert(
|
||||
$event->input_path,
|
||||
$event->input_mime,
|
||||
|
@ -227,9 +227,9 @@ class Media extends Extension
|
|||
}
|
||||
|
||||
// TODO: Get output optimization tools working better
|
||||
// if ($config->get_bool("thumb_optim", false)) {
|
||||
// exec("jpegoptim $outname", $output, $ret);
|
||||
// }
|
||||
// if ($config->get_bool("thumb_optim", false)) {
|
||||
// exec("jpegoptim $outname", $output, $ret);
|
||||
// }
|
||||
}
|
||||
|
||||
public const CONTENT_SEARCH_TERM_REGEX = "/^content[=|:]((video)|(audio)|(image)|(unknown))$/i";
|
||||
|
|
|
@ -63,12 +63,12 @@ abstract class VideoCodecs
|
|||
|
||||
|
||||
|
||||
//
|
||||
// public static function is_input_supported(string $engine, string $mime): bool
|
||||
// {
|
||||
// return MimeType::matches_array(
|
||||
// $mime,
|
||||
// MediaEngine::INPUT_SUPPORT[$engine]
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// public static function is_input_supported(string $engine, string $mime): bool
|
||||
// {
|
||||
// return MimeType::matches_array(
|
||||
// $mime,
|
||||
// MediaEngine::INPUT_SUPPORT[$engine]
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -251,9 +251,11 @@ class MimeType
|
|||
$output = MimeType::PPM;
|
||||
break;
|
||||
// TODO: There is no uniquely defined Mime type for the cursor format. Need to figure this out.
|
||||
// case FileExtension::CUR:
|
||||
// $output = MimeType::CUR;
|
||||
// break;
|
||||
/*
|
||||
case FileExtension::CUR:
|
||||
$output = MimeType::CUR;
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ class PrivateImage extends Extension
|
|||
throw new SCoreException("Post not found.");
|
||||
}
|
||||
if ($image->owner_id!=$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
throw new SCoreException("Cannot set another user's image to private.");
|
||||
throw new SCoreException("Cannot set another user's image to public.");
|
||||
}
|
||||
|
||||
self::publicize_image($image_id);
|
||||
|
@ -114,7 +114,7 @@ class PrivateImage extends Extension
|
|||
{
|
||||
global $user, $page;
|
||||
|
||||
if ($event->image->private===true && $event->image->owner_id!=$user->id && !$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
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"));
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ class PrivateImage extends Extension
|
|||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::SET_PRIVATE_IMAGE) && $user->id==$event->image->owner_id) {
|
||||
if (($user->can(Permissions::SET_PRIVATE_IMAGE) && $user->id==$event->image->owner_id) || $user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) {
|
||||
$event->add_part($this->theme->get_image_admin_html($event->image));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class PrivateImageTheme extends Themelet
|
|||
{
|
||||
public function get_image_admin_html(Image $image): string
|
||||
{
|
||||
if ($image->private===false) {
|
||||
if ($image->private==false) {
|
||||
$html = SHM_SIMPLE_FORM(
|
||||
'privatize_image/'.$image->id,
|
||||
INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image->id]),
|
||||
|
|
|
@ -13,9 +13,9 @@ class QRImageInfo extends ExtensionInfo
|
|||
public string $url = "http://seemslegit.com";
|
||||
public array $authors = ["Zach Hall"=>"zach@sosguy.net"];
|
||||
public string $license = self::LICENSE_GPLV2;
|
||||
public string $description = "Turns BBCode into HTML";
|
||||
public string $description = "Shows a QR Code for downloading a post to cell phones";
|
||||
public ?string $documentation =
|
||||
"Shows a QR Code for downloading a post to cell phones.
|
||||
Based on Artanis's Link to Post Extension <artanis.00@gmail.com>
|
||||
Further modified by Shish to remove the 7MB local QR generator and replace it with a link to google chart APIs";
|
||||
<br>Based on <a href='mailto:artanis.00@gmail.com'>Artanis</a>'s Link to Post Extension.
|
||||
<br>Further modified by Shish to remove the 7MB local QR generator and replace it with a link to Google Chart APIs";
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ class Ratings extends Extension
|
|||
$options[$rating->name] = $rating->code;
|
||||
}
|
||||
|
||||
$sb = $event->panel->create_new_block("Post Ratings");
|
||||
$sb = $event->panel->create_new_block("Post Rating Visibility");
|
||||
$sb->start_table();
|
||||
foreach (array_keys($_shm_user_classes) as $key) {
|
||||
if ($key == "base" || $key == "hellbanned") {
|
||||
|
|
|
@ -101,6 +101,14 @@ class Rule34 extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onRobotsBuilding(RobotsBuildingEvent $event)
|
||||
{
|
||||
// robots should only check the canonical site, not mirrors
|
||||
if ($_SERVER['HTTP_HOST'] != "rule34.paheal.net") {
|
||||
$event->add_disallow("");
|
||||
}
|
||||
}
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
global $database, $page, $user;
|
||||
|
|
|
@ -51,6 +51,11 @@ class SourceHistory extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onRobotsBuilding(RobotsBuildingEvent $event)
|
||||
{
|
||||
$event->add_disallow("source_history");
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||
{
|
||||
$event->add_part("
|
||||
|
|
|
@ -4,11 +4,34 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shimmie2;
|
||||
|
||||
class RobotsBuildingEvent extends Event
|
||||
{
|
||||
public array $parts = [
|
||||
"User-agent: *",
|
||||
// Site is rate limited to 1 request / sec,
|
||||
// returns 503 for more than that
|
||||
"Crawl-delay: 3",
|
||||
];
|
||||
|
||||
public function add_disallow(string $path): void
|
||||
{
|
||||
$this->parts[] = "Disallow: /$path";
|
||||
}
|
||||
}
|
||||
|
||||
class StaticFiles extends Extension
|
||||
{
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
global $config, $page;
|
||||
|
||||
if ($event->page_matches("robots.txt")) {
|
||||
$rbe = send_event(new RobotsBuildingEvent());
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_mime("text/plain");
|
||||
$page->set_data(join("\n", $rbe->parts));
|
||||
}
|
||||
|
||||
// hax.
|
||||
if ($page->mode == PageMode::PAGE && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) {
|
||||
$h_pagename = html_escape(implode('/', $event->args));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
These files can be overriden by placing files in
|
||||
themes/$your_theme/filename.foo. For example if
|
||||
themes/$your_theme/static/filename.foo. For example if
|
||||
you want your theme "radiance" to have a custom
|
||||
favicon, place it in themes/radiance/favicon.ico
|
||||
favicon, place it in themes/radiance/static/favicon.ico
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
User-agent: *
|
||||
# comment lists change all the time, crawlers should
|
||||
# index individual image's comments
|
||||
Disallow: /comment/
|
||||
# next and prev are just CPU-heavier ways of getting
|
||||
# to the same images that the index shows
|
||||
Disallow: /post/next/
|
||||
Disallow: /post/prev/
|
||||
# Site is rate limited to 1 request / sec,
|
||||
# returns 503 for more than that
|
||||
Crawl-delay: 3
|
|
@ -33,7 +33,7 @@ class SourceSetEvent extends Event
|
|||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->source = $source;
|
||||
$this->source = trim($source);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ class TagHistory extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onRobotsBuilding(RobotsBuildingEvent $event)
|
||||
{
|
||||
$event->add_disallow("tag_history");
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||
{
|
||||
$event->add_part("
|
||||
|
|
|
@ -211,7 +211,7 @@ class TagListTheme extends Themelet
|
|||
);
|
||||
$main_html .= " <br><a class='more' href='".make_link("tags")."'>Full List</a>\n";
|
||||
|
||||
$page->add_block(new Block("refine Search", $main_html, "left", 60));
|
||||
$page->add_block(new Block("Refine Search", $main_html, "left", 60));
|
||||
}
|
||||
|
||||
public function return_tag(array $row, array $tag_category_dict): array
|
||||
|
|
|
@ -167,6 +167,18 @@ class TranscodeImage extends Extension
|
|||
{
|
||||
global $config;
|
||||
|
||||
// this onDataUpload happens earlier (or could happen earlier) than handle_pixel.onDataUpload
|
||||
// it mutates the image such that the incorrect mime type is not checked (checking against
|
||||
// the post-transcode mime type instead). This is to give user feedback on what the mime type
|
||||
// was before potential transcoding (the original) at the time of upload, and that it failed if not allowed.
|
||||
// does it break bulk image importing? ZIP? SVG? there are a few flows that are untested!
|
||||
if ($config->get_bool(UploadConfig::MIME_CHECK_ENABLED) == true) {
|
||||
$allowed_mimes = $config->get_array(UploadConfig::ALLOWED_MIME_STRINGS);
|
||||
if (!MimeType::matches_array($event->mime, $allowed_mimes)) {
|
||||
throw new UploadException("MIME type not supported: " . $event->mime);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config->get_bool(TranscodeConfig::UPLOAD) == true) {
|
||||
if ($event->mime === MimeType::GIF&&MimeType::is_animated_gif($event->tmpname)) {
|
||||
return;
|
||||
|
|
|
@ -65,37 +65,37 @@ class TranscodeVideo extends Extension
|
|||
$sb->end_table();
|
||||
}
|
||||
|
||||
/*
|
||||
public function onDataUpload(DataUploadEvent $event)
|
||||
{
|
||||
// global $config;
|
||||
//
|
||||
// if ($config->get_bool(TranscodeVideoConfig::UPLOAD) == true) {
|
||||
// $ext = strtolower($event->type);
|
||||
//
|
||||
// $ext = Media::normalize_format($ext);
|
||||
//
|
||||
// if ($event->type=="gif"&&Media::is_animated_gif($event->tmpname)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (in_array($ext, array_values(self::INPUT_FORMATS))) {
|
||||
// $target_format = $config->get_string(TranscodeVideoConfig::UPLOAD_PREFIX.$ext);
|
||||
// if (empty($target_format)) {
|
||||
// return;
|
||||
// }
|
||||
// try {
|
||||
// $new_image = $this->transcode_image($event->tmpname, $ext, $target_format);
|
||||
// $event->set_tmpname($new_image, Media::determine_ext($target_format));
|
||||
// } catch (Exception $e) {
|
||||
// log_error("transcode_video", "Error while performing upload transcode: ".$e->getMessage());
|
||||
// // We don't want to interfere with the upload process,
|
||||
// // so if something goes wrong the untranscoded image jsut continues
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
global $config;
|
||||
|
||||
if ($config->get_bool(TranscodeVideoConfig::UPLOAD) == true) {
|
||||
$ext = strtolower($event->type);
|
||||
|
||||
$ext = Media::normalize_format($ext);
|
||||
|
||||
if ($event->type=="gif"&&Media::is_animated_gif($event->tmpname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_array($ext, array_values(self::INPUT_FORMATS))) {
|
||||
$target_format = $config->get_string(TranscodeVideoConfig::UPLOAD_PREFIX.$ext);
|
||||
if (empty($target_format)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$new_image = $this->transcode_image($event->tmpname, $ext, $target_format);
|
||||
$event->set_tmpname($new_image, Media::determine_ext($target_format));
|
||||
} catch (Exception $e) {
|
||||
log_error("transcode_video", "Error while performing upload transcode: ".$e->getMessage());
|
||||
// We don't want to interfere with the upload process,
|
||||
// so if something goes wrong the untranscoded image jsut continues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
|
|
|
@ -11,4 +11,6 @@ class UploadConfig
|
|||
public const MIN_FREE_SPACE = "upload_min_free_space";
|
||||
public const TLSOURCE = "upload_tlsource";
|
||||
public const TRANSLOAD_ENGINE = "transload_engine";
|
||||
public const MIME_CHECK_ENABLED = "mime_check_enabled";
|
||||
public const ALLOWED_MIME_STRINGS = "allowed_mime_strings";
|
||||
}
|
||||
|
|
|
@ -98,6 +98,12 @@ class Upload extends Extension
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config->set_default_bool(UploadConfig::MIME_CHECK_ENABLED, false);
|
||||
$config->set_default_array(
|
||||
UploadConfig::ALLOWED_MIME_STRINGS,
|
||||
DataHandlerExtension::get_all_supported_mimes()
|
||||
);
|
||||
}
|
||||
|
||||
public function onSetupBuilding(SetupBuildingEvent $event)
|
||||
|
@ -119,8 +125,21 @@ class Upload extends Extension
|
|||
$sb->add_label("<i>PHP Limit = " . ini_get('upload_max_filesize') . "</i>");
|
||||
$sb->add_choice_option(UploadConfig::TRANSLOAD_ENGINE, $tes, "<br/>Transload: ");
|
||||
$sb->add_bool_option(UploadConfig::TLSOURCE, "<br/>Use transloaded URL as source if none is provided: ");
|
||||
|
||||
$sb->start_table();
|
||||
$sb->add_bool_option(UploadConfig::MIME_CHECK_ENABLED, "Enable upload MIME checks", true);
|
||||
$sb->add_multichoice_option(UploadConfig::ALLOWED_MIME_STRINGS, $this->get_mime_options(), "Allowed MIME uploads", true);
|
||||
$sb->end_table();
|
||||
}
|
||||
|
||||
private function get_mime_options(): array
|
||||
{
|
||||
$output = [];
|
||||
foreach (DataHandlerExtension::get_all_supported_mimes() as $mime) {
|
||||
$output[MimeMap::get_name_for_mime($mime)] = $mime;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function onPageNavBuilding(PageNavBuildingEvent $event)
|
||||
{
|
||||
|
|
|
@ -345,8 +345,8 @@ class UserPage extends Extension
|
|||
$sb->add_choice_option(
|
||||
"user_loginshowprofile",
|
||||
[
|
||||
"return to previous page" => 0, // 0 is default
|
||||
"send to user profile" => 1],
|
||||
"Return to previous page" => 0, // 0 is default
|
||||
"Send to user profile" => 1],
|
||||
"On log in/out",
|
||||
true
|
||||
);
|
||||
|
@ -422,7 +422,7 @@ class UserPage extends Extension
|
|||
if (!$user->can(Permissions::CREATE_USER)) {
|
||||
throw new UserCreationException("Account creation is currently disabled");
|
||||
}
|
||||
if (!$config->get_bool("login_signup_enabled")) {
|
||||
if (!$config->get_bool("login_signup_enabled") && !$user->can(Permissions::CREATE_OTHER_USER)) {
|
||||
throw new UserCreationException("Account creation is currently disabled");
|
||||
}
|
||||
if (strlen($name) < 1) {
|
||||
|
|
|
@ -94,6 +94,14 @@ class ViewImage extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function onRobotsBuilding(RobotsBuildingEvent $event)
|
||||
{
|
||||
// next and prev are just CPU-heavier ways of getting
|
||||
// to the same images that the index shows
|
||||
$event->add_disallow("post/next");
|
||||
$event->add_disallow("post/prev");
|
||||
}
|
||||
|
||||
public function onDisplayingImage(DisplayingImageEvent $event)
|
||||
{
|
||||
global $page, $user;
|
||||
|
|
|
@ -9,7 +9,7 @@ class CustomUploadTheme extends UploadTheme
|
|||
// public function display_block(Page $page) {
|
||||
// $page->add_block(new Block("Upload", $this->build_upload_block(), "left", 20));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public function display_full(Page $page) {
|
||||
// $page->add_block(new Block("Upload", "Disk nearly full, uploads disabled", "left", 20));
|
||||
// }
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
</ul>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://s.zlinkb.com/d.php?z=3832265" target="_blank">[Check out our partners!]</a></li>
|
||||
<li><a href="https://s.zlinkb.com/d.php?z=3832265" target="_blank">Adult games! 🎮</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Loading…
Reference in New Issue