PageRenderTime 44ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/tag_edit/main.php

https://gitlab.com/dali99/shimmie2-Material-Theme
PHP | 371 lines | 208 code | 44 blank | 119 comment | 50 complexity | a4fdf970cf9890afe48cb43a51e27181 MD5 | raw file
  1. <?php
  2. /*
  3. * Name: Tag Editor
  4. * Author: Shish
  5. * Description: Allow images to have tags assigned to them
  6. * Documentation:
  7. * Here is a list of the tagging metatags available out of the box;
  8. * Shimmie extensions may provide other metatags:
  9. * <ul>
  10. * <li>source=(*, none) eg -- using this metatag will ignore anything set in the "Source" box
  11. * <ul>
  12. * <li>source=http://example.com -- set source to http://example.com
  13. * <li>source=none -- set source to NULL
  14. * </ul>
  15. * </ul>
  16. * <p>Metatags can be followed by ":" rather than "=" if you prefer.
  17. * <br />I.E: "source:http://example.com", "source=http://example.com" etc.
  18. * <p>Some tagging metatags provided by extensions:
  19. * <ul>
  20. * <li>Numeric Score
  21. * <ul>
  22. * <li>vote=(up, down, remove) -- vote, or remove your vote on an image
  23. * </ul>
  24. * <li>Pools
  25. * <ul>
  26. * <li>pool=(PoolID, PoolTitle, lastcreated) -- add post to pool (if exists)
  27. * <li>pool=(PoolID, PoolTitle, lastcreated):(PoolOrder) -- add post to pool (if exists) with set pool order
  28. * <ul>
  29. * <li>pool=50 -- add post to pool with ID of 50
  30. * <li>pool=10:25 -- add post to pool with ID of 10 and with order 25
  31. * <li>pool=This_is_a_Pool -- add post to pool with a title of "This is a Pool"
  32. * <li>pool=lastcreated -- add post to the last pool the user created
  33. * </ul>
  34. * </ul>
  35. * <li>Post Relationships
  36. * <ul>
  37. * <li>parent=(parentID, none) -- set parent ID of current image
  38. * <li>child=(childID) -- set parent ID of child image to current image ID
  39. * </ul>
  40. * </ul>
  41. */
  42. /*
  43. * OwnerSetEvent:
  44. * $image_id
  45. * $source
  46. *
  47. */
  48. class OwnerSetEvent extends Event {
  49. /** @var \Image */
  50. public $image;
  51. /** @var \User */
  52. public $owner;
  53. /**
  54. * @param Image $image
  55. * @param User $owner
  56. */
  57. public function __construct(Image $image, User $owner) {
  58. $this->image = $image;
  59. $this->owner = $owner;
  60. }
  61. }
  62. /*
  63. * SourceSetEvent:
  64. * $image_id
  65. * $source
  66. *
  67. */
  68. class SourceSetEvent extends Event {
  69. /** @var \Image */
  70. public $image;
  71. /** @var string */
  72. public $source;
  73. /**
  74. * @param Image $image
  75. * @param string $source
  76. */
  77. public function __construct(Image $image, $source) {
  78. $this->image = $image;
  79. $this->source = $source;
  80. }
  81. }
  82. /*
  83. * TagSetEvent:
  84. * $image_id
  85. * $tags
  86. *
  87. */
  88. class TagSetEvent extends Event {
  89. /** @var \Image */
  90. public $image;
  91. var $tags;
  92. public function __construct(Image $image, $tags) {
  93. $this->image = $image;
  94. $this->tags = Tag::explode($tags);
  95. }
  96. }
  97. /*
  98. * LockSetEvent:
  99. * $image_id
  100. * $locked
  101. *
  102. */
  103. class LockSetEvent extends Event {
  104. /** @var \Image */
  105. public $image;
  106. /** @var bool */
  107. public $locked;
  108. /**
  109. * @param Image $image
  110. * @param bool $locked
  111. */
  112. public function __construct(Image $image, $locked) {
  113. assert(is_bool($locked));
  114. $this->image = $image;
  115. $this->locked = $locked;
  116. }
  117. }
  118. /*
  119. * TagTermParseEvent:
  120. * Signal that a tag term needs parsing
  121. */
  122. class TagTermParseEvent extends Event {
  123. var $term = null;
  124. var $id = null;
  125. /** @var bool */
  126. public $metatag = false;
  127. public function __construct($term, $id) {
  128. $this->term = $term;
  129. $this->id = $id;
  130. }
  131. /**
  132. * @return bool
  133. */
  134. public function is_metatag() {
  135. return $this->metatag;
  136. }
  137. }
  138. class TagEdit extends Extension {
  139. public function onPageRequest(PageRequestEvent $event) {
  140. global $user, $page;
  141. if($event->page_matches("tag_edit")) {
  142. if($event->get_arg(0) == "replace") {
  143. if($user->can("mass_tag_edit") && isset($_POST['search']) && isset($_POST['replace'])) {
  144. $search = $_POST['search'];
  145. $replace = $_POST['replace'];
  146. $this->mass_tag_edit($search, $replace);
  147. $page->set_mode("redirect");
  148. $page->set_redirect(make_link("admin"));
  149. }
  150. }
  151. if($event->get_arg(0) == "mass_source_set") {
  152. if($user->can("mass_tag_edit") && isset($_POST['tags']) && isset($_POST['source'])) {
  153. $this->mass_source_edit($_POST['tags'], $_POST['source']);
  154. $page->set_mode("redirect");
  155. $page->set_redirect(make_link("post/list"));
  156. }
  157. }
  158. }
  159. }
  160. public function onPostListBuilding(PostListBuildingEvent $event) {
  161. global $user;
  162. if($user->can("bulk_edit_image_source") && !empty($event->search_terms)) {
  163. $event->add_control($this->theme->mss_html(implode(" ", $event->search_terms)));
  164. }
  165. }
  166. public function onImageInfoSet(ImageInfoSetEvent $event) {
  167. global $user;
  168. if($user->can("edit_image_owner")) {
  169. $owner = User::by_name($_POST['tag_edit__owner']);
  170. if ($owner instanceof User) {
  171. send_event(new OwnerSetEvent($event->image, $owner));
  172. } else {
  173. throw new NullUserException("Error: No user with that name was found.");
  174. }
  175. }
  176. if($this->can_tag($event->image) && isset($_POST['tag_edit__tags'])) {
  177. send_event(new TagSetEvent($event->image, $_POST['tag_edit__tags']));
  178. }
  179. if($this->can_source($event->image) && isset($_POST['tag_edit__source'])) {
  180. if(isset($_POST['tag_edit__tags']) ? !preg_match('/source[=|:]/', $_POST["tag_edit__tags"]) : TRUE){
  181. send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source']));
  182. }
  183. }
  184. if($user->can("edit_image_lock")) {
  185. $locked = isset($_POST['tag_edit__locked']) && $_POST['tag_edit__locked']=="on";
  186. send_event(new LockSetEvent($event->image, $locked));
  187. }
  188. }
  189. public function onOwnerSet(OwnerSetEvent $event) {
  190. global $user;
  191. if($user->can("edit_image_owner") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) {
  192. $event->image->set_owner($event->owner);
  193. }
  194. }
  195. public function onTagSet(TagSetEvent $event) {
  196. global $user;
  197. if($user->can("edit_image_tag") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) {
  198. $event->image->set_tags($event->tags);
  199. }
  200. }
  201. public function onSourceSet(SourceSetEvent $event) {
  202. global $user;
  203. if($user->can("edit_image_source") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) {
  204. $event->image->set_source($event->source);
  205. }
  206. }
  207. public function onLockSet(LockSetEvent $event) {
  208. global $user;
  209. if($user->can("edit_image_lock")) {
  210. $event->image->set_locked($event->locked);
  211. }
  212. }
  213. public function onImageDeletion(ImageDeletionEvent $event) {
  214. $event->image->delete_tags_from_image();
  215. }
  216. public function onAdminBuilding(AdminBuildingEvent $event) {
  217. $this->theme->display_mass_editor();
  218. }
  219. /**
  220. * When an alias is added, oldtag becomes inaccessible.
  221. * @param AddAliasEvent $event
  222. */
  223. public function onAddAlias(AddAliasEvent $event) {
  224. $this->mass_tag_edit($event->oldtag, $event->newtag);
  225. }
  226. public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) {
  227. $event->add_part($this->theme->get_user_editor_html($event->image), 39);
  228. $event->add_part($this->theme->get_tag_editor_html($event->image), 40);
  229. $event->add_part($this->theme->get_source_editor_html($event->image), 41);
  230. $event->add_part($this->theme->get_lock_editor_html($event->image), 42);
  231. }
  232. public function onTagTermParse(TagTermParseEvent $event) {
  233. $matches = array();
  234. if(preg_match("/^source[=|:](.*)$/i", $event->term, $matches)) {
  235. $source = ($matches[1] !== "none" ? $matches[1] : null);
  236. send_event(new SourceSetEvent(Image::by_id($event->id), $source));
  237. }
  238. if(!empty($matches)) $event->metatag = true;
  239. }
  240. /**
  241. * @param Image $image
  242. * @return bool
  243. */
  244. private function can_tag(Image $image) {
  245. global $user;
  246. return ($user->can("edit_image_tag") || !$image->is_locked());
  247. }
  248. /**
  249. * @param Image $image
  250. * @return bool
  251. */
  252. private function can_source(Image $image) {
  253. global $user;
  254. return ($user->can("edit_image_source") || !$image->is_locked());
  255. }
  256. /**
  257. * @param string $search
  258. * @param string $replace
  259. */
  260. private function mass_tag_edit($search, $replace) {
  261. global $database;
  262. $search_set = Tag::explode(strtolower($search), false);
  263. $replace_set = Tag::explode(strtolower($replace), false);
  264. log_info("tag_edit", "Mass editing tags: '$search' -> '$replace'");
  265. if(count($search_set) == 1 && count($replace_set) == 1) {
  266. $images = Image::find_images(0, 10, $replace_set);
  267. if(count($images) == 0) {
  268. log_info("tag_edit", "No images found with target tag, doing in-place rename");
  269. $database->execute("DELETE FROM tags WHERE tag=:replace",
  270. array("replace" => $replace_set[0]));
  271. $database->execute("UPDATE tags SET tag=:replace WHERE tag=:search",
  272. array("replace" => $replace_set[0], "search" => $search_set[0]));
  273. return;
  274. }
  275. }
  276. $last_id = -1;
  277. while(true) {
  278. // make sure we don't look at the same images twice.
  279. // search returns high-ids first, so we want to look
  280. // at images with lower IDs than the previous.
  281. $search_forward = $search_set;
  282. $search_forward[] = "order=id_desc"; //Default order can be changed, so make sure we order high > low ID
  283. if($last_id >= 0){
  284. $search_forward[] = "id<$last_id";
  285. }
  286. $images = Image::find_images(0, 100, $search_forward);
  287. if(count($images) == 0) break;
  288. foreach($images as $image) {
  289. // remove the search'ed tags
  290. $before = array_map('strtolower', $image->get_tag_array());
  291. $after = array();
  292. foreach($before as $tag) {
  293. if(!in_array($tag, $search_set)) {
  294. $after[] = $tag;
  295. }
  296. }
  297. // add the replace'd tags
  298. foreach($replace_set as $tag) {
  299. $after[] = $tag;
  300. }
  301. $image->set_tags($after);
  302. $last_id = $image->id;
  303. }
  304. }
  305. }
  306. /**
  307. * @param string|string[] $tags
  308. * @param string $source
  309. */
  310. private function mass_source_edit($tags, $source) {
  311. $tags = Tag::explode($tags);
  312. $last_id = -1;
  313. while(true) {
  314. // make sure we don't look at the same images twice.
  315. // search returns high-ids first, so we want to look
  316. // at images with lower IDs than the previous.
  317. $search_forward = $tags;
  318. if($last_id >= 0) $search_forward[] = "id<$last_id";
  319. $images = Image::find_images(0, 100, $search_forward);
  320. if(count($images) == 0) break;
  321. foreach($images as $image) {
  322. $image->set_source($source);
  323. $last_id = $image->id;
  324. }
  325. }
  326. }
  327. }