Skip to content

Commit 5cf05cb

Browse files
committed
Merge branch 'master' into AO3-6799-zoho-url-fields
2 parents 5002320 + fd5046c commit 5cf05cb

37 files changed

+403
-99
lines changed

Gemfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ GEM
411411
netrc (0.11.0)
412412
nio4r (2.7.4)
413413
nkf (0.2.0)
414-
nokogiri (1.18.2)
414+
nokogiri (1.18.3)
415415
mini_portile2 (~> 2.8.2)
416416
racc (~> 1.4)
417417
orm_adapter (0.5.0)

app/controllers/invite_requests_controller.rb

+27-4
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,40 @@ def index
1111
# GET /invite_requests/1
1212
def show
1313
@invite_request = InviteRequest.find_by(email: params[:email])
14-
@position_in_queue = @invite_request.position if @invite_request.present?
15-
unless (request.xml_http_request?) || @invite_request
16-
flash[:error] = "You can search for the email address you signed up with below. If you can't find it, your invitation may have already been emailed to that address; please check your email spam folder as your spam filters may have placed it there."
17-
redirect_to status_invite_requests_path and return
14+
15+
if @invite_request.present?
16+
@position_in_queue = @invite_request.position
17+
else
18+
@invitation = Invitation.unredeemed.from_queue.find_by(invitee_email: params[:email])
1819
end
20+
1921
respond_to do |format|
2022
format.html
2123
format.js
2224
end
2325
end
2426

27+
def resend
28+
@invitation = Invitation.unredeemed.from_queue.find_by(invitee_email: params[:email])
29+
30+
if @invitation.nil?
31+
flash[:error] = t("invite_requests.resend.not_found")
32+
elsif !@invitation.can_resend?
33+
flash[:error] = t("invite_requests.resend.not_yet",
34+
count: ArchiveConfig.HOURS_BEFORE_RESEND_INVITATION)
35+
else
36+
@invitation.send_and_set_date(resend: true)
37+
38+
if @invitation.errors.any?
39+
flash[:error] = @invitation.errors.full_messages.first
40+
else
41+
flash[:notice] = t("invite_requests.resend.success", email: @invitation.invitee_email)
42+
end
43+
end
44+
45+
redirect_to status_invite_requests_path
46+
end
47+
2548
# POST /invite_requests
2649
def create
2750
unless AdminSetting.current.invite_from_queue_enabled?

app/controllers/pseuds_controller.rb

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def new
7474

7575
# GET /pseuds/1/edit
7676
def edit
77+
raise ActiveRecord::RecordNotFound, "Couldn't find user '#{params[:user_id]}'" unless @user
78+
7779
@pseud = @user.pseuds.find_by!(name: params[:id])
7880
authorize @pseud if logged_in_as_admin?
7981
end

app/helpers/tags_helper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def sub_tag_tree(tag)
176176
sub_ul = ""
177177
unless tag.direct_sub_tags.empty?
178178
sub_ul << "<ul class='tags tree index'>"
179-
tag.direct_sub_tags.each do |sub|
179+
tag.direct_sub_tags.order(:name).each do |sub|
180180
sub_ul << "<li>" + link_to_tag(sub)
181181
unless sub.direct_sub_tags.empty?
182182
sub_ul << sub_tag_tree(sub)

app/mailers/user_mailer.rb

+2-4
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,11 @@ def admin_deleted_work_notification(user, work)
345345
html = ::Mail::Encodings::Base64.encode(html)
346346
attachments["#{download.file_name}.html"] = { content: html, encoding: "base64" }
347347
attachments["#{download.file_name}.txt"] = { content: html, encoding: "base64" }
348-
349-
I18n.with_locale(@user.preference.locale.iso) do
350-
mail(
348+
349+
mail(
351350
to: user.email,
352351
subject: t("user_mailer.admin_deleted_work_notification.subject", app_name: ArchiveConfig.APP_SHORT_NAME)
353352
)
354-
end
355353
end
356354

357355
# Sends email to creators when a creation is hidden by an admin

app/models/invitation.rb

+33-21
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ def recipient_is_not_registered
1919
scope :unsent, -> { where(invitee_email: nil, redeemed_at: nil) }
2020
scope :unredeemed, -> { where('invitee_email IS NOT NULL and redeemed_at IS NULL') }
2121
scope :redeemed, -> { where('redeemed_at IS NOT NULL') }
22+
scope :from_queue, -> { where(external_author: nil).where(creator_type: [nil, "Admin"]) }
2223

2324
before_validation :generate_token, on: :create
24-
after_save :send_and_set_date
25+
after_save :send_and_set_date, if: :saved_change_to_invitee_email?
2526
after_save :adjust_user_invite_status
2627

2728
#Create a certain number of invitations for all valid users
@@ -58,30 +59,41 @@ def mark_as_redeemed(user=nil)
5859
save
5960
end
6061

61-
private
62+
def send_and_set_date(resend: false)
63+
return if invitee_email.blank?
6264

63-
def generate_token
64-
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
65+
if self.external_author
66+
archivist = self.external_author.external_creatorships.collect(&:archivist).collect(&:login).uniq.join(", ")
67+
# send invite synchronously for now -- this should now work delayed but just to be safe
68+
UserMailer.invitation_to_claim(self.id, archivist).deliver_now
69+
else
70+
# send invitations actively sent by a user synchronously to avoid delays
71+
UserMailer.invitation(self.id).deliver_now
72+
end
73+
74+
# Skip callbacks within after_save by using update_column to avoid a callback loop
75+
if resend
76+
attrs = { resent_at: Time.current }
77+
# This applies to old invites when AO3-6094 wasn't fixed.
78+
attrs[:sent_at] = self.created_at if self.sent_at.nil?
79+
self.update_columns(attrs)
80+
else
81+
self.update_column(:sent_at, Time.current)
82+
end
83+
rescue StandardError => e
84+
errors.add(:base, :notification_could_not_be_sent, error: e.message)
6585
end
6686

67-
def send_and_set_date
68-
if self.saved_change_to_invitee_email? && !self.invitee_email.blank?
69-
begin
70-
if self.external_author
71-
archivist = self.external_author.external_creatorships.collect(&:archivist).collect(&:login).uniq.join(", ")
72-
# send invite synchronously for now -- this should now work delayed but just to be safe
73-
UserMailer.invitation_to_claim(self.id, archivist).deliver_now
74-
else
75-
# send invitations actively sent by a user synchronously to avoid delays
76-
UserMailer.invitation(self.id).deliver_now
77-
end
87+
def can_resend?
88+
# created_at fallback is a vestige of the already fixed AO3-6094.
89+
checked_date = self.resent_at || self.sent_at || self.created_at
90+
checked_date < ArchiveConfig.HOURS_BEFORE_RESEND_INVITATION.hours.ago
91+
end
7892

79-
# Skip callbacks within after_save by using update_column to avoid a callback loop
80-
self.update_column(:sent_at, Time.now)
81-
rescue Exception => exception
82-
errors.add(:base, "Notification email could not be sent: #{exception.message}")
83-
end
84-
end
93+
private
94+
95+
def generate_token
96+
self.token = Digest::SHA1.hexdigest([Time.current, rand].join)
8597
end
8698

8799
#Update the user's out_of_invites status

app/models/work.rb

+19-14
Original file line numberDiff line numberDiff line change
@@ -250,21 +250,26 @@ def new_recipients_have_not_blocked_gift_giver
250250

251251
before_destroy :send_deleted_work_notification, prepend: true
252252
def send_deleted_work_notification
253-
if self.posted?
254-
users = self.pseuds.collect(&:user).uniq
255-
orphan_account = User.orphan_account
256-
unless users.blank?
257-
for user in users
258-
next if user == orphan_account
259-
# Check to see if this work is being deleted by an Admin
260-
if User.current_user.is_a?(Admin)
261-
# this has to use the synchronous version because the work is going to be destroyed
262-
UserMailer.admin_deleted_work_notification(user, self).deliver_now
263-
else
264-
# this has to use the synchronous version because the work is going to be destroyed
265-
UserMailer.delete_work_notification(user, self).deliver_now
266-
end
253+
return unless self.posted?
254+
255+
users = self.pseuds.collect(&:user).uniq
256+
257+
return if users.blank?
258+
259+
orphan_account = User.orphan_account
260+
261+
users.each do |user|
262+
next if user == orphan_account
263+
264+
# Check to see if this work is being deleted by an Admin
265+
if User.current_user.is_a?(Admin)
266+
# this has to use the synchronous version because the work is going to be destroyed
267+
I18n.with_locale(user.preference.locale.iso) do
268+
UserMailer.admin_deleted_work_notification(user, self).deliver_now
267269
end
270+
else
271+
# this has to use the synchronous version because the work is going to be destroyed
272+
UserMailer.delete_work_notification(user, self).deliver_now
268273
end
269274
end
270275
end

app/views/invitations/_invitation.html.erb

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<dd><%= invitation.created_at %></dd>
2525
<dt>Sent at</dt>
2626
<dd><%= invitation.sent_at %></dd>
27+
<dt>Last resent at</dt>
28+
<dd><%= invitation.resent_at %></dd>
2729
<dt>Redeemed at</dt>
2830
<dd><%= invitation.redeemed_at %></dd>
2931
</dl>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!--Descriptive page name, messages and instructions-->
2+
<h2 class="heading" id="invite-heading">
3+
<%= t(".title", email: invitation.invitee_email) %>
4+
</h2>
5+
<!--/descriptions-->
6+
7+
<!--main content-->
8+
<p>
9+
<% status = invitation.resent_at ? "resent" : "not_resent" %>
10+
<%= t(".info.#{status}",
11+
sent_at: l((invitation.sent_at || invitation.created_at).to_date),
12+
resent_at: invitation.resent_at ? l(invitation.resent_at.to_date) : nil) %>
13+
</p>
14+
15+
<p>
16+
<% if invitation.can_resend? %>
17+
<%# i18n-tasks-use t("invite_requests.invitation.after_cooldown_period.not_resent")
18+
i18n-tasks-use t("invite_requests.invitation.after_cooldown_period.resent_html")-%>
19+
<% status = invitation.resent_at ? "resent_html" : "not_resent" %>
20+
<%= t(".after_cooldown_period.#{status}",
21+
count: ArchiveConfig.HOURS_BEFORE_RESEND_INVITATION,
22+
contact_support_link: link_to(t(".contact_support"), new_feedback_report_path)) %>
23+
<%= button_to t(".resend_button"), resend_invite_requests_path(email: invitation.invitee_email) %>
24+
<% else %>
25+
<%= t(".before_cooldown_period", count: ArchiveConfig.HOURS_BEFORE_RESEND_INVITATION) %>
26+
<% end %>
27+
</p>
28+
<!--/content-->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<p class="notice">
2+
<%= t(".email_not_found") %>
3+
</p>
+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
<%= render "invite_request", invite_request: @invite_request %>
1+
<% if @invite_request %>
2+
<%= render "invite_request", invite_request: @invite_request %>
3+
<% elsif @invitation %>
4+
<%= render "invitation", invitation: @invitation %>
5+
<% else %>
6+
<%= render "no_invitation" %>
7+
<% end %>
28

39
<p>
4-
<%= ts("To check on the status of your invitation, go to the %{status_page} and enter your email in the space provided!", status_page: link_to("Invitation Request Status page", status_invite_requests_path)).html_safe %>
10+
<%= t(".instructions_html", status_link: link_to("Invitation Request Status page", status_invite_requests_path)) %>
511
</p>

app/views/invite_requests/show.js.erb

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<% if @invite_request %>
22
$j("#invite-status").html("<%= escape_javascript(render "invite_requests/invite_request", invite_request: @invite_request) %>");
3+
<% elsif @invitation %>
4+
$j("#invite-status").html("<%= escape_javascript(render "invitation", invitation: @invitation) %>");
5+
6+
<%# Correct heading size for JavaScript display %>
7+
$j(document).ready(function(){
8+
$j('#invite-heading').replaceWith(function () {
9+
return '<h3 class="heading">' + $j(this).html() + "</h3>";
10+
});
11+
})
312
<% else %>
4-
$j("#invite-status").html("<p>Sorry, we can't find the email address you entered. If you had used it to join our invitation queue, it's possible that your invitation may have already been emailed to you; please check your spam folder, as your spam filters may have placed it there.</p>");
13+
$j("#invite-status").html("<%= escape_javascript(render "no_invitation") %>");
514
<% end %>

app/views/invite_requests/status.html.erb

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<!--Descriptive page name, messages and instructions-->
22
<h2 class="heading">
3-
<%= ts("Invitation Request Status") %>
3+
<%= t(".heading") %>
44
</h2>
55

66
<p>
7-
<%= ts("There are currently %{count} people on the waiting list.", count: InviteRequest.count) %>
7+
<%= t(".waiting_list", count: InviteRequest.count) %>
88
<% if AdminSetting.current.invite_from_queue_enabled? %>
9-
<%= ts("We are sending out %{invites} invitations per day.", invites: AdminSetting.current.invite_from_queue_number) %>
9+
<%= t(".send_rate", invites: AdminSetting.current.invite_from_queue_number) %>
1010
<% end %>
1111
</p>
1212
<!--/descriptions-->
@@ -17,7 +17,9 @@
1717
<p>
1818
<%= label_tag :email %>
1919
<%= text_field_tag :email %>
20-
<%= submit_tag ts("Look me up") %>
20+
<span class="submit actions">
21+
<%= submit_tag t(".search") %>
22+
</span>
2123
</p>
2224
</fieldset>
2325
<% end %>

app/views/tags/show.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<% unless @tag.direct_sub_tags.blank? %>
8484
<div class="sub listbox group">
8585
<h3 class="heading"><%= ts('Subtags') %>:</h3>
86-
<% cache "tag-#{@tag.cache_key}-subtags-v2", expires_in: 24.hours, skip_digest: true do %>
86+
<% cache "tag-#{@tag.cache_key}-subtags-v3", expires_in: 24.hours, skip_digest: true do %>
8787
<%= sub_tag_tree(@tag) %>
8888
<% end %>
8989
</div>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<% content_for :message do %>
22
<p><%= t("mailer.general.greeting.formal_html", name: style_bold(@user.login)) %></p>
33

4-
<p><%= t('.deleted.html', title: style_creation_title(@work.title)) %></p>
4+
<p><%= t(".deleted.html", title: style_creation_title(@work.title)) %></p>
55

6-
<p><%= t('.import_project.html', opendoors_link: opendoors_link(t '.opendoors')) %></p>
6+
<p><%= t(".import_project.html", opendoors_link: opendoors_link(t(".opendoors"))) %></p>
77

8-
<p><%= t(".html.tos_violation", contact_abuse_link: abuse_link(t(".contact_abuse"))).html_safe %></p>
8+
<p><%= t(".tos_violation.html", contact_abuse_link: abuse_link(t(".contact_abuse"))) %></p>
99

10-
<p><%= t '.bye' %></p>
10+
<p><%= t(".bye") %></p>
1111
<% end %>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<% content_for :message do %>
22
<%= t("mailer.general.greeting.formal_html", name: @user.login) %>
33

4-
<%= t('.deleted.text', title: @work.title) %>
4+
<%= t(".deleted.text", title: @work.title) %>
55

6-
<%= t('.import_project.text', opendoors_link: 'http://opendoors.transformativeworks.org/contact-open-doors/') %>
6+
<%= t(".import_project.text", opendoors_link: "http://opendoors.transformativeworks.org/contact-open-doors/") %>
77

8-
<%= t(".text.tos_violation", contact_abuse_url: new_abuse_report_url) %>
8+
<%= t(".tos_violation.text", contact_abuse_url: new_abuse_report_url) %>
99

10-
<%= t '.bye' %>
10+
<%= t(".bye") %>
1111
<% end %>

config/config.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ DELIMITER_FOR_OUTPUT: ', '
8181
INVITE_FROM_QUEUE_ENABLED: true
8282
INVITE_FROM_QUEUE_NUMBER: 10
8383
INVITE_FROM_QUEUE_FREQUENCY: 7
84+
85+
HOURS_BEFORE_RESEND_INVITATION: 24
8486
# this is whether or not people without invitations can create accounts
8587
ACCOUNT_CREATION_ENABLED: false
8688
DAYS_TO_PURGE_UNACTIVATED: 7
@@ -100,7 +102,7 @@ TITLE_MIN: 1
100102
SUMMARY_MAX: 1250
101103
NOTES_MAX: 5000
102104
COMMENT_MAX: 10000
103-
TAG_MAX: 100
105+
TAG_MAX: 150
104106
CONTENT_MIN: 10
105107
CONTENT_MAX: 510000
106108
CONTENT_MAX_DISPLAYED: 500000

config/locales/controllers/en.yml

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ en:
105105
invite_requests:
106106
index:
107107
page_title: Invitation Requests
108+
resend:
109+
not_found: Could not find an invitation associated with that email.
110+
not_yet: You cannot resend an invitation that was sent in the last %{count} hours.
111+
success: Invitation resent to %{email}.
108112
kudos:
109113
create:
110114
success: Thank you for leaving kudos!

config/locales/mailers/en.yml

+3-4
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,14 @@ en:
111111
deleted:
112112
html: Your work %{title} was deleted from the Archive by a site admin.
113113
text: Your work "%{title}" was deleted from the Archive by a site admin.
114-
html:
115-
tos_violation: If it's possible your work violated the Archive's Terms of Service, please %{contact_abuse_link}.
116114
import_project:
117115
html: If your work was part of an import project managed by our Open Doors team, please %{opendoors_link} with any further questions.
118116
text: If your work was part of an import project managed by our Open Doors team, please contact Open Doors (%{opendoors_link}) with any further questions.
119117
opendoors: contact Open Doors
120118
subject: "[%{app_name}] Your work has been deleted by an admin"
121-
text:
122-
tos_violation: If it's possible your work violated the Archive's Terms of Service, please contact our Policy & Abuse team (%{contact_abuse_url}).
119+
tos_violation:
120+
html: If it's possible your work violated the Archive's Terms of Service, please %{contact_abuse_link}.
121+
text: If it's possible your work violated the Archive's Terms of Service, please contact our Policy & Abuse team (%{contact_abuse_url}).
123122
admin_hidden_work_notification:
124123
access: While your work is hidden, you will still be able to access it through the link provided above, but it will not be listed on your works page, and it won't be available to other users of the Archive.
125124
check_email: Please check your email, including your spam folder, as the Policy & Abuse team may have already contacted you explaining why your work was hidden.

0 commit comments

Comments
 (0)