PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/app/controllers/tag_set_nominations_controller.rb

https://github.com/sarken/otwarchive
Ruby | 365 lines | 332 code | 19 blank | 14 comment | 36 complexity | 2fa62dc0e5625ec48ba851e69a101da4 MD5 | raw file
  1. class TagSetNominationsController < ApplicationController
  2. cache_sweeper :tag_set_sweeper
  3. before_action :users_only
  4. before_action :load_tag_set, except: [ :index ]
  5. before_action :check_pseud_ownership, only: [:create, :update]
  6. before_action :load_nomination, only: [:show, :edit, :update, :destroy, :confirm_delete]
  7. before_action :set_limit, only: [:new, :edit, :show, :create, :update, :review]
  8. def check_pseud_ownership
  9. if !tag_set_nomination_params[:pseud_id].blank?
  10. pseud = Pseud.find(tag_set_nomination_params[:pseud_id])
  11. unless pseud && current_user && current_user.pseuds.include?(pseud)
  12. flash[:error] = ts("You can't nominate tags with that pseud.")
  13. redirect_to root_path and return
  14. end
  15. end
  16. end
  17. def load_tag_set
  18. @tag_set = OwnedTagSet.find_by_id(params[:tag_set_id])
  19. unless @tag_set
  20. flash[:error] = ts("What tag set did you want to nominate for?")
  21. redirect_to tag_sets_path and return
  22. end
  23. end
  24. def load_nomination
  25. @tag_set_nomination = @tag_set.tag_set_nominations.find_by(id: params[:id])
  26. unless @tag_set_nomination
  27. flash[:error] = ts("Which nominations did you want to work with?")
  28. redirect_to tag_set_path(@tag_set) and return
  29. end
  30. unless current_user.is_author_of?(@tag_set_nomination) || @tag_set.user_is_moderator?(current_user)
  31. flash[:error] = ts("You can only see your own nominations or nominations for a set you moderate.")
  32. redirect_to tag_set_path(@tag_set) and return
  33. end
  34. end
  35. def set_limit
  36. @limit = @tag_set.limits
  37. end
  38. # used in new/edit to build any nominations that don't already exist before we open the form
  39. def build_nominations
  40. @limit[:fandom].times do |i|
  41. fandom_nom = @tag_set_nomination.fandom_nominations[i] || @tag_set_nomination.fandom_nominations.build
  42. @limit[:character].times {|j| fandom_nom.character_nominations[j] || fandom_nom.character_nominations.build }
  43. @limit[:relationship].times {|j| fandom_nom.relationship_nominations[j] || fandom_nom.relationship_nominations.build }
  44. end
  45. if @limit[:fandom] == 0
  46. @limit[:character].times {|j| @tag_set_nomination.character_nominations[j] || @tag_set_nomination.character_nominations.build }
  47. @limit[:relationship].times {|j| @tag_set_nomination.relationship_nominations[j] || @tag_set_nomination.relationship_nominations.build }
  48. end
  49. @limit[:freeform].times {|i| @tag_set_nomination.freeform_nominations[i] || @tag_set_nomination.freeform_nominations.build }
  50. end
  51. def index
  52. if params[:user_id]
  53. @user = User.find_by(login: params[:user_id])
  54. if @user != current_user
  55. flash[:error] = ts("You can only view your own nominations, sorry.")
  56. redirect_to tag_sets_path and return
  57. else
  58. @tag_set_nominations = TagSetNomination.owned_by(@user)
  59. end
  60. elsif (@tag_set = OwnedTagSet.find_by_id(params[:tag_set_id]))
  61. if @tag_set.user_is_moderator?(current_user)
  62. # reviewing nominations
  63. setup_for_review
  64. else
  65. flash[:error] = ts("You can't see those nominations, sorry.")
  66. redirect_to tag_sets_path and return
  67. end
  68. else
  69. flash[:error] = ts("What nominations did you want to work with?")
  70. redirect_to tag_sets_path and return
  71. end
  72. end
  73. def show
  74. end
  75. def new
  76. if @tag_set_nomination = TagSetNomination.for_tag_set(@tag_set).owned_by(current_user).first
  77. redirect_to edit_tag_set_nomination_path(@tag_set, @tag_set_nomination)
  78. else
  79. @tag_set_nomination = TagSetNomination.new(pseud: current_user.default_pseud, owned_tag_set: @tag_set)
  80. build_nominations
  81. end
  82. end
  83. def edit
  84. # build up extra nominations if not all were used
  85. build_nominations
  86. end
  87. def create
  88. @tag_set_nomination = @tag_set.tag_set_nominations.build(tag_set_nomination_params)
  89. if @tag_set_nomination.save
  90. flash[:notice] = ts('Your nominations were successfully submitted.')
  91. redirect_to tag_set_nomination_path(@tag_set, @tag_set_nomination)
  92. else
  93. build_nominations
  94. render action: "new"
  95. end
  96. end
  97. def update
  98. if @tag_set_nomination.update(tag_set_nomination_params)
  99. flash[:notice] = ts("Your nominations were successfully updated.")
  100. redirect_to tag_set_nomination_path(@tag_set, @tag_set_nomination)
  101. else
  102. build_nominations
  103. render action: "edit"
  104. end
  105. end
  106. def destroy
  107. unless @tag_set_nomination.unreviewed? || @tag_set.user_is_moderator?(current_user)
  108. flash[:error] = ts("You cannot delete nominations after some of them have been reviewed, sorry!")
  109. redirect_to tag_set_nomination_path(@tag_set, @tag_set_nomination)
  110. else
  111. @tag_set_nomination.destroy
  112. flash[:notice] = ts("Your nominations were deleted.")
  113. redirect_to tag_set_path(@tag_set)
  114. end
  115. end
  116. def base_nom_query(tag_type)
  117. TagNomination.where(type: (tag_type.is_a?(Array) ? tag_type.map {|t| "#{t.classify}Nomination"} : "#{tag_type.classify}Nomination")).
  118. for_tag_set(@tag_set).unreviewed.limit(@nom_limit)
  119. end
  120. # set up various variables for reviewing nominations
  121. def setup_for_review
  122. set_limit
  123. @nom_limit = 30
  124. @nominations = HashWithIndifferentAccess.new
  125. @nominations_count = HashWithIndifferentAccess.new
  126. more_noms = false
  127. if @tag_set.includes_fandoms?
  128. # all char and rel tags happen under fandom noms
  129. @nominations_count[:fandom] = @tag_set.fandom_nominations.unreviewed.count
  130. more_noms = true if @nominations_count[:fandom] > @nom_limit
  131. @nominations[:fandom] = more_noms ? base_nom_query("fandom").random_order : base_nom_query("fandom").order(:tagname)
  132. if (@limit[:character] > 0 || @limit[:relationship] > 0)
  133. @nominations[:cast] = base_nom_query(%w(character relationship)).
  134. join_fandom_nomination.
  135. where('fandom_nominations_tag_nominations.approved = 1').
  136. order(:parent_tagname, :type, :tagname)
  137. end
  138. else
  139. # if there are no fandoms we're going to assume this is a one or few fandom tagset
  140. @nominations_count[:character] = @tag_set.character_nominations.unreviewed.count
  141. @nominations_count[:relationship] = @tag_set.relationship_nominations.unreviewed.count
  142. more_noms = true if (@tag_set.character_nominations.unreviewed.count > @nom_limit || @tag_set.relationship_nominations.unreviewed.count > @nom_limit)
  143. @nominations[:character] = base_nom_query("character") if @limit[:character] > 0
  144. @nominations[:relationship] = base_nom_query("relationship") if @limit[:relationship] > 0
  145. if more_noms
  146. parent_tagnames = TagNomination.for_tag_set(@tag_set).unreviewed.random_order.limit(100).pluck(:parent_tagname).uniq.first(30)
  147. @nominations[:character] = @nominations[:character].where(parent_tagname: parent_tagnames) if @limit[:character] > 0
  148. @nominations[:relationship] = @nominations[:relationship].where(parent_tagname: parent_tagnames) if @limit[:relationship] > 0
  149. end
  150. @nominations[:character] = @nominations[:character].order(:parent_tagname, :tagname) if @limit[:character] > 0
  151. @nominations[:relationship] = @nominations[:relationship].order(:parent_tagname, :tagname) if @limit[:relationship] > 0
  152. end
  153. @nominations_count[:freeform] = @tag_set.freeform_nominations.unreviewed.count
  154. more_noms = true if @nominations_count[:freeform] > @nom_limit
  155. @nominations[:freeform] = (more_noms ? base_nom_query("freeform").random_order : base_nom_query("freeform").order(:tagname)) unless @limit[:freeform].zero?
  156. if more_noms
  157. flash[:notice] = ts("There are too many nominations to show at once, so here's a randomized selection! Additional nominations will appear after you approve or reject some.")
  158. end
  159. if @tag_set.tag_nominations.unreviewed.empty?
  160. flash[:notice] = ts("No nominations to review!")
  161. end
  162. end
  163. def confirm_delete
  164. end
  165. def confirm_destroy_multiple
  166. end
  167. def destroy_multiple
  168. unless @tag_set.user_is_owner?(current_user)
  169. flash[:error] = ts("You don't have permission to do that.")
  170. redirect_to tag_set_path(@tag_set) and return
  171. end
  172. @tag_set.clear_nominations!
  173. flash[:notice] = ts("All nominations for this Tag Set have been cleared.")
  174. redirect_to tag_set_path(@tag_set)
  175. end
  176. # update_multiple gets called from the index/review form.
  177. # we expect params like "character_approve_My Awesome Tag" and "fandom_reject_My Lousy Tag"
  178. def update_multiple
  179. unless @tag_set.user_is_moderator?(current_user)
  180. flash[:error] = ts("You don't have permission to do that.")
  181. redirect_to tag_set_path(@tag_set) and return
  182. end
  183. # Collate the input into @approve, @reject, @synonym, @change, checking for:
  184. # - invalid tag name changes
  185. # - approve & reject both selected
  186. # put errors in @errors, mark types to force to be expanded with @force_expand
  187. @approve = HashWithIndifferentAccess.new; @synonym = HashWithIndifferentAccess.new
  188. @reject = HashWithIndifferentAccess.new; @change = HashWithIndifferentAccess.new
  189. @errors = []; @force_expand = {}
  190. collect_update_multiple_results
  191. # If we have errors don't move ahead
  192. unless @errors.empty?
  193. render_index_on_error and return
  194. end
  195. # OK, now we're going ahead and making piles of db changes! eep! D:
  196. TagSet::TAG_TYPES_INITIALIZABLE.each do |tag_type|
  197. # we're adding the approved tags and synonyms
  198. @tagnames_to_add = @approve[tag_type] + @synonym[tag_type]
  199. @tagnames_to_remove = @reject[tag_type]
  200. # If we've approved a tag, change any other nominations that have this tag as a synonym to the synonym
  201. if @tagnames_to_add.present?
  202. tagnames_to_change = TagNomination.for_tag_set(@tag_set).where(type: "#{tag_type.classify}Nomination").where("synonym IN (?)", @tagnames_to_add).pluck(:tagname).uniq
  203. tagnames_to_change.each do |oldname|
  204. synonym = TagNomination.for_tag_set(@tag_set).where(type: "#{tag_type.classify}Nomination", tagname: oldname).pluck(:synonym).first
  205. unless TagNomination.change_tagname!(@tag_set, oldname, synonym)
  206. flash[:error] = ts("Oh no! We ran into a problem partway through saving your updates, changing %{oldname} to %{newname} -- please check over your tag set closely!",
  207. oldname: oldname, newname: synonym)
  208. render_index_on_error and return
  209. end
  210. end
  211. end
  212. # do the name changes
  213. @change[tag_type].each do |oldname, newname|
  214. if TagNomination.change_tagname!(@tag_set, oldname, newname)
  215. @tagnames_to_add << newname
  216. else
  217. # ughhhh
  218. flash[:error] = ts("Oh no! We ran into a problem partway through saving your updates, changing %{oldname} to %{newname} -- please check over your tag set closely!",
  219. oldname: oldname, newname: newname)
  220. render_index_on_error and return
  221. end
  222. end
  223. # update the tag set
  224. unless @tag_set.add_tagnames(tag_type, @tagnames_to_add) && @tag_set.remove_tagnames(tag_type, @tagnames_to_remove)
  225. @errors = @tag_set.errors.full_messages
  226. flash[:error] = ts("Oh no! We ran into a problem partway through saving your updates -- please check over your tag set closely!")
  227. render_index_on_error and return
  228. end
  229. @notice ||= []
  230. @notice << ts("Successfully added to set: %{approved}", approved: @tagnames_to_add.join(', ')) unless @tagnames_to_add.empty?
  231. @notice << ts("Successfully rejected: %{rejected}", rejected: @tagnames_to_remove.join(', ')) unless @tagnames_to_remove.empty?
  232. end
  233. # If we got here we made it through, YAY
  234. flash[:notice] = @notice
  235. if @tag_set.tag_nominations.unreviewed.empty?
  236. flash[:notice] << ts("All nominations reviewed, yay!")
  237. redirect_to tag_set_path(@tag_set)
  238. else
  239. flash[:notice] << ts("Still some nominations left to review!")
  240. redirect_to tag_set_nominations_path(@tag_set) and return
  241. end
  242. end
  243. protected
  244. def render_index_on_error
  245. setup_for_review
  246. render action: "index"
  247. end
  248. # gathers up the data for all the tag types
  249. def collect_update_multiple_results
  250. TagSet::TAG_TYPES_INITIALIZABLE.each do |tag_type|
  251. @approve[tag_type] = []
  252. @synonym[tag_type] = []
  253. @reject[tag_type] = []
  254. @change[tag_type] = []
  255. end
  256. params.each_pair do |key, val|
  257. next unless val.present?
  258. if key.match(/^([a-z]+)_(approve|reject|synonym|change)_(.*)$/)
  259. type = $1
  260. action = $2
  261. name = $3
  262. # fix back the tagname if it has [] brackets -- see _review_individual_nom for details
  263. name = name.gsub('#LBRACKET', '[').gsub('#RBRACKET', ']')
  264. if TagSet::TAG_TYPES_INITIALIZABLE.include?(type)
  265. # we're safe
  266. case action
  267. when "reject"
  268. @reject[type] << name
  269. when "approve"
  270. @approve[type] << name unless params["#{type}_change_#{name}"].present? && (params["#{type}_change_#{name}"] != name)
  271. when "synonym", "change"
  272. next if val == name
  273. # this is the tricky one: make sure we can do this name change
  274. tagnom = TagNomination.for_tag_set(@tag_set).where(type: "#{type.classify}Nomination", tagname: name).first
  275. if !tagnom
  276. @errors << ts("Couldn't find a #{type} nomination for #{name}")
  277. @force_expand[type] = true
  278. elsif !tagnom.change_tagname?(val)
  279. @errors << ts("Invalid name change for #{name} to #{val}: %{msg}", msg: tagnom.errors.full_messages.join(', '))
  280. @force_expand[type] = true
  281. elsif action == "synonym"
  282. @synonym[type] << val
  283. else
  284. @change[type] << [name, val]
  285. end
  286. end
  287. end
  288. end
  289. end
  290. TagSet::TAG_TYPES_INITIALIZABLE.each do |tag_type|
  291. unless (intersect = @approve[tag_type] & @reject[tag_type]).empty?
  292. @errors << ts("You have both approved and rejected the following %{type} tags: %{intersect}", type: tag_type, intersect: intersect.join(", "))
  293. @force_expand[tag_type] = true
  294. end
  295. end
  296. end
  297. private
  298. def tag_set_nomination_params
  299. params.require(:tag_set_nomination).permit(
  300. :pseud_id,
  301. fandom_nominations_attributes: [
  302. :id,
  303. :tagname,
  304. character_nominations_attributes: [
  305. :id, :tagname, :from_fandom_nomination
  306. ],
  307. relationship_nominations_attributes: [
  308. :id, :tagname, :from_fandom_nomination
  309. ],
  310. ],
  311. freeform_nominations_attributes: [
  312. :id, :tagname
  313. ],
  314. character_nominations_attributes: [
  315. :id, :tagname, :parent_tagname
  316. ],
  317. relationship_nominations_attributes: [
  318. :id, :tagname, :parent_tagname
  319. ]
  320. )
  321. end
  322. end