Skip to content

Commit

Permalink
Merge branch 'main' into jdc-thread-offenses
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeCohen committed Feb 21, 2024
2 parents 246707d + ca33c12 commit 575e014
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 81 deletions.
9 changes: 0 additions & 9 deletions app/controllers/account/profile_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,17 @@ def upload_image_if_present
end

def deal_with_possible_profile_changes
# compute legal name change now because @user.save will overwrite it
legal_name_change = @user.legal_name_change
if !@user.changed
flash_notice(:runtime_no_changes.t)
redirect_to(user_path(@user.id))
elsif !@user.save
flash_object_errors(@user)
render(:edit) and return
else
update_copyright_holder(legal_name_change)
maybe_update_location_and_finish
end
end

def update_copyright_holder(legal_name_change = nil)
return unless legal_name_change

Image.update_copyright_holder(*legal_name_change, @user)
end

def maybe_update_location_and_finish
if @need_location
flash_notice(:runtime_profile_must_define.t)
Expand Down
10 changes: 8 additions & 2 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,7 @@ def index
# id:: Warp to page that includes object with this id.
# action:: Template used to render results.
# matrix:: Displaying results as matrix?
# cache:: Cache the HTML of the results?
# letters:: Paginating by letter?
# letter_arg:: Param used to store letter for pagination.
# number_arg:: Param used to store page number for pagination.
Expand Down Expand Up @@ -1470,6 +1471,11 @@ def skip_if_coming_back(query, args)
end
end

# NOTE: there are two places where cache args have to be sent to enable
# efficient caching. Sending `cache: true` here to `show_index_of_objects`
# allows us to optimize eager-loading, doing it only for records not cached.
# (The other place is from the template to the `matrix_box` helper, which
# actually caches the HTML.)
def find_objects(query, args)
caching = args[:cache] || false
include = args[:include] || nil
Expand Down Expand Up @@ -1510,11 +1516,11 @@ def objects_with_only_needed_eager_loads(query, include)
# Check if a cached partial exists for this object.
# digest_path_from_template from ActionView::Helpers::CacheHelper :nodoc:
# https://stackoverflow.com/a/77862353/3357635
def object_fragment_exist?(obj, user, locale)
def object_fragment_exist?(obj, locale)
template = lookup_context.find(action_name, lookup_context.prefixes)
digest_path = helpers.digest_path_from_template(template)

fragment_exist?([digest_path, obj, user, locale])
fragment_exist?([digest_path, obj, locale])
end

def show_index_render(args)
Expand Down
18 changes: 9 additions & 9 deletions app/helpers/matrix_box_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ def render_cached_matrix_boxes(objects, locals)
# matrix box has one version except langs.
# css hides image vote ui when body.no-user
objects.each do |object|
# cache(object) do
concat(render(partial: "shared/matrix_box",
locals: { object: object }.merge(locals)))
# end
cache(object) do
concat(render(partial: "shared/matrix_box",
locals: { object: object }.merge(locals)))
end
end
end

Expand Down Expand Up @@ -79,23 +79,23 @@ def matrix_box_image(image = nil, **args)
# end

# NOTE: object_id may be "no_ID" for logs of deleted records
def matrix_box_details(presenter, object, object_id, identify)
def matrix_box_details(presenter, object_id, identify)
tag.div(class: "panel-body rss-box-details") do
[
matrix_box_what(presenter, object, object_id, identify),
matrix_box_what(presenter, object_id, identify),
matrix_box_where(presenter),
matrix_box_when_who(presenter)
].safe_join
end
end

def matrix_box_what(presenter, object, object_id, identify)
def matrix_box_what(presenter, object_id, identify)
# heading style: bigger if no image.
# TODO: make box layouts specific to object type
h_style = presenter.image_data ? "h5" : "h3"
what = presenter.what
what = presenter.what # for an obs or rss_log, it's the obs
consensus = presenter.consensus || nil
identify_ui = matrix_box_vote_or_propose_ui(identify, object, consensus)
identify_ui = matrix_box_vote_or_propose_ui(identify, what, consensus)

tag.div(class: "rss-what") do
[
Expand Down
10 changes: 8 additions & 2 deletions app/models/location.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
# notify_users:: After save: send email notification.
#
################################################################################
#
# rubocop:disable Metrics/ClassLength
class Location < AbstractModel
require "acts_as_versioned"

Expand Down Expand Up @@ -199,8 +199,13 @@ class Location < AbstractModel
}

# Let attached observations update their cache if these fields changed.
# Also touch updated_at to expire obs fragment caches
def update_observation_cache
Observation.update_cache("location", "where", id, name) if name_changed?
return unless name_changed?

Observation.where(location_id: id).update_all(
{ where: name, updated_at: Time.zone.now }
)
end

##############################################################################
Expand Down Expand Up @@ -847,3 +852,4 @@ def check_requirements
end
end
end
# rubocop:enable Metrics/ClassLength
43 changes: 30 additions & 13 deletions app/models/name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,6 @@ class Name < AbstractModel
"locked"
)

before_create :inherit_stuff
before_update :update_observation_cache
after_update :notify_users

validates :icn_id, numericality: { allow_nil: true,
only_integer: true,
greater_than_or_equal_to: 1 }
Expand All @@ -436,15 +432,11 @@ class Name < AbstractModel
validate :author_ending
validate :citation_start

# Notify webmaster that a new name was created.
after_create do |name|
user = User.current || User.admin
QueuedEmail::Webmaster.create_email(
sender_email: user.email,
subject: "#{user.login} created #{name.real_text_name}",
content: "#{MO.http_domain}/names/#{name.id}"
)
end
before_create :inherit_stuff
after_create :notify_webmaster

before_update :update_observation_cache
after_update :notify_users

# Used by name/_form_name.rhtml
attr_accessor :misspelling
Expand Down Expand Up @@ -685,6 +677,31 @@ class Name < AbstractModel
)
}

# This is called before a name is created to let us populate things like
# classification and lifeform from the parent (if infrageneric only).
def inherit_stuff
return unless accepted_genus

self.classification ||= accepted_genus.classification
self.lifeform ||= accepted_genus.lifeform
end

# Let attached observations update their cache if these fields changed.
# Also, `touch` if it changes the obs name and should invalidate HTML
# caches of the observation.
def update_observation_cache
touch_cases = text_name_changed? || author_changed? || deprecated_changed?
no_touch_cases = lifeform_changed? || classification_changed?
return unless touch_cases || no_touch_cases

updates = {}
updates[:updated_at] = Time.zone.now if touch_cases && !no_touch_cases
updates[:lifeform] = lifeform if lifeform_changed?
updates[:text_name] = text_name if text_name_changed?
updates[:classification] = classification if classification_changed?
Observation.where(name_id: id).update_all(updates) if updates.present?
end

def <=>(other)
sort_name <=> other.sort_name
end
Expand Down
59 changes: 44 additions & 15 deletions app/models/name/notify.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# frozen_string_literal: true

module Name::Notify
# Notify webmaster that a new name was created.
def notify_webmaster
user = User.current || User.admin
QueuedEmail::Webmaster.create_email(
sender_email: user.email,
subject: "#{user.login} created #{real_text_name}",
content: "#{MO.http_domain}/names/#{id}"
)
end

# This is called after saving potential changes to a Name. It will determine
# if the changes are important enough to notify the authors, and do so.
def notify_users
Expand All @@ -9,53 +19,72 @@ def notify_users
sender = User.current
recipients = []

# Tell admins of the change.
notify_admins(recipients)
notify_authors(recipients)
notify_editors(recipients)
notify_reviewers(recipients)
notify_masochists(recipients)
notify_interested(recipients)

# Remove users who have opted out of all emails.
recipients.reject!(&:no_emails)

# Send notification to all except the person who triggered the change.
(recipients.uniq - [sender]).each do |recipient|
QueuedEmail::NameChange.create_email(sender, recipient, self, nil, false)
end
end

# Tell admins of the change.
def notify_admins(recipients)
descriptions.map(&:admins).each do |user_list|
user_list.each do |user|
recipients.push(user) if user.email_names_admin
end
end
end

# Tell authors of the change.
# Tell authors of the change.
def notify_authors(recipients)
descriptions.map(&:authors).each do |user_list|
user_list.each do |user|
recipients.push(user) if user.email_names_author
end
end
end

# Tell editors of the change.
# Tell editors of the change.
def notify_editors(recipients)
descriptions.map(&:editors).each do |user_list|
user_list.each do |user|
recipients.push(user) if user.email_names_editor
end
end
end

# Tell reviewers of the change.
# Tell reviewers of the change.
def notify_reviewers(recipients)
descriptions.map(&:reviewer).each do |user|
recipients.push(user) if user&.email_names_reviewer
end
end

# Tell masochists who want to know about all name changes.
# Tell masochists who want to know about all name changes.
def notify_masochists(recipients)
User.where(email_names_all: true).find_each do |user|
recipients.push(user)
end
end

# Send to people who have registered interest.
# Also remove everyone who has explicitly said they are NOT interested.
# Send to people who have registered interest.
# Also remove everyone who has explicitly said they are NOT interested.
def notify_interested(recipients)
interests.each do |interest|
if interest.state
recipients.push(interest.user)
else
recipients.delete(interest.user)
end
end

# Remove users who have opted out of all emails.
recipients.reject!(&:no_emails)

# Send notification to all except the person who triggered the change.
(recipients.uniq - [sender]).each do |recipient|
QueuedEmail::NameChange.create_email(sender, recipient, self, nil, false)
end
end
end
19 changes: 0 additions & 19 deletions app/models/name/taxonomy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -297,25 +297,6 @@ def has_notes?
notes&.match(/\S/)
end

# This is called before a name is created to let us populate things like
# classification and lifeform from the parent (if infrageneric only).
def inherit_stuff
return unless accepted_genus

self.classification ||= accepted_genus.classification
self.lifeform ||= accepted_genus.lifeform
end

# Let attached observations update their cache if these fields changed.
def update_observation_cache
Observation.update_cache("name", "lifeform", id, lifeform) \
if lifeform_changed?
Observation.update_cache("name", "text_name", id, text_name) \
if text_name_changed?
Observation.update_cache("name", "classification", id, classification) \
if classification_changed?
end

# Copy classification from parent. Just take parent's classification string
# and add the parent's name to the bottom of it. Nice and easy.
def inherit_classification(parent)
Expand Down
6 changes: 0 additions & 6 deletions app/models/observation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -610,12 +610,6 @@ def self.refresh_cached_column(type: nil, foreign: nil, local: foreign,
msgs
end

# Used by Name and Location to update the observation cache when a cached
# field value is changed.
def self.update_cache(type, field, id, val)
Observation.where("#{type}_id": id).update_all("#{field}": val)
end

# Check for any observations whose consensus is a misspelled name. This can
# mess up the mirrors because misspelled names are "invisible", so their
# classification and lifeform and such will not necessarily be kept up to
Expand Down
21 changes: 20 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
# crypt_password:: Password attribute is encrypted
# before object is created.
#
class User < AbstractModel
class User < AbstractModel # rubocop:disable Metrics/ClassLength
require "digest/sha1"

# enum definitions for use by simple_enum gem
Expand Down Expand Up @@ -329,6 +329,9 @@ class User < AbstractModel
# go through +change_password+.)
before_create :crypt_password

before_update :update_image_copyright_holder
before_update :expire_caches_of_associated_observations

# Ensure that certain default values are symbols (rather than strings)
# might only be an issue for test environment?
# Probably better to instead use after_create and after_update,
Expand Down Expand Up @@ -484,6 +487,22 @@ def legal_name_change
[old_legal_name, new_legal_name]
end

# TODO: Move this to an ActiveJob, once we get jobs going - AN 20240220
# This can be fairly expensive if the user has a lot of images
def update_image_copyright_holder
return unless legal_name_change

Image.update_copyright_holder(*legal_name_change, self)
end

# EXPIRE CACHES OF OBS WITH CHANGED USER LOGINS
# "touch" the updated_at column of all observations with the changed login
def expire_caches_of_associated_observations
return unless name_changed? || login_changed?

Observation.where(user_id: id).touch_all
end

##############################################################################
#
# :section: Authentication
Expand Down
2 changes: 1 addition & 1 deletion app/views/controllers/shared/_matrix_box.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if presenter
[
# Helper Dependency Updated: Jan 14, 2024 at 3am
matrix_box_image(image, **image_args),
matrix_box_details(presenter, object, object_id, identify),
matrix_box_details(presenter, object_id, identify),
].safe_join
end,
matrix_box_log_footer(presenter),
Expand Down
Loading

0 comments on commit 575e014

Please sign in to comment.