<%= link_to ts('Delete Banner'), confirm_delete_admin_banner_path(@admin_banner), data: {confirm: ts('Are you sure you want to delete this banner?')} %>
<% end %>
<% # end of information added by the bookmark owner %>
diff --git a/app/views/collections/_collection_blurb.html.erb b/app/views/collections/_collection_blurb.html.erb
index ea6ea2b066b..055927b3be7 100644
--- a/app/views/collections/_collection_blurb.html.erb
+++ b/app/views/collections/_collection_blurb.html.erb
@@ -72,8 +72,8 @@
<% end %>
<% if !collection.user_is_owner?(current_user) && collection.moderated? && !(collection.challenge && collection.challenge.signup_open) %>
- <% if (@participant ||= collection.get_participants_for_user(current_user).first) %>
- <%= link_to ts("Leave"), collection_participant_path(collection, @participant),
+ <% if (participant = collection.get_participants_for_user(current_user).first) %>
+ <%= link_to ts("Leave"), collection_participant_path(collection, participant),
data: {confirm: ts('Are you certain you want to leave this collection?')},
:method => :delete %>
<% if policy(Language).new? %>
- <%= link_to ts('Add a Language'), new_language_path %>
+ <%= link_to t(".navigation.add"), new_language_path %>
<% else %>
- <%= link_to ts('Suggest a Language'), new_feedback_report_path %>
+ <%= link_to t(".navigation.suggest"), new_feedback_report_path %>
<% end %>
- <% for language in @languages %>
-
+ <% @languages.each do |language| %>
+ <% works_count = estimate_number(@works_counts[language.short] || 0) %>
<% if language == Language.default %>
diff --git a/app/views/layouts/_banner.html.erb b/app/views/layouts/_banner.html.erb
index 745779d2186..25115ff1f6b 100644
--- a/app/views/layouts/_banner.html.erb
+++ b/app/views/layouts/_banner.html.erb
@@ -1,25 +1,20 @@
-<% # BACK END this seems giant and messy and confusing, pls can we review?
- # FRONT END yes let us rewrite this
-%>
-<% unless current_user && current_user.try(:preference).try(:banner_seen) %>
-<% if @admin_banner && @admin_banner.active? %>
-<% unless current_user.nil? && session[:hide_banner] %>
-
You're receiving this e-mail because you had works in a fanworks archive that has been imported by <%= style_link('Open Doors', 'http://opendoors.transformativeworks.org/') %> into the <%= style_link('Archive of Our Own', root_url) %> (AO3). Because this e-mail address is connected to one registered on the imported archive, the associated fanworks (listed below) have been automatically added to your AO3 account.
You can read announcements about recent archive moves at <%= style_link('AO3 News', 'http://archiveofourown.org/admin_posts?tag=18') %>, and find additional information on Open Doors' <%= style_link('FAQ page', 'http://opendoors.transformativeworks.org/faq') %> or <%= style_link('tutorials page', 'http://opendoors.transformativeworks.org/tutorials') %>. For any questions not answered in the FAQ, tutorials, or this e-mail, please <%= support_link('contact AO3 Support') %>.
If this is a mistake and these are not your works, please don't delete them! Please <%= style_link('contact Open Doors', 'https://opendoors.transformativeworks.org/en/contact-open-doors/') %> and we will sort it out.
Depending on the archive, your works may have been imported restricted to registered users only (to keep them out of Google searches). If this is the case, the works will only be accessible by logged-in users unless you choose to make them fully visible. For help unlocking, orphaning, or deleting your works, please <%= support_link('contact AO3 Support') %>.
To preserve rec lists and bookmarks, the imported archive's web addresses may redirect to the imported copy of these works for a limited time (check the announcement post for your archive to be sure). If you've already uploaded a copy of these works and you did NOT use the import from URL feature, there will be two copies of the same work on the archive.
+
<%= t(".redirects") %>
-
If you would like Open Doors to update the redirect to point to your pre-existing work, please delete the imported copy, and <%= style_link('contact Open Doors', 'https://opendoors.transformativeworks.org/en/contact-open-doors/') %> with your AO3 account name, your account name on the imported archive, and the title and URL of the fanwork you would like the redirect to point to. (If you have multiple works you would like to change the redirects for, you can list these in one email.)
If you had other works on the imported archive under an e-mail address you can no longer access, please <%= style_link('contact Open Doors', 'https://opendoors.transformativeworks.org/en/contact-open-doors/') %> with any information that can help verify your identity.
If you're contacting us, please add email addresses from @transformativeworks.org to your list of safe contacts and check your spam folders for our reply.
+
<%= t(".email_tips") %>
<%= t("mailer.general.closing.informal") %>
<%= style_bold(t("mailer.general.signature.open_doors")) %>
diff --git a/app/views/user_mailer/claim_notification.text.erb b/app/views/user_mailer/claim_notification.text.erb
index adec8a12bb0..3a316eff16e 100644
--- a/app/views/user_mailer/claim_notification.text.erb
+++ b/app/views/user_mailer/claim_notification.text.erb
@@ -1,30 +1,34 @@
<% content_for :message do %>
- <%= t("mailer.general.greeting.introductory") %>
+<%= t("mailer.general.greeting.introductory") %>
- You're receiving this e-mail because you had works in a fanworks archive that has been imported by Open Doors (http://opendoors.transformativeworks.org) into the Archive of Our Own (AO3 - <%= root_url %>). Because this e-mail address is connected to one registered on the imported archive, the associated fanworks (listed below) have been automatically added to your AO3 account.
+<%= t(".introduction.text", open_doors_url: "http://opendoors.transformativeworks.org", app_url: root_url) %>
- You can read announcements about recent archive moves at AO3 News (<%= root_url %>admin_posts?tag=18), and find additional information on Open Doors's FAQ page (http://opendoors.transformativeworks.org/faq) or tutorials page (http://opendoors.transformativeworks.org/tutorials). For any questions not answered in the FAQ, tutorials, or this e-mail, please contact AO3 Support at <%= root_url %>support.
+<%= t(".more_info.text", news_url: admin_posts_url(tag: 18), open_doors_faq_url: "http://opendoors.transformativeworks.org/faq", open_doors_tutorial_url: "http://opendoors.transformativeworks.org/tutorials", support_url: new_feedback_report_url) %>
- If this is a mistake and these are not your works, please don't delete them! Please contact Open Doors (https://opendoors.transformativeworks.org/en/contact-open-doors/) and we will sort it out.
+<%= t(".mistake.text", open_doors_url: "https://opendoors.transformativeworks.org/en/contact-open-doors/") %>
- Depending on the archive, your works may have been imported restricted to registered users only (to keep them out of Google searches). If this is the case, the works will only be accessible by logged-in users unless you choose to make them fully visible. For help unlocking, orphaning, or deleting your works, please contact Support.
+<%= t(".access.text", support_url: new_feedback_report_url) %>
- These works were written under the e-mail: <%= @external_email %>
- <% @claimed_works.each do |work| %>
- - <%= work.title.html_safe %> <%= work_url(work) %> <%= (work.fandom_string.blank? ? "" : " (#{work.fandom_string})") %>
+<%= t(".works_by", email: @external_email) %>
+<% @claimed_works.each do |work| %>
+ <% if work.fandom_string.present? %>
+<%= t(".work_info.text.with_fandom", work_title: work.title, work_url: work_url(work), fandom: work.fandom_string.split(",").to_sentence) %>
+ <% else %>
+<%= t(".work_info.text.no_fandom", work_title: work.title, work_url: work_url(work)) %>
<% end %>
+<% end %>
- To preserve rec lists and bookmarks, the imported archive's web addresses may redirect to the imported copy of these works for a limited time (check the announcement post for your archive to be sure). If you've already uploaded a copy of these works and you did NOT use the import from URL feature, there will be two copies of the same work on the archive.
+<%= t(".redirects") %>
- If you would like Open Doors to update the redirect to point to your pre-existing work, please delete the imported copy, and contact Open Doors at https://opendoors.transformativeworks.org/en/contact-open-doors/ with your AO3 account name, your account name on the imported archive, and the title and URL of the fanwork you would like the redirect to point to. (If you have multiple works you would like to change the redirects for, you can list these in one email.)
+<%= t(".update_redirect.text", open_doors_url: "https://opendoors.transformativeworks.org/en/contact-open-doors/") %>
- If you had other works on the imported archive under an e-mail address you can no longer access, please contact Open Doors with any information that can help verify your identity.
+<%= t(".other_works.text") %>
- For other inquiries, please contact Support at <%= root_url %>support.
+<%= t(".questions.text", support_url: new_feedback_report_url) %>
- If you're contacting us, please add email addresses from @transformativeworks.org to your list of safe contacts and check your spam folders for our reply.
+<%= t(".email_tips") %>
- <%= t("mailer.general.closing.informal") %>
- <%= t("mailer.general.signature.open_doors") %>
- <%= t("mailer.general.signature.parent_org") %>
+<%= t("mailer.general.closing.informal") %>
+<%= t("mailer.general.signature.open_doors") %>
+<%= t("mailer.general.signature.parent_org") %>
<% end %>
diff --git a/app/views/user_mailer/feedback.html.erb b/app/views/user_mailer/feedback.html.erb
index fa34fcd167e..6b9e7dfd275 100644
--- a/app/views/user_mailer/feedback.html.erb
+++ b/app/views/user_mailer/feedback.html.erb
@@ -16,7 +16,7 @@
form:
If you have additional questions or information, do not hesitate to send in
diff --git a/app/views/users/sessions/_greeting.html.erb b/app/views/users/sessions/_greeting.html.erb
index 466fddbc354..4455da69ccc 100644
--- a/app/views/users/sessions/_greeting.html.erb
+++ b/app/views/users/sessions/_greeting.html.erb
@@ -1,59 +1,58 @@
-
+
+
diff --git a/app/views/works/_search_box.html.erb b/app/views/works/_search_box.html.erb
index 6b10347802a..fb1801116c3 100644
--- a/app/views/works/_search_box.html.erb
+++ b/app/views/works/_search_box.html.erb
@@ -1,11 +1,10 @@
-<%= form_for WorkSearchForm.new, as: :work_search, :url => search_works_path, :html => {:class => 'search', :id => 'search', :method => :get} do |f| %>
+<%= form_for WorkSearchForm.new, as: :work_search, url: search_works_path, html: { class: "search", id: "search", role: "search", "aria-label": t(".a11y_label"), method: :get } do |f| %>
<% end %>
diff --git a/config/application.rb b/config/application.rb
index 92614ab0e08..3a76bc4d2bd 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -119,6 +119,7 @@ class Application < Rails::Application
domain: ArchiveConfig.SMTP_DOMAIN,
port: ArchiveConfig.SMTP_PORT,
enable_starttls_auto: ArchiveConfig.SMTP_ENABLE_STARTTLS_AUTO,
+ enable_starttls: ArchiveConfig.SMTP_ENABLE_STARTTLS,
openssl_verify_mode: ArchiveConfig.SMTP_OPENSSL_VERIFY_MODE
}
if ArchiveConfig.SMTP_AUTHENTICATION
diff --git a/config/config.yml b/config/config.yml
index 329e6e30435..1c98285bd51 100644
--- a/config/config.yml
+++ b/config/config.yml
@@ -50,6 +50,7 @@ SMTP_PORT: 25
SMTP_DOMAIN: localhost
SMTP_OPENSSL_VERIFY_MODE: none
SMTP_ENABLE_STARTTLS_AUTO: false
+SMTP_ENABLE_STARTTLS: false
# if required for email authentication
#SMTP_USER:
#SMTP_PASSWORD:
@@ -330,48 +331,228 @@ BANNED_MULTIMEDIA_SRCS: []
# Allowed CSS
#
-# *** IMPORTANT: if you edit these values please also update the css-help.html file in public/help ***
+# *** IMPORTANT: if you edit these values please also update the
+# skins-creating.html file in public/help ***
#
-# the following properties and keywords will be ADDED to the default set allowed in user-submitted CSS code, along with:
+# the following properties and keywords will be ADDED to the default set allowed
+# in user-submitted CSS code, along with:
# - hex and rgb color values
# - numeric values specified with percentages or cm|em|ex|in|mm|pc|pt|px|%
# - image files specified with url(http://image.url/blahblah/whatever.(jpg|png|gif))
#
-# We use this list for properties which can take shorthand values and also have subproperties or variants
-# (ie, because "border" is in here, we allow through: border-right, border-bottom-left-radius, -moz-border-foo, etc)
-# It is slightly less secure so do NOT put any property in here if you aren't sure all variations on it are okay!
-SUPPORTED_CSS_SHORTHAND_PROPERTIES: ["background", "border", "column", "cue", "font", "layer-background",
-"layout-grid", "list-style", "margin", "marker", "outline", "overflow", "padding", "page-break", "pause",
-"scrollbar", "text", "transform", "transition"]
-
-SUPPORTED_CSS_PROPERTIES: ["-replace", "-use-link-source",
- "accelerator", "alignment-adjust", "alignment-baseline", "appearance", "azimuth",
- "baseline-shift", "behavior", "binding", "bookmark-label", "bookmark-level", "bookmark-target", "bottom", "box-align", "box-direction",
- "box-flex", "box-flex-group", "box-lines", "box-orient", "box-pack", "box-shadow", "box-sizing", "caption-side", "clear", "clip", "color", "color-profile",
- "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after", "cue-before",
- "cursor", "direction", "display", "dominant-baseline", "drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust",
- "drop-initial-before-align", "drop-initial-size", "drop-initial-value", "elevation", "empty-cells", "filter", "fit", "fit-position", "float",
- "float-offset", "font", "font-effect", "font-emphasize", "font-emphasize-position", "font-emphasize-style", "font-family", "font-size", "font-size-adjust",
- "font-smooth", "font-stretch", "font-style", "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after",
- "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation", "image-resolution", "ime-mode",
- "include-source", "inline-box-align", "layout-flow", "left", "letter-spacing", "line-break", "line-height", "line-stacking", "line-stacking-ruby",
- "line-stacking-shift", "line-stacking-strategy", "mark", "mark-after", "mark-before", "marks", "marquee-direction", "marquee-play-count",
- "marquee-speed", "marquee-style", "max-height", "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
- "nav-up", "opacity", "orphans", "page",
- "page-policy", "phonemes", "pitch", "pitch-range", "play-during", "position", "presentation-level", "punctuation-trim",
- "quotes", "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
- "ruby-position", "ruby-span", "size", "speak", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "tab-side",
- "table-layout", "target", "target-name", "target-new", "target-position", "top", "unicode-bibi", "unicode-bidi", "user-select",
- "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family", "voice-pitch", "voice-pitch-range", "voice-rate", "voice-stress",
- "voice-volume", "volume", "white-space", "white-space-collapse", "widows", "width", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index"]
-
-# we allow through any string that is just a-z plus dashes (no spaces), so the only keywords you need to specify would
-# be ones that have something else or url, if you want to allow url values
+# We use this list for properties which can take shorthand values and also have
+# subproperties or variants (ie, because "border" is in here, we allow through:
+# border-right, border-bottom-left-radius, -moz-border-foo, etc)
+# It is slightly less secure so do NOT put any property in here if you aren't
+# sure all variations on it are okay!
+SUPPORTED_CSS_SHORTHAND_PROPERTIES:
+- background
+- border
+- column
+- cue
+- flex
+- font
+- layer-background
+- layout-grid
+- list-style
+- margin
+- marker
+- outline
+- overflow
+- padding
+- page-break
+- pause
+- scrollbar
+- text
+- transform
+- transition
+
+SUPPORTED_CSS_PROPERTIES:
+- -replace
+- -use-link-source
+- accelerator
+- align-content
+- align-items
+- align-self
+- alignment-adjust
+- alignment-baseline
+- appearance
+- azimuth
+- baseline-shift
+- behavior
+- binding
+- bookmark-label
+- bookmark-level
+- bookmark-target
+- bottom
+- box-align
+- box-direction
+- box-flex
+- box-flex-group
+- box-lines
+- box-orient
+- box-pack
+- box-shadow
+- box-sizing
+- caption-side
+- clear
+- clip
+- color
+- color-profile
+- content
+- counter-increment
+- counter-reset
+- crop
+- cue
+- cue-after
+- cue-before
+- cursor
+- direction
+- display
+- dominant-baseline
+- drop-initial-after-adjust
+- drop-initial-after-align
+- drop-initial-before-adjust
+- drop-initial-before-align
+- drop-initial-size
+- drop-initial-value
+- elevation
+- empty-cells
+- filter
+- fit
+- fit-position
+- float
+- float-offset
+- font
+- font-effect
+- font-emphasize
+- font-emphasize-position
+- font-emphasize-style
+- font-family
+- font-size
+- font-size-adjust
+- font-smooth
+- font-stretch
+- font-style
+- font-variant
+- font-weight
+- grid-columns
+- grid-rows
+- hanging-punctuation
+- height
+- hyphenate-after
+- hyphenate-before
+- hyphenate-character
+- hyphenate-lines
+- hyphenate-resource
+- hyphens
+- icon
+- image-orientation
+- image-resolution
+- ime-mode
+- include-source
+- inline-box-align
+- justify-content
+- layout-flow
+- left
+- letter-spacing
+- line-break
+- line-height
+- line-stacking
+- line-stacking-ruby
+- line-stacking-shift
+- line-stacking-strategy
+- mark
+- mark-after
+- mark-before
+- marks
+- marquee-direction
+- marquee-play-count
+- marquee-speed
+- marquee-style
+- max-height
+- max-width
+- min-height
+- min-width
+- move-to
+- nav-down
+- nav-index
+- nav-left
+- nav-right
+- nav-up
+- opacity
+- order
+- orphans
+- page
+- page-policy
+- phonemes
+- pitch
+- pitch-range
+- play-during
+- position
+- presentation-level
+- punctuation-trim
+- quotes
+- rendering-intent
+- resize
+- rest
+- rest-after
+- rest-before
+- richness
+- right
+- rotation
+- rotation-point
+- ruby-align
+- ruby-overhang
+- ruby-position
+- ruby-span
+- size
+- speak
+- speak-header
+- speak-numeral
+- speak-punctuation
+- speech-rate
+- stress
+- string-set
+- tab-side
+- table-layout
+- target
+- target-name
+- target-new
+- target-position
+- top
+- unicode-bibi
+- unicode-bidi
+- user-select
+- vertical-align
+- visibility
+- voice-balance
+- voice-duration
+- voice-family
+- voice-pitch
+- voice-pitch-range
+- voice-rate
+- voice-stress
+- voice-volume
+- volume
+- white-space
+- white-space-collapse
+- widows
+- width
+- word-break
+- word-spacing
+- word-wrap
+- writing-mode
+- z-index
+
+# we allow through any string that is just a-z plus dashes (no spaces), so the
+# only keywords you need to specify would be ones that have something else or
+# url, if you want to allow url values
SUPPORTED_CSS_KEYWORDS: ["!important", "url"]
-# if you include "url" in the SUPPORTED_CSS_KEYWORDS, only urls pointing to this kind of
-# resource will be allowed
+# if you include "url" in the SUPPORTED_CSS_KEYWORDS, only urls pointing to this
+# kind of resource will be allowed
SUPPORTED_EXTERNAL_URLS: ["jpg", "jpeg", "png", "gif"]
# variables for Askimet http://akismet.com/
@@ -459,6 +640,9 @@ CHARACTER_COUNT_SCRIPTS: ["Han", "Hiragana", "Katakana", "Thai"]
SECONDS_UNTIL_WORK_INDEX_EXPIRE: 1800
SECONDS_UNTIL_BOOKMARK_INDEX_EXPIRE: 1800
+# Cache duration for commentable kudos list of names
+MINUTES_UNTIL_COMMENTABLE_KUDOS_LISTS_EXPIRE: 60
+
# Cache duration for comment counts:
SECONDS_UNTIL_COMMENT_COUNTS_EXPIRE: 3600
diff --git a/config/docker/Dockerfile b/config/docker/Dockerfile
index bee750f6e93..8e904ec6399 100644
--- a/config/docker/Dockerfile
+++ b/config/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:3.0.5
+FROM ruby:3.1.4
# Install additional packages
RUN apt-get update && \
@@ -16,7 +16,7 @@ VOLUME /otwa
# Install ruby packages
COPY Gemfile .
COPY Gemfile.lock .
-RUN gem install bundler -v 2.2.33 && bundle install
+RUN gem install bundler -v 2.5.6 && bundle install
# Default command to run in a new container
EXPOSE 3000
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 3e29fe4d98d..51b08d60c85 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -109,6 +109,13 @@
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
+ # paperclip config
+ Paperclip::Attachment.default_options[:storage] = :s3
+ Paperclip::Attachment.default_options[:s3_credentials] = { s3_region: ENV["S3_REGION"],
+ bucket: ENV["S3_BUCKET"],
+ access_key_id: ENV["S3_ACCESS_KEY_ID"],
+ secret_access_key: ENV["S3_SECRET_ACCESS_KEY"] }
+
# Inserts middleware to perform automatic connection switching.
# The `database_selector` hash is used to pass options to the DatabaseSelector
# middleware. The `delay` is used to determine how long to wait after a write
diff --git a/config/environments/staging.rb b/config/environments/staging.rb
index a19d6a60c5e..89fb19bde17 100644
--- a/config/environments/staging.rb
+++ b/config/environments/staging.rb
@@ -68,6 +68,12 @@
Bullet.counter_cache_enable = false
end
+ Paperclip::Attachment.default_options[:storage] = :s3
+ Paperclip::Attachment.default_options[:s3_credentials] = { s3_region: ENV["S3_REGION"],
+ bucket: ENV["S3_BUCKET"],
+ access_key_id: ENV["S3_ACCESS_KEY_ID"],
+ secret_access_key: ENV["S3_SECRET_ACCESS_KEY"] }
+
config.middleware.use Rack::Attack
# Disable dumping schemas after migrations.
diff --git a/config/initializers/monkeypatches/mail_disable_starttls.rb b/config/initializers/monkeypatches/mail_disable_starttls.rb
deleted file mode 100644
index 44c236b64b3..00000000000
--- a/config/initializers/monkeypatches/mail_disable_starttls.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# from https://github.com/rails/rails/issues/44698#issuecomment-1069675285
-
-module MailDisableStarttls
- def build_smtp_session
- super.tap do |smtp|
- unless settings[:enable_starttls_auto]
- if smtp.respond_to?(:disable_starttls)
- smtp.disable_starttls
- end
- end
- end
- end
-end
-
-if Mail::VERSION::STRING == "2.7.1"
- Mail::SMTP.prepend MailDisableStarttls
-else
- puts "WARNING: The monkeypatch #{__FILE__} was written for version 2.7.1 of the mail gem, but you are running #{Mail::VERSION::STRING}. Please update or remove the monkeypatch."
-end
diff --git a/config/locales/mailers/en.yml b/config/locales/mailers/en.yml
index 4a8b771a40a..7c79f8cabd5 100644
--- a/config/locales/mailers/en.yml
+++ b/config/locales/mailers/en.yml
@@ -195,6 +195,48 @@ en:
html: "%{login}, the email associated with your account has been changed to %{email}"
text: "%{login}, the email associated with your account has been changed to %{email}"
subject: "[%{app_name}] Email changed"
+ claim_notification:
+ access:
+ contact_support: contact AO3 Support
+ html: Depending on the archive, your works may have been imported restricted to registered users only (to keep them out of Google searches). If this is the case, the works will only be accessible by logged-in users unless you choose to make them fully visible. For help unlocking, orphaning, or deleting your works, please %{contact_support_link}.
+ text: Depending on the archive, your works may have been imported restricted to registered users only (to keep them out of Google searches). If this is the case, the works will only be accessible by logged-in users unless you choose to make them fully visible. For help unlocking, orphaning, or deleting your works, please contact AO3 Support at %{support_url}.
+ email_tips: If you're contacting us, please add email addresses from @transformativeworks.org to your list of safe contacts and check your spam folders for our reply.
+ introduction:
+ ao3_name: Archive of Our Own
+ html: You're receiving this e-mail because you had works in a fanworks archive that has been imported by %{open_doors_name_link} into the %{app_link} (AO3). Because this e-mail address is connected to one registered on the imported archive, the associated fanworks (listed below) have been automatically added to your AO3 account.
+ open_doors_name: Open Doors
+ text: You're receiving this e-mail because you had works in a fanworks archive that has been imported by Open Doors (%{open_doors_url}) into the Archive of Our Own (AO3 - %{app_url}). Because this e-mail address is connected to one registered on the imported archive, the associated fanworks (listed below) have been automatically added to your AO3 account.
+ mistake:
+ contact_open_doors: contact Open Doors
+ html: If this is a mistake and these are not your works, please don't delete them! Please just %{contact_open_doors_link} and we will sort it out.
+ text: If this is a mistake and these are not your works, please don't delete them! Please just contact Open Doors (%{open_doors_url}) and we will sort it out.
+ more_info:
+ ao3_news: AO3 News
+ contact_support: contact AO3 Support
+ faq_page: FAQ page
+ html: You can read announcements about recent archive moves at %{ao3_news_link}, and find additional information on Open Doors' %{faq_page_link} or %{tutorial_page_link}. For any questions not answered in the FAQ, tutorials, or this e-mail, please %{contact_support_link}.
+ text: You can read announcements about recent archive moves at AO3 News (%{news_url}), and find additional information on Open Doors' FAQ page (%{open_doors_faq_url}) or tutorials page (%{open_doors_tutorial_url}). For any questions not answered in the FAQ, tutorials, or this e-mail, please contact Support at %{support_url}.
+ tutorial_page: tutorial page
+ other_works:
+ contact_open_doors: contact Open Doors
+ html: If you had other works on the imported archive under an e-mail address you can no longer access, please %{contact_open_doors_link} with any information that can help verify your identity.
+ text: If you had other works on the imported archive under an e-mail address you can no longer access, please contact Open Doors with any information that can help verify your identity.
+ questions:
+ contact_support: contact AO3 Support
+ html: For other inquiries, please %{contact_support_link}.
+ text: For other inquiries, please contact AO3 Support at %{support_url}.
+ redirects: To preserve rec lists and bookmarks, the imported archive's web addresses may redirect to the imported copy of these works for a limited time (check the announcement post for your archive to be sure). If you've already uploaded a copy of these works and you did NOT use the import from URL feature, there will be two copies of the same work on the archive.
+ subject: "[%{app_name}] Works uploaded"
+ update_redirect:
+ contact_open_doors: contact Open Doors
+ html: If you would like Open Doors to update the redirect to point to your pre-existing work, please delete the imported copy, and %{contact_open_doors_link} with your AO3 account name, your account name on the imported archive, and the title and URL of the fanwork you would like the redirect to point to. (If you have multiple works you would like to change the redirects for, you can list these in one email.)
+ text: If you would like Open Doors to update the redirect to point to your pre-existing work, please delete the imported copy, and contact Open Doors at %{open_doors_url} with your AO3 account name, your account name on the imported archive, and the title and URL of the fanwork you would like the redirect to point to. (If you have multiple works you would like to change the redirects for, you can list these in one email.)
+ work_info:
+ html: "%{work_link} (%{fandom})"
+ text:
+ no_fandom: "- %{work_title} %{work_url}"
+ with_fandom: "- %{work_title} %{work_url} (%{fandom})"
+ works_by: 'These works were written under the e-mail: %{email}'
collection_notification:
assignments_sent:
complete: All assignments have now been sent out.
diff --git a/config/locales/models/en.yml b/config/locales/models/en.yml
index 5d9d2f2a6a5..61e3df5bb02 100644
--- a/config/locales/models/en.yml
+++ b/config/locales/models/en.yml
@@ -4,7 +4,9 @@ en:
attributes:
admin/role:
board: Board
+ board_assistants_team: Board Assistants Team
communications: Communications
+ development_and_membership: Development & Membership
docs: AO3 Docs
elections: Elections
open_doors: Open Doors
@@ -42,6 +44,11 @@ en:
name_with_colon:
one: 'Additional Tag:'
other: 'Additional Tags:'
+ gift_exchange:
+ offers_num_allowed: Number of offers allowed per sign-up
+ offers_num_required: Number of offers required per sign-up
+ requests_num_allowed: Number of requests allowed per sign-up
+ requests_num_required: Number of requests required per sign-up
meta_tagging:
meta_tag: Metatag
meta_tag_id: Metatag
@@ -170,6 +177,8 @@ en:
attributes:
user_defined_tags_count:
at_most: must not add up to more than %{count}. Your work has %{value} of these tags, so you must remove %{diff} of them.
+ blocked_gifts: "%{byline} does not accept gifts."
+ blocked_your_gifts: "%{byline} does not accept gifts from you."
work/parent_work_relationships:
format: "%{message}"
models:
diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml
index 985f9b31d63..1024efb1b7a 100644
--- a/config/locales/views/en.yml
+++ b/config/locales/views/en.yml
@@ -178,6 +178,20 @@ en:
required: Required when adding or removing a warning or suspension to an account.
submit: Update
heading: Record Warnings, Suspensions, or Notes
+ banners:
+ index:
+ actions:
+ active: Active
+ confirm_delete: Are you sure you want to delete this banner?
+ delete: Delete
+ edit: Edit
+ page_heading: Banners
+ navigation:
+ confirm_delete: Are you sure you want to delete this banner?
+ delete: Delete Banner
+ edit: Edit Banner
+ index: Banners
+ new: New Banner
blacklist:
emails_found:
one: one email found
@@ -201,6 +215,38 @@ en:
canonical_format: All emails are stored in a single canonical format and common variants of the same address are not allowed (for instance, foo+whatever@bar.com will not be allowed if foo@bar.com is banned).
guest_comments: Banned email addresses cannot be used in guest comments.
page_heading: Manage Banned Emails
+ header:
+ nav:
+ activities: Activities
+ api_tokens: Manage API Tokens
+ banned_emails: Banned Emails
+ banners: Banners
+ invitations:
+ invitations: Invitations
+ new: Invite New Users
+ queue: Manage Queue
+ requests: Manage Requests
+ label: Admin
+ locales: Locales
+ posts:
+ admin_posts: Admin Posts
+ faqs: Archive FAQ
+ known_issues: Known Issues
+ news: AO3 News
+ post_news: Post AO3 News
+ wrangling_guidelines: Wrangling Guidelines
+ settings: Settings
+ skins:
+ approved: Approved Skins
+ queue: Approval Queue
+ rejected: Rejected Skins
+ skins: Skins
+ spam: Spam
+ users:
+ email_search: Bulk Email Search
+ manage: Manage Users
+ search: Find Users
+ wrangling: Tag Wrangling
passwords:
edit:
describedby:
@@ -293,6 +339,7 @@ en:
title: Block %{name}
will:
commenting: commenting or leaving kudos on your works
+ gifting: giving you gift works outside of challenge assignments and claimed prompts
intro: 'Blocking a user prevents them from:'
replying: replying to your comments anywhere on the site
will_not:
@@ -305,6 +352,7 @@ en:
cancel: Cancel
resume:
commenting: commenting or leaving kudos on your works
+ gifting: giving you gift works outside of challenge assignments and claimed prompts
intro: 'Unblocking a user allows them to resume:'
replying: replying to your comments anywhere on the site
sure_html: Are you sure you want to %{unblock} %{username}?
@@ -324,6 +372,7 @@ en:
title: Blocked Users
will:
commenting: commenting or leaving kudos on your works
+ gifting: giving you gift works outside of challenge assignments and claimed prompts
intro:
one: 'You can block up to %{block_limit} user. Blocking a user prevents them from:'
other: 'You can block up to %{block_limit} users. Blocking a user prevents them from:'
@@ -528,14 +577,52 @@ en:
more_link:
one: "%{count} more user"
other: "%{count} more users"
+ languages:
+ index:
+ navigation:
+ add: Add a Language
+ edit: Edit
+ suggest: Suggest a Language
+ page_heading: Work Languages
+ works_count:
+ one: "%{formatted_count} work"
+ other: "%{formatted_count} works"
layouts:
header:
+ collections:
+ new: New Collection
+ javascript: While we've done our best to make the core functionality of this site accessible without JavaScript, it will work better with it enabled. Please consider turning it on!
login: Log In
+ nav:
+ about: About
+ browse: Browse
+ fandoms: Fandoms
+ label: Site
+ search: Search
proxy_notice:
button: Dismiss Notice
faux_heading: 'Important message:'
point1: You are using a proxy site that is not part of the Archive of Our Own.
point2: The entity that set up the proxy site can see what you submit, including your IP address. If you log in through the proxy site, it can see your password.
+ menu:
+ menu_about:
+ about_us: About Us
+ donate: Donate or Volunteer
+ faq: FAQ
+ news: News
+ wrangling_guidelines: Wrangling Guidelines
+ menu_browse:
+ bookmarks: Bookmarks
+ collections: Collections
+ tags: Tags
+ works: Works
+ menu_fandoms:
+ all: All Fandoms
+ menu_search:
+ bookmarks: Bookmarks
+ people: People
+ tags: Tags
+ works: Works
muted:
mute: Mute
muted_items_notice_html: You have muted some users on the Archive. Some items may not be shown, and any counts may be inaccurate. You can mute or unmute users on %{muted_users_link}.
@@ -660,6 +747,13 @@ en:
random: These are some random tags used on the Archive. To find more tags, %{search_tags_link}.
random_in_collection: These are some random tags used in the collection.
search_tags: try our tag search
+ show:
+ canonical_html: It's a %{canonical_tag_link}. You can use it to %{filter_works_link} and to %{filter_bookmarks_link}.
+ canonical_tag: canonical tag
+ fandom_relationship_tags: Relationship tags in this fandom
+ filter_bookmarks: filter bookmarks
+ filter_works: filter works
+ list_fandom_tags_html: You can also access a list of %{fandom_relationship_tags_link}.
time:
formats:
date_short_html: %a %d %b %Y
@@ -733,6 +827,8 @@ en:
options_info: You can delete them, but please consider %{orphaning_link} them instead!
works_summary: 'You have %{work_count} work(s) under the following pseuds: %{pseuds}.'
submit: Save
+ edit:
+ browser_title: Edit Profile
registrations:
new:
cancel: Cancel
@@ -743,6 +839,26 @@ en:
submit: Create Account
wait: Please wait...
sessions:
+ greeting:
+ nav:
+ assignments: My Assignments
+ bookmarks: My Bookmarks
+ collections: My Collections
+ dashboard: My Dashboard
+ greeting: Hi, %{current_user}!
+ history: My History
+ import: Import Work
+ label: User
+ log_out: Log Out
+ new_work: New Work
+ open_doors: Open Doors
+ post: Post
+ post_draft: From Draft
+ preferences: My Preferences
+ sign_ups: My Sign-ups
+ subscriptions: My Subscriptions
+ tag_wrangling: Tag Wrangling
+ works: My Works
new:
beta_reminder:
give_feedback: give us your feedback
@@ -798,6 +914,11 @@ en:
multiple_works_restricted: Only show to registered users
restricted: Only show your work to registered users
unrestricted: Show to all
+ search_box:
+ a11y_label: Work
+ label: Work Search
+ submit: Search
+ tooltip_label: 'tip:'
show:
unposted_deletion_notice_html: This work is a draft and has not been posted. The draft will be scheduled for deletion on %{deletion_date}.
work_approved_children:
diff --git a/config/routes.rb b/config/routes.rb
index a2ae0a0e3e7..9927d21ea1a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -639,7 +639,7 @@
# See how all your routes lay out with "rake routes"
- # These are whitelisted routes that are proven to be used throughout the
+ # These are allowlisted routes that are proven to be used throughout the
# application, which previously relied on a deprecated catch-all route definition
# (`get ':controller(/:action(/:id(.:format)))'`) to work.
#
diff --git a/features/admins/admin_languages.feature b/features/admins/admin_languages.feature
index 8fb2841e762..cda790115f0 100644
--- a/features/admins/admin_languages.feature
+++ b/features/admins/admin_languages.feature
@@ -13,7 +13,7 @@ Scenario: An admin can add a language
And I fill in "Abbreviation" with "tlh"
And I press "Create Language"
Then I should see "Language was successfully added."
- And I should see "The Archive supports these languages"
+ And I should see "Work Languages"
And I should see "Klingon"
Scenario: Adding Abuse support for a language
diff --git a/features/admins/admin_works.feature b/features/admins/admin_works.feature
index eeebd918748..acfd6ca877b 100644
--- a/features/admins/admin_works.feature
+++ b/features/admins/admin_works.feature
@@ -227,6 +227,7 @@ Feature: Admin Actions for Works, Comments, Series, Bookmarks
When I am logged in as a "policy_and_abuse" admin
And I view the work "The One Where Neal is Awesome"
And I follow "Comments (1)"
+ And it is currently 1 second from now
And I follow "Not Spam"
Then I should see "Hide Comments (2)"
And I should not see "Not Spam"
diff --git a/features/bookmarks/bookmark_create.feature b/features/bookmarks/bookmark_create.feature
index 72d1d8efe72..e56da0f057c 100644
--- a/features/bookmarks/bookmark_create.feature
+++ b/features/bookmarks/bookmark_create.feature
@@ -108,6 +108,18 @@ Scenario: extra commas in bookmark form (Issue 2284)
And I press "Create"
Then I should see "created"
+Scenario: Bookmark notes do not display images
+ Given I am logged in as "bookmarkuser"
+ And I post the work "Some Work"
+ When I follow "Bookmark"
+ And I fill in "Notes" with "Fantastic!"
+ And I press "Create"
+ And all indexing jobs have been run
+ Then I should see "Bookmark was successfully created"
+ When I go to the bookmarks page
+ Then I should not see the image "src" text "http://example.com/icon.svg"
+ And I should see "Fantastic!"
+
Scenario: bookmark added to moderated collection has flash notice only when not approved
Given the following activated users exist
| login | password |
diff --git a/features/collections/collection_participants.feature b/features/collections/collection_participants.feature
index 84d1222b182..868faa442a1 100644
--- a/features/collections/collection_participants.feature
+++ b/features/collections/collection_participants.feature
@@ -66,3 +66,12 @@
And I follow "Join"
Then I should see "You are now a member of Such a nice collection"
When I am in the default browser
+
+Scenario: Collection member should see correct button text
+ Given I have the moderated collection "ModeratedCollection"
+ And I have the moderated collection "ModeratedCollectionTheSequel"
+ And I am logged in as "sam"
+ And I have joined the collection "ModeratedCollection" as "sam"
+ When I am on the collections page
+ Then I should see "Leave" exactly 1 time
+ And I should see "Join" exactly 1 time
\ No newline at end of file
diff --git a/features/collections/icon.feature b/features/collections/icon.feature
index 6d9a5495a21..e7e5ab86106 100644
--- a/features/collections/icon.feature
+++ b/features/collections/icon.feature
@@ -40,3 +40,15 @@ Feature: User icons
When I delete the icon from the collection "Pretty"
Then I should see "Collection was successfully updated."
And the "Pretty" collection should not have an icon
+
+ Scenario: Users can delete icon and alt text
+
+ Given I have an icon uploaded
+ When I follow "Edit Pseud"
+ And I fill in "pseud_icon_alt_text" with "Some test description"
+ And I press "Update"
+ Then I should see the image "alt" text "Some test description"
+ When I delete the icon from my pseud
+ Then I should see "Pseud was successfully updated."
+ When I follow "Edit Pseud"
+ Then I should see the icon and alt text boxes are blank
diff --git a/features/comments_and_kudos/add_comment.feature b/features/comments_and_kudos/add_comment.feature
index 7d6bd91def6..c4613c550a8 100644
--- a/features/comments_and_kudos/add_comment.feature
+++ b/features/comments_and_kudos/add_comment.feature
@@ -133,7 +133,7 @@ Scenario: Comment threading, comment editing
And I fill in "Comment" with "B's improved comment (edited)"
And I press "Update"
Then 0 emails should be delivered to "User_A"
-
+
Scenario: Try to post an invalid comment
When I am logged in as "author"
@@ -180,6 +180,17 @@ Scenario: Set preference and receive comment notifications of your own comments
And "commenter" should be emailed
And 1 email should be delivered to "commenter"
+Scenario: Work comment displays images
+
+ Given the work "Generic Work"
+ And I am logged in as "commenter"
+ And I visit the new comment page for the work "Generic Work"
+ When I fill in "Comment" with "Fantastic!"
+ And I press "Comment"
+ Then I should see "Comment created!"
+ And I should see "Fantastic!"
+ And I should see the image "src" text "http://example.com/icon.svg"
+
Scenario: Try to post a comment with a < angle bracket before a linebreak, without a space before the bracket
Given the work "Generic Work"
@@ -194,7 +205,7 @@ Scenario: Try to post a comment with a < angle bracket before a linebreak, witho
And I press "Comment"
Then I should see "Comment created!"
-Scenario: Try to post a comment with a < angle bracket before a linebreak, with a space before the bracket
+Scenario: Try to post a comment with a < angle bracket before a linebreak, with a space before the bracket
Given the work "Generic Work"
And I am logged in as "commenter"
@@ -207,3 +218,20 @@ Scenario: Try to post a comment with a < angle bracket before a linebreak, with
"""
And I press "Comment"
Then I should see "Comment created!"
+
+Scenario: Users with different time zone preferences should see the time in their own timezone
+ Given the work "Generic Work"
+ And I am logged in as "commenter"
+ And I set my time zone to "UTC"
+ And I post the comment "Something" on the work "Generic Work"
+ And it is currently 1 second from now
+ And I follow "Edit"
+ And I fill in "Comment" with "Something else"
+ And I press "Update"
+ Then I should see "UTC" within ".posted.datetime"
+ And I should see "UTC" within ".edited.datetime"
+ When I am logged in as "reader"
+ And I set my time zone to "Brisbane"
+ And I view the work "Generic Work" with comments
+ Then I should see "AEST" within ".posted.datetime"
+ And I should see "AEST" within ".edited.datetime"
diff --git a/features/comments_and_kudos/comments_adminposts.feature b/features/comments_and_kudos/comments_adminposts.feature
index da130cc1d0e..a5060948daa 100644
--- a/features/comments_and_kudos/comments_adminposts.feature
+++ b/features/comments_and_kudos/comments_adminposts.feature
@@ -135,3 +135,14 @@ Feature: Commenting on admin posts
When I follow "Edit Post"
Then I should see "No one can comment"
# TODO: Test that the other options aren't available/selected in a non-brittle way
+
+ Scenario: Admin post comment does not display images
+ Given I have posted an admin post
+ And I am logged in as "regular"
+ And I go to the admin-posts page
+ And I follow "Default Admin Post"
+ When I fill in "Comment" with "Hi!"
+ And I press "Comment"
+ Then I should see "Comment created!"
+ And I should not see the image "src" text "http://example.com/icon.svg"
+ And I should see "Hi!"
diff --git a/features/comments_and_kudos/inbox.feature b/features/comments_and_kudos/inbox.feature
index ee7d85bc285..545e6665d3b 100644
--- a/features/comments_and_kudos/inbox.feature
+++ b/features/comments_and_kudos/inbox.feature
@@ -159,3 +159,12 @@ Feature: Get messages in the inbox
And I go to the homepage
Then I should see "sewwiththeflo on Cat Thor's Bizarre Adventure"
And I should see "Thank you! Please go to bed."
+
+ Scenario: Reply to a comment on an admin post that contains an image
+ Given I have posted an admin post
+ And a comment "My comment" by "sewwiththeflo" on the admin post "Default Admin Post"
+ And a reply "My reply " by "unbeatablesg" on the admin post "Default Admin Post"
+ When I am logged in as "sewwiththeflo"
+ And I go to the homepage
+ Then I should see "My reply"
+ And I should not see ""
diff --git a/features/comments_and_kudos/kudos.feature b/features/comments_and_kudos/kudos.feature
index 8a7e67eff36..f5d5fd03cc1 100644
--- a/features/comments_and_kudos/kudos.feature
+++ b/features/comments_and_kudos/kudos.feature
@@ -174,3 +174,24 @@ Feature: Kudos
When I am logged in as "pest"
And I view the work "Aftermath"
Then I should not see a "Kudos ♥" button
+
+ Scenario: Kudos cache expires periodically to ensure deleted users are removed and renamed users are updated
+ Given the work "Interesting beans"
+ And I am logged in as "oldusername1" with password "password"
+ When I view the work "Interesting beans"
+ And I press "Kudos ♥"
+ Then I should see "oldusername1 left kudos on this work!"
+ When I visit the change username page for oldusername1
+ And I fill in "New user name" with "newusername1"
+ And I fill in "Password" with "password"
+ And I press "Change User Name"
+ Then I should get confirmation that I changed my username
+ And I should see "Hi, newusername1"
+ When the kudos cache has expired
+ And I view the work "Interesting beans"
+ Then I should see "newusername1 left kudos on this work!"
+ And I should not see "oldusername1"
+ When I try to delete my account as newusername1
+ And the kudos cache has expired
+ And I view the work "Interesting beans"
+ Then I should not see "newusername1 left kudos on this work!"
diff --git a/features/gift_exchanges/challenge_giftexchange.feature b/features/gift_exchanges/challenge_giftexchange.feature
index 424f0c209ac..13c95d5d5bd 100644
--- a/features/gift_exchanges/challenge_giftexchange.feature
+++ b/features/gift_exchanges/challenge_giftexchange.feature
@@ -641,3 +641,37 @@ Feature: Gift Exchange Challenge
And I uncheck "exchange_collection (recip)"
And I press "Post"
Then I should see "For recip."
+
+ Scenario: If a work is connected to an assignment for a user who blocked the gifter,
+ user is still automatically added as a gift recipient. The recipient
+ remains attached even if the work is later disconnected from the assignment.
+ Given basic tags
+ And the user "recip" exists and is activated
+ And the user "recip" allows gifts
+ And the user "recip" has blocked the user "gifter"
+ And I am logged in as "gifter"
+ And I have an assignment for the user "recip" in the collection "exchange_collection"
+ When I fulfill my assignment
+ Then I should see "For recip."
+ When I follow "Edit"
+ And I uncheck "exchange_collection (recip)"
+ And I press "Post"
+ Then I should see "For recip."
+
+ Scenario: A user can explicitly give a gift to a user who blocked the gifter if
+ the work is connected to an assignment. The recipient remains attached even if
+ the work is later disconnected from the assignment.
+ Given basic tags
+ And the user "recip" exists and is activated
+ And the user "recip" allows gifts
+ And the user "recip" has blocked the user "gifter"
+ And I am logged in as "gifter"
+ And I have an assignment for the user "recip" in the collection "exchange_collection"
+ When I start to fulfill my assignment
+ And I fill in "Gift this work to" with "recip"
+ And I press "Post"
+ Then I should see "For recip."
+ When I follow "Edit"
+ And I uncheck "exchange_collection (recip)"
+ And I press "Post"
+ Then I should see "For recip."
diff --git a/features/other_a/abuse_report.feature b/features/other_a/abuse_report.feature
index e325ed64718..01636e32302 100644
--- a/features/other_a/abuse_report.feature
+++ b/features/other_a/abuse_report.feature
@@ -59,3 +59,15 @@ Feature: Filing an abuse report
And I press "Submit"
And I should see "Your report was submitted to the Policy & Abuse team. A confirmation message has been sent to the email address you provided."
And 1 email should be delivered
+
+ Scenario: File a report containing images
+
+ Given I am logged in as "otheruser"
+ And basic languages
+ When I follow "Policy Questions & Abuse Reports"
+ And I fill in "Description of the content you are reporting (required)" with "This is wrong"
+ And I fill in "Brief summary of Terms of Service violation (required)" with 'Hi'
+ And I fill in "Link to the page you are reporting (required)" with "http://www.archiveofourown.org/works"
+ And I press "Submit"
+ Then 1 email should be delivered
+ And the email should not contain ""
diff --git a/features/other_a/banner_general.feature b/features/other_a/banner_general.feature
index 355ef2f0895..490301814ec 100644
--- a/features/other_a/banner_general.feature
+++ b/features/other_a/banner_general.feature
@@ -118,3 +118,25 @@ Scenario: Admin can make minor changes to the text of an active banner without t
Then I should see the banner with minor edits
When I am logged in as "banner_tester_4"
Then I should see the banner with minor edits
+
+Scenario: Development & Membership admin can see edit options but not delete or create
+ Given an admin creates a banner
+ When I am logged in as a "development_and_membership" admin
+ And I go to the admin_banners page
+ Then I should see "Banners" within "#header .admin.navigation"
+ And I should see "Banners" within "#main .navigation.actions"
+ And I should see "Edit" within "#main ul.banners.index.group"
+ But I should not see "Delete" within "#main ul.banners.index.group"
+ And I should not see "New Banner" within "#main .navigation.actions"
+ When I follow "Edit"
+ Then I should not see "New Banner" within "#main .navigation.actions"
+ And I should not see "Delete Banner" within "#main .navigation.actions"
+ But I should see "Edit Banner" within "#main h2"
+ And I should see "Edit Banner" within "#main .navigation.actions"
+ When I fill in "Banner text" with "Some fun new text"
+ And I press "Update Banner"
+ Then I should see "Banner successfully updated."
+ And I should see "Banners" within "#main .navigation.actions"
+ And I should see "Edit Banner" within "#main .navigation.actions"
+ But I should not see "Delete Banner" within "#main .navigation.actions"
+ And I should not see "New Banner" within "#main .navigation.actions"
diff --git a/features/other_a/gift.feature b/features/other_a/gift.feature
index ec39358f45f..8d77e3b5171 100644
--- a/features/other_a/gift.feature
+++ b/features/other_a/gift.feature
@@ -337,3 +337,57 @@ Feature: Create Gifts
And I should not see "by gifter for giftee1"
When I view the work "Rude Gift"
Then I should not see "For giftee1."
+
+ Scenario: Can't give a gift to a user who has blocked you
+ Given the user "giftee1" has blocked the user "gifter"
+ When I am logged in as "gifter"
+ And I post the work "Rude Gift" as a gift for "giftee1"
+ Then I should see "Sorry! We couldn't save this work because: giftee1 does not accept gifts from you."
+ And 0 emails should be delivered to "giftee1@example.com"
+
+ Scenario: Can't gift an existing work to a user who has blocked you
+ Given the user "giftee1" has blocked the user "gifter"
+ And I press "Post"
+ And I follow "Edit"
+ And I give the work to "giftee1"
+ When I press "Post"
+ Then I should see "Sorry! We couldn't save this work because: giftee1 does not accept gifts from you."
+
+ Scenario: Can't gift a work whose co-creator is blocked by recipient
+ Given I coauthored the work "Collateral" as "gifter" with "gifter2"
+ And the user "giftee1" has blocked the user "gifter2"
+ And I edit the work "Collateral"
+ And I give the work to "giftee1"
+ When I press "Post"
+ Then I should see "Sorry! We couldn't save this work because: giftee1 does not accept gifts."
+
+ Scenario: Only see one error message is shown if gifts are disabled and user is blocked*
+ Given the user "giftee1" disallows gifts
+ And the user "giftee1" has blocked the user "gifter"
+ When I am logged in as "gifter"
+ And I post the work "Rude Gift" as a gift for "giftee1"
+ Then I should see "Sorry! We couldn't save this work because:"
+ And I should see "giftee1 does not accept gifts."
+ And I should not see "giftee1 does not accept gifts from you."
+
+ Scenario: A user can refuse previous gifts from user after blocking them
+ Given I am logged in as "gifter"
+ And I post the work "Rude Gift" as a gift for "giftee1"
+ When I am logged in as "giftee1"
+ And I go to my gifts page
+ Then I should see "Rude Gift"
+ When I go to my blocked users page
+ And I fill in "blocked_id" with "gifter"
+ And I press "Block"
+ And I press "Yes, Block User"
+ Then I should see "You have blocked the user gifter."
+ When I go to my gifts page
+ And it is currently 1 second from now
+ And I follow "Refuse Gift"
+ Then I should see "This work will no longer be listed among your gifts."
+ And I should not see "Rude Gift"
+ When I follow "Refused Gifts"
+ Then I should see "Rude Gift"
+ And I should not see "by gifter for giftee1"
+ When I view the work "Rude Gift"
+ Then I should not see "For giftee1."
diff --git a/features/other_a/help.feature b/features/other_a/help.feature
index 2f39d8875ca..da05dc690bc 100644
--- a/features/other_a/help.feature
+++ b/features/other_a/help.feature
@@ -4,21 +4,21 @@ Feature: Help
As a humble user
I want to read help links
-Scenario: clicking the help popup for moderated collection
-
- Given I am logged in as "first_user"
- When I go to the collections page
- When I follow "New Collection"
+ Scenario: clicking the help popup for moderated collection
+
+ Given I am logged in as "first_user"
+ When I go to the collections page
+ When I follow "New Collection"
And I follow "Collection moderated"
- Then I should see "By default, collections are not moderated"
-
-Scenario: view the help popup for chapter title
+ Then I should see "By default, collections are not moderated"
+
+ Scenario: view the help popup for chapter title
- Given the following activated user exists
- | login | password |
- | epicauthor | password |
+ Given the following activated user exists
+ | login | password |
+ | epicauthor | password |
And basic tags
- When I am logged in as "epicauthor"
+ When I am logged in as "epicauthor"
And I go to epicauthor's user page
And I follow "New Work"
And I select "Not Rated" from "Rating"
@@ -30,4 +30,13 @@ Scenario: view the help popup for chapter title
And I press "Post"
And I follow "Add Chapter"
And I follow "Chapter title"
- Then I should see "You can add a chapter title"
\ No newline at end of file
+ Then I should see "You can add a chapter title"
+
+ Scenario: Asked to log in if trying to access the first login page as guest
+
+ When I go to the first login help page
+ Then I should be on the login page
+
+ Given I am logged in
+ When I go to the first login help page
+ Then I should see "Here are some tips to help you get started"
diff --git a/features/other_a/orphan_account.feature b/features/other_a/orphan_account.feature
index 76bceb692bc..9461d1b42e2 100644
--- a/features/other_a/orphan_account.feature
+++ b/features/other_a/orphan_account.feature
@@ -23,7 +23,9 @@ Scenario: Orphan all works belonging to a user
Then I should see "Orphan All Works"
And I should see "Are you really sure you want to"
When I choose "Take my pseud off as well"
- And I press "Yes, I'm sure"
+ # Delay before orphaning to make sure the cache is expired
+ And it is currently 1 second from now
+ And I press "Yes, I'm sure"
Then I should see "Orphaning was successful."
When I view the work "Shenanigans"
Then I should see "orphan_account"
@@ -54,7 +56,9 @@ Given I have an orphan account
Then I should see "Orphan All Works"
And I should see "Are you really sure you want to"
When I choose "Leave a copy of my pseud on"
- And I press "Yes, I'm sure"
+ # Delay before orphaning to make sure the cache is expired
+ And it is currently 1 second from now
+ And I press "Yes, I'm sure"
Then I should see "Orphaning was successful."
When I view the work "Shenanigans"
Then I should see "orphaneer (orphan_account)"
diff --git a/features/other_a/profile_edit.feature b/features/other_a/profile_edit.feature
index d383bcf2941..4f6186226ca 100644
--- a/features/other_a/profile_edit.feature
+++ b/features/other_a/profile_edit.feature
@@ -13,21 +13,25 @@ Background:
Scenario: Add details
+ Then I should see the page title "Edit Profile"
When I fill in the details of my profile
Then I should see "Your profile has been successfully updated"
And 0 emails should be delivered
+ And I should see "I live in"
Scenario: Change details
When I change the details in my profile
Then I should see "Your profile has been successfully updated"
And 0 emails should be delivered
+ And I should see "I live in"
Scenario: Remove details
When I remove details from my profile
Then I should see "Your profile has been successfully updated"
And 0 emails should be delivered
+ And I should not see "I live in"
Scenario: Change details as an admin
diff --git a/features/other_a/pseuds.feature b/features/other_a/pseuds.feature
index 50379fcfc78..077c1407e00 100644
--- a/features/other_a/pseuds.feature
+++ b/features/other_a/pseuds.feature
@@ -118,6 +118,18 @@ Scenario: Manage pseuds - add, edit
And I should see "I wanted to add another fancy name"
And I should not see "My new name (editpseuds)"
+Scenario: Pseud descriptions do not display images
+
+ Given I am logged in as "myself"
+ And I go to my pseuds page
+ When I follow "Edit"
+ And I fill in "Description" with "Fantastic!"
+ And I press "Update"
+ Then I should see "Pseud was successfully updated."
+ When I follow "Back To Pseuds"
+ Then I should not see the image "src" text "http://example.com/icon.svg"
+ And I should see "Fantastic!"
+
Scenario: Comments reflect pseud changes immediately
Given the work "Interesting"
diff --git a/features/other_b/errors.feature b/features/other_b/errors.feature
index a7dff1b681b..7bc6c78696d 100644
--- a/features/other_b/errors.feature
+++ b/features/other_b/errors.feature
@@ -1,6 +1,5 @@
@errors
-Feature: We need to do something when someone asks for something we don't have
-Some pages with non existent things raise errors
+Feature: Error messages should work
Scenario: Some pages with non existent things raise errors
Given the user "KnownUser" exists and is activated
@@ -24,3 +23,12 @@ Some pages with non existent things raise errors
And visiting "/tags/UnknownTag/works" should fail with a not found error
When I am logged in as "wranglerette"
And visiting "/tags/NonexistentTag/edit" should fail with a not found error
+
+ Scenario: Error messages should be able to display '^'
+ Given I am logged in as a random user
+ And I post the work "Work 1"
+ And I view the work "Work 1"
+ And I follow "Edit Tags"
+ When I fill in "Fandoms" with "^"
+ And I press "Post"
+ Then I should see "Sorry! We couldn't save this work because: Tag name '^' cannot include the following restricted characters: , ^ * < > { } = ` , 、 \ %"
diff --git a/features/other_b/fandoms.feature b/features/other_b/fandoms.feature
index 8fba205aae1..06dcf699f66 100644
--- a/features/other_b/fandoms.feature
+++ b/features/other_b/fandoms.feature
@@ -34,6 +34,7 @@ Feature: There is a list of unassigned Fandoms
And I add the fandom "Steven Universe" to the character "Sapphire (Steven Universe)"
And I am logged in as "author"
And I post the work "Stronger than you" with fandom "Steven Universe" with character "Ruby (Steven Universe)" with second character "Sapphire (Steven Universe)" with relationship "Ruby/Sapphire (Steven Universe)"
- When I go to the "Steven Universe" fandom relationship page
+ When I go to the "Steven Universe" tag page
+ And I follow "Relationship tags in this fandom"
Then I should see "Ruby (Steven Universe)"
And I should see "Sapphire (Steven Universe)"
diff --git a/features/other_b/series.feature b/features/other_b/series.feature
index f1208224c8c..f1c822734ca 100644
--- a/features/other_b/series.feature
+++ b/features/other_b/series.feature
@@ -44,14 +44,21 @@ Feature: Create and Edit Series
Scenario: Works in a series have series navigation
Given I am logged in as "author"
And I post the work "Sweetie Belle" as part of a series "Ponies"
+ And it is currently 1 second from now
And I post the work "Starsong" as part of a series "Ponies"
+ And it is currently 1 second from now
And I post the work "Rainbow Dash" as part of a series "Ponies"
When I view the series "Ponies"
And I follow "Rainbow Dash"
Then I should see "Part 3 of Ponies"
+ And I should not see "Next Work →"
When I follow "← Previous Work"
Then I should see "Starsong"
+ And I should see "Next Work →" within ".work.meta .next"
+ And I should see "Next Work →" within ".afterword .next"
When I follow "← Previous Work"
+ And I should see "Next Work →" within ".work.meta .next"
+ And I should see "Next Work →" within ".afterword .next"
Then I should see "Sweetie Belle"
When I follow "Next Work →"
Then I should see "Starsong"
@@ -179,6 +186,22 @@ Feature: Create and Edit Series
Then I should see "penguins30"
When I follow "Next"
Then I should see "penguins0"
+
+ Scenario: Series show page with many works
+ Given I am logged in as "author"
+ And I post the work "Caesar" as part of a series "Salads"
+ And I post the work "Chicken" as part of a series "Salads"
+ And I post the work "Pasta" as part of a series "Salads"
+ And I post the work "Spring" as part of a series "Salads"
+ And I post the work "Chef" as part of a series "Salads"
+ And there are 3 works per series page
+ When I view the series "Salads"
+ Then I should see "Caesar"
+ And I should see "Chicken"
+ And I should see "Pasta"
+ When I follow "Next"
+ Then I should see "Spring"
+ And I should see "Chef"
Scenario: Removing self as co-creator from co-created series when you are the only creator of a work in the series.
Given I am logged in as "sun"
diff --git a/features/other_b/series_order.feature b/features/other_b/series_order.feature
index a65b5918b8d..421c0b3e20a 100644
--- a/features/other_b/series_order.feature
+++ b/features/other_b/series_order.feature
@@ -29,3 +29,42 @@ Feature: Rearrange works within a series
And I should see "1. A Bad, Bad Night"
And I should see "2. Things Get Worse"
And I should see "3. A Bad, Bad Day"
+
+ @javascript
+ Scenario: Reordering series by drag and drop updates work blurbs and meta correctly.
+ Given I am logged in as "author"
+ And I post the work "A Bad, Bad Day" as part of a series "Tale of Woe"
+ And I post the work "A Bad, Bad Night" as part of a series "Tale of Woe"
+ And I post the work "Things Get Worse" as part of a series "Tale of Woe"
+ # Blurbs
+ When I view the series "Tale of Woe"
+ Then I should see "Part 1 of Tale of Woe" within ".work.blurb:first-child"
+ Then I should see "Part 2 of Tale of Woe" within ".work.blurb:nth-child(2)"
+ Then I should see "Part 3 of Tale of Woe" within ".work.blurb:nth-child(3)"
+ # Meta
+ When I view the work "A Bad, Bad Day"
+ Then I should see "Part 1 of Tale of Woe"
+ When I view the work "A Bad, Bad Night"
+ Then I should see "Part 2 of Tale of Woe"
+ When I view the work "Things Get Worse"
+ When I view the series "Tale of Woe"
+ And I follow "Reorder Series"
+ And I reorder the 2nd work to be below the 3rd work in the series
+ And I press "Update Positions"
+ Then I should see "Series order has been successfully updated"
+ # Blurbs
+ And I should see "Part 1 of Tale of Woe" within ".work.blurb:first-child"
+ And I should see "Part 2 of Tale of Woe" within ".work.blurb:nth-child(2)"
+ And I should see "Part 3 of Tale of Woe" within ".work.blurb:nth-child(3)"
+ When I follow "Reorder Series"
+ Then I should see "1. A Bad, Bad Day"
+ And I should see "2. Things Get Worse"
+ And I should see "3. A Bad, Bad Night"
+ # Meta
+ When I view the work "A Bad, Bad Day"
+ When I view the work "A Bad, Bad Day"
+ Then I should see "Part 1 of Tale of Woe"
+ When I view the work "Things Get Worse"
+ Then I should see "Part 2 of Tale of Woe"
+ When I view the work "A Bad, Bad Night"
+ Then I should see "Part 3 of Tale of Woe"
diff --git a/features/other_b/skin.feature b/features/other_b/skin.feature
index f1724c7fe81..a13dc513fdb 100755
--- a/features/other_b/skin.feature
+++ b/features/other_b/skin.feature
@@ -12,7 +12,7 @@ Feature: Non-public site and work skins
And I should see "text-decoration: blink;"
And I should see "(No Description Provided)"
And I should see "by skinner"
- But I should see "Use"
+ But I should see a button with text "Use"
And I should see "Delete"
And I should see "Edit"
And I should not see "Stop Using"
@@ -40,7 +40,6 @@ Feature: Non-public site and work skins
Given I am logged in as "skinner"
And I create the skin "my blinking skin" with css "#title { text-decoration: blink;}"
Then I should see "my blinking skin"
- And I should see "Use"
When I press "Use"
Then I should see "#title {" in the page style
And I should see "text-decoration: blink;" in the page style
diff --git a/features/other_b/support.feature b/features/other_b/support.feature
index d8733c4e088..2f31585e9ca 100644
--- a/features/other_b/support.feature
+++ b/features/other_b/support.feature
@@ -46,4 +46,14 @@ Feature: Filing a support request
And I press "Send"
Then I should see "Your message was sent to the Archive team - thank you!"
And 1 email should be delivered
-
+
+ Scenario: Submit a request containing an image
+
+ Given I am logged in as "puzzled"
+ And basic languages
+ When I follow "Support & Feedback"
+ And I fill in "Brief summary" with "Just a brief note"
+ And I fill in "Your question or problem" with 'Hi'
+ And I press "Send"
+ Then 1 email should be delivered
+ And the email should not contain ""
diff --git a/features/prompt_memes_b/challenge_promptmeme_posting_fills.feature b/features/prompt_memes_b/challenge_promptmeme_posting_fills.feature
index 38691a84848..e15e8e7c5fb 100755
--- a/features/prompt_memes_b/challenge_promptmeme_posting_fills.feature
+++ b/features/prompt_memes_b/challenge_promptmeme_posting_fills.feature
@@ -542,3 +542,20 @@ Feature: Prompt Meme Challenge
And I fill in "Gift this work to" with "prompter, bystander"
And I press "Post"
Then I should see "bystander does not accept gifts."
+
+ Scenario: A creator can give a gift to a user who has blocked them if the work is connected to a claim of a non-anonymous prompt belonging to the recipient
+
+ Given I have Battle 12 prompt meme fully set up
+ And the user "prompter" exists and is activated
+ And the user "prompter" has blocked the user "gifter"
+ And "prompter" has signed up for Battle 12 with combination A
+ When I am logged in as "gifter"
+ And I claim a prompt from "Battle 12"
+ And I start to fulfill my claim
+ And I fill in "Gift this work to" with "prompter"
+ And I press "Post"
+ Then I should see "For prompter."
+ When I follow "Edit"
+ And I uncheck "Battle 12 (prompter)"
+ And I press "Post"
+ Then I should see "For prompter."
diff --git a/features/step_definitions/bookmark_steps.rb b/features/step_definitions/bookmark_steps.rb
index 65f15a7afb8..f5c15665217 100644
--- a/features/step_definitions/bookmark_steps.rb
+++ b/features/step_definitions/bookmark_steps.rb
@@ -348,6 +348,7 @@ def submit_bookmark_form(pseud, note, tags)
When /^I bookmark the works "([^\"]*)"$/ do |worklist|
worklist.split(/, ?/).each do |work_title|
step %{I bookmark the work "#{work_title}"}
+ step %{it is currently 1 second from now}
end
end
diff --git a/features/step_definitions/collection_steps.rb b/features/step_definitions/collection_steps.rb
index c01aba626fa..6daa52bf489 100644
--- a/features/step_definitions/collection_steps.rb
+++ b/features/step_definitions/collection_steps.rb
@@ -117,6 +117,13 @@
step %{I should see "Updated #{name}"}
end
+Given "I have joined the collection {string} as {string}" do |title, login|
+ collection = Collection.find_by(title: title)
+ user = User.find_by(login: login)
+ FactoryBot.create(:collection_participant, pseud: user.default_pseud, collection: collection, participant_role: "Member")
+ visit collections_path
+end
+
### WHEN
When /^I set up (?:a|the) collection "([^"]*)"(?: with name "([^"]*)")?$/ do |title, name|
diff --git a/features/step_definitions/generic_steps.rb b/features/step_definitions/generic_steps.rb
index 7f53e72c35c..db802cc0f50 100644
--- a/features/step_definitions/generic_steps.rb
+++ b/features/step_definitions/generic_steps.rb
@@ -271,3 +271,7 @@ def assure_xpath_not_present(tag, attribute, value, selector)
Time.zone = zone
page.body.should =~ /#{Regexp.escape(Time.zone.now.zone)}/
end
+
+Then "I should see {string} exactly {int} time(s)" do |string, int|
+ expect(page).to have_content(string).exactly(int)
+end
diff --git a/features/step_definitions/icon_steps.rb b/features/step_definitions/icon_steps.rb
index 6edd808c42b..0cb200d2d80 100644
--- a/features/step_definitions/icon_steps.rb
+++ b/features/step_definitions/icon_steps.rb
@@ -34,6 +34,12 @@
step %{I press "Update"}
end
+When "I delete the icon from my pseud" do
+ visit edit_user_pseud_path(User.current_user, User.current_user.default_pseud)
+ check("pseud_delete_icon")
+ step %{I press "Update"}
+end
+
Then /^the "([^"]*)" collection should have an icon$/ do |title|
collection = Collection.find_by(title: title)
assert !collection.icon_file_name.blank?
@@ -45,3 +51,8 @@
end
### THEN
+
+Then "I should see the icon and alt text boxes are blank" do
+ expect(find("#pseud_icon").value).to be_blank
+ expect(find("#pseud_icon_alt_text").value).to be_nil
+end
diff --git a/features/step_definitions/kudos_steps.rb b/features/step_definitions/kudos_steps.rb
index d5b9c06aa40..9052f90d70f 100644
--- a/features/step_definitions/kudos_steps.rb
+++ b/features/step_definitions/kudos_steps.rb
@@ -24,6 +24,10 @@
click_button("kudo_submit")
end
+When "the kudos cache has expired" do
+ step "it is currently #{ArchiveConfig.MINUTES_UNTIL_COMMENTABLE_KUDOS_LISTS_EXPIRE} minutes from now"
+end
+
### THEN
Then /^I should see kudos on every chapter$/ do
diff --git a/features/step_definitions/potential_match_steps.rb b/features/step_definitions/potential_match_steps.rb
index faca5b115ac..3b2e8d9350b 100644
--- a/features/step_definitions/potential_match_steps.rb
+++ b/features/step_definitions/potential_match_steps.rb
@@ -60,8 +60,8 @@
# constraints simpler to express. (The tests are designed to verify
# potential match generation, not challenge signups, so we don't need
# that kind of fine-grained control.)
- collection.challenge.request_restriction.update(attributes)
- collection.challenge.offer_restriction.update(attributes)
+ collection.challenge.request_restriction.update!(attributes)
+ collection.challenge.offer_restriction.update!(attributes)
potential_match_settings.update_attribute(
"num_required_#{type.pluralize}", match
diff --git a/features/step_definitions/preferences_steps.rb b/features/step_definitions/preferences_steps.rb
index 8325d4cd1f4..cd3cd900f4b 100644
--- a/features/step_definitions/preferences_steps.rb
+++ b/features/step_definitions/preferences_steps.rb
@@ -40,7 +40,7 @@
Given "the user {string} is hidden from search engines" do |login|
user = User.find_by(login: login)
- user.preference.update(minimize_search_engines: true)
+ user.preference.update!(minimize_search_engines: true)
end
When /^I set my preferences to turn off notification emails for comments$/ do
diff --git a/features/step_definitions/series_steps.rb b/features/step_definitions/series_steps.rb
index 78dd9da169f..333aea5c9d4 100644
--- a/features/step_definitions/series_steps.rb
+++ b/features/step_definitions/series_steps.rb
@@ -1,5 +1,9 @@
When /^I view the series "([^\"]*)"$/ do |series|
- visit series_url(Series.find_by(title: series))
+ visit series_path(Series.find_by(title: series))
+end
+
+Given "there are {int} works per series page" do |amount|
+ allow(WillPaginate).to receive(:per_page).and_return(amount)
end
When /^I add the series "([^\"]*)"$/ do |series_title|
@@ -41,7 +45,7 @@
if work.blank?
step "the draft \"#{work_title}\""
work = Work.find_by(title: work_title)
- visit preview_work_url(work)
+ visit preview_work_path(work)
click_button("Post")
step "I should see \"Work was successfully posted.\""
step %{all indexing jobs have been run}
@@ -56,6 +60,24 @@
end
end
+When /^I reorder the (\d+)(?:st|nd|rd|th) work to be below the (\d+)(?:st|nd|rd|th) work in the series$/ do |n1, n2|
+ # Step only accounts for downward changes through a downward offset.
+ assert n1 < n2
+
+ draggable = find(".serial-position-list:nth-child(#{n1})")
+ droppable = find(".serial-position-list:nth-child(#{n2})")
+
+ # Capybara's drag_to method doesn't work well with this jQuery sortable list that has a default tolerance of "intersect".
+ # Using another way to simulate dragging. Credit to https://stackoverflow.com/questions/72369314/
+ webdriver = page.driver.browser
+ webdriver.action.click_and_hold(draggable.native).perform
+ step "I wait 1 second" # a delay is necessary.
+ # Add downward offset to make the rearrangement register.
+ webdriver.action.move_to(droppable.native, 0, 10).release.perform
+
+ step "all AJAX requests are complete"
+end
+
When /^I delete the series "([^"]*)"$/ do |series|
step %{I view the series "#{series}"}
step %{I follow "Delete Series"}
diff --git a/features/step_definitions/tag_steps.rb b/features/step_definitions/tag_steps.rb
index c2968d136d3..f2d3ba09b72 100644
--- a/features/step_definitions/tag_steps.rb
+++ b/features/step_definitions/tag_steps.rb
@@ -78,9 +78,9 @@
Given /^I have a canonical "([^\"]*)" fandom tag named "([^\"]*)"$/ do |media, fandom|
fandom = Fandom.find_or_create_by_name(fandom)
- fandom.update(canonical: true)
+ fandom.update!(canonical: true)
media = Media.find_or_create_by_name(media)
- media.update(canonical: true)
+ media.update!(canonical: true)
fandom.add_association media
end
diff --git a/features/step_definitions/user_steps.rb b/features/step_definitions/user_steps.rb
index 39793320b9b..e779c02a7a3 100644
--- a/features/step_definitions/user_steps.rb
+++ b/features/step_definitions/user_steps.rb
@@ -34,7 +34,7 @@
[hash[:password], salt].flatten.join,
cost: ArchiveConfig.BCRYPT_COST || 14)
- user.update(
+ user.update!(
password_salt: salt,
encrypted_password: encrypted_password
)
@@ -54,7 +54,7 @@
encrypted_password = [hash[:password], salt].flatten.join
20.times { encrypted_password = Digest::SHA512.hexdigest(encrypted_password) }
- user.update(
+ user.update!(
password_salt: salt,
encrypted_password: encrypted_password
)
@@ -163,9 +163,9 @@
Given(/^I coauthored the work "(.*?)" as "(.*?)" with "(.*?)"$/) do |title, login, coauthor|
step %{basic tags}
author1 = User.find_by(login: login).default_pseud
- author1.user.preference.update(allow_cocreator: true)
+ author1.user.preference.update!(allow_cocreator: true)
author2 = User.find_by(login: coauthor).default_pseud
- author2.user.preference.update(allow_cocreator: true)
+ author2.user.preference.update!(allow_cocreator: true)
work = FactoryBot.create(:work, authors: [author1, author2], title: title)
work.creatorships.unapproved.each(&:accept!)
end
@@ -205,7 +205,7 @@
When /^the user "([^\"]*)" has failed to log in (\d+) times$/ do |login, count|
user = User.find_by(login: login)
- user.update(failed_attempts: count.to_i)
+ user.update!(failed_attempts: count.to_i)
end
When /^I fill in the sign up form with valid data$/ do
diff --git a/features/step_definitions/work_steps.rb b/features/step_definitions/work_steps.rb
index 820650af4a3..c02c7647e12 100644
--- a/features/step_definitions/work_steps.rb
+++ b/features/step_definitions/work_steps.rb
@@ -69,7 +69,7 @@
# If the work is already a draft then visit the preview page and post it
work = Work.find_by(title: title)
if work
- visit preview_work_url(work)
+ visit preview_work_path(work)
click_button("Post")
else
# Note: this will match the above regexp and work just fine even if all the options are blank!
@@ -79,7 +79,7 @@
# Now add the chapters
if number_of_chapters.present? && number_of_chapters.to_i > 1
work = Work.find_by_title(title)
- visit work_url(work)
+ visit work_path(work)
(number_of_chapters.to_i - 1).times do
step %{I follow "Add Chapter"}
fill_in("content", with: "Yet another chapter.")
diff --git a/features/support/paths.rb b/features/support/paths.rb
index 95df95966e3..01a4bdc2fd7 100644
--- a/features/support/paths.rb
+++ b/features/support/paths.rb
@@ -285,8 +285,6 @@ def path_to(page_name)
edit_tag_path(Tag.find_by(name: Regexp.last_match(1)))
when /^the wrangling tools page$/
tag_wranglings_path
- when /^the "(.*)" fandom relationship page$/i
- fandom_path($1)
when /^the new external work page$/i
new_external_work_path
when /^the external works page$/i
diff --git a/features/users/blocking.feature b/features/users/blocking.feature
index d136e427c9a..d305915dd3f 100644
--- a/features/users/blocking.feature
+++ b/features/users/blocking.feature
@@ -121,3 +121,17 @@ Feature: Blocking
| superadmin |
| policy_and_abuse |
| support |
+
+ Scenario: Users are told about blocking effects on gift-giving
+ Given the user "pest" exists and is activated
+ And I am logged in as "blocker"
+ When I go to my blocked users page
+ Then I should see "giving you gift works"
+ Given the user "unblocker" has blocked the user "improving"
+ And I am logged in as "unblocker"
+ When I go to my blocked users page
+ Then I should see "improving"
+ And I should see "giving you gift works"
+ When I follow "Unblock"
+ Then I should see a "Yes, Unblock User" button
+ And I should see "giving you gift works"
diff --git a/features/users/user_dashboard.feature b/features/users/user_dashboard.feature
index b3a05b24aee..f2028e3646a 100644
--- a/features/users/user_dashboard.feature
+++ b/features/users/user_dashboard.feature
@@ -196,3 +196,46 @@ Feature: User dashboard
Then I should see "2 Works by meatloaf in Star Trek"
When I press "Sort and Filter"
Then I should see "2 Works by meatloaf in Star Trek"
+
+ Scenario: The dashboard sidebar series count should exclude restricted series when logged out
+ Given I have the anonymous collection "Anon works"
+ And I am logged in as "Accumulator"
+ And I add the pseud "Battery"
+ And I add the pseud "Centrifuge"
+ And I post the work "Normal work" as part of a series "Mine" using the pseud "Battery"
+ And I post the work "Normal work 2" as part of a series "Mine" using the pseud "Battery"
+ And I post the work "Restricted work" as part of a series "Restricted" using the pseud "Battery"
+ And I lock the work "Restricted work"
+ And I post the work "Another restricted work" as part of a series "Restricted" using the pseud "Battery"
+ And I lock the work "Another restricted work"
+ When I go to Accumulator's user page
+ Then I should see "Series (2)" within "#dashboard"
+ When I go to Accumulator's "Battery" pseud page
+ Then I should see "Series (2)" within "#dashboard"
+ When I go to Accumulator's "Centrifuge" pseud page
+ Then I should see "Series (0)" within "#dashboard"
+ When I am logged out
+ And I go to Accumulator's user page
+ Then I should see "Series (1)" within "#dashboard"
+ When I go to Accumulator's "Battery" pseud page
+ Then I should see "Series (1)" within "#dashboard"
+ When I go to Accumulator's "Centrifuge" pseud page
+ Then I should see "Series (0)" within "#dashboard"
+ # Series with anon works are never counted
+ When I am logged in as "Accumulator"
+ And I post the work "Another normal work" as part of a series "Anon" using the pseud "Battery"
+ And I post the work "Anon work" in the collection "Anon works" as part of a series "Anon" using the pseud "Battery"
+ And I go to Accumulator's user page
+ Then I should see "Series (2)" within "#dashboard"
+ When I go to Accumulator's "Battery" pseud page
+ Then I should see "Series (2)" within "#dashboard"
+ When I go to Accumulator's "Centrifuge" pseud page
+ Then I should see "Series (0)" within "#dashboard"
+ When I am logged out
+ And I go to Accumulator's user page
+ Then I should see "Series (1)" within "#dashboard"
+ When I go to Accumulator's "Battery" pseud page
+ Then I should see "Series (1)" within "#dashboard"
+ When I go to Accumulator's "Centrifuge" pseud page
+ Then I should see "Series (0)" within "#dashboard"
+
diff --git a/features/works/work_create.feature b/features/works/work_create.feature
index bbbf30effde..65c3cbb900a 100755
--- a/features/works/work_create.feature
+++ b/features/works/work_create.feature
@@ -200,7 +200,7 @@ Feature: Create Works
When I fill in "work_collection_names" with ""
And I fill in "pseud_byline" with "badcoauthor"
And I press "Preview"
- Then I should see "badcoauthor is currently banned"
+ Then I should see "badcoauthor cannot be listed as a co-creator"
When I fill in "pseud_byline" with "coauthor"
And I fill in "Additional Tags" with "this is a very long tag more than one hundred characters in length how would this normally even be created"
And I press "Preview"
diff --git a/lib/html_cleaner.rb b/lib/html_cleaner.rb
index f2636d00a16..a70e19517ec 100644
--- a/lib/html_cleaner.rb
+++ b/lib/html_cleaner.rb
@@ -2,17 +2,21 @@
module HtmlCleaner
# If we aren't sure that this field hasn't been sanitized since the last sanitizer version,
# we sanitize it before we allow it to pass through (and save it if possible).
- def sanitize_field(object, fieldname)
+ def sanitize_field(object, fieldname, strip_images: false)
return "" if object.send(fieldname).nil?
sanitizer_version = object.try("#{fieldname}_sanitizer_version")
- if sanitizer_version && sanitizer_version >= ArchiveConfig.SANITIZER_VERSION
- # return the field without sanitizing
- object.send(fieldname)
- else
- # no sanitizer version information, so re-sanitize
- sanitize_value(fieldname, object.send(fieldname))
- end
+ sanitized_field =
+ if sanitizer_version && sanitizer_version >= ArchiveConfig.SANITIZER_VERSION
+ # return the field without sanitizing
+ object.send(fieldname)
+ else
+ # no sanitizer version information, so re-sanitize
+ sanitize_value(fieldname, object.send(fieldname))
+ end
+
+ sanitized_field = strip_images(sanitized_field) if strip_images
+ sanitized_field
end
# yank out bad end-of-line characters and evil msword curly quotes
diff --git a/lib/otw_sanitize/embed_sanitizer.rb b/lib/otw_sanitize/embed_sanitizer.rb
index 950c8204821..573eed7ec0a 100644
--- a/lib/otw_sanitize/embed_sanitizer.rb
+++ b/lib/otw_sanitize/embed_sanitizer.rb
@@ -6,7 +6,7 @@
module OtwSanitize
# Creates a Sanitize transformer to sanitize embedded media
class EmbedSanitizer
- WHITELIST_REGEXES = {
+ ALLOWLIST_REGEXES = {
archiveorg: %r{^archive\.org\/embed/},
bilibili: %r{^(player\.)?bilibili\.com/},
criticalcommons: %r{^criticalcommons\.org/},
@@ -35,7 +35,7 @@ class EmbedSanitizer
def self.transformer
lambda do |env|
# Don't continue if this node is already safelisted.
- return if env[:is_whitelisted]
+ return if env[:is_allowlisted]
new(env[:node]).sanitized_node
end
@@ -80,11 +80,11 @@ def embed_node?
%w(embed iframe).include?(node_name)
end
- # Compare the url to our list of whitelisted sources
+ # Compare the url to our list of allowlisted sources
# and return the appropriate source symbol
def source
return @source if @source
- WHITELIST_REGEXES.each_pair do |name, reg|
+ ALLOWLIST_REGEXES.each_pair do |name, reg|
if source_url =~ reg
@source = name
break
@@ -141,7 +141,7 @@ def sanitize_object
disable_scripts(parent)
- { node_whitelist: [node, parent] }
+ { node_allowlist: [node, parent] }
end
def sanitize_embed
@@ -163,7 +163,7 @@ class type width
disable_scripts(node)
node['flashvars'] = "" unless allows_flashvars?
end
- { node_whitelist: [node] }
+ { node_allowlist: [node] }
end
# disable script access and networking
diff --git a/lib/otw_sanitize/media_sanitizer.rb b/lib/otw_sanitize/media_sanitizer.rb
index b2e09bc4de9..e5cab04e296 100644
--- a/lib/otw_sanitize/media_sanitizer.rb
+++ b/lib/otw_sanitize/media_sanitizer.rb
@@ -3,7 +3,7 @@
# Creates a Sanitize transformer to sanitize audio and video tags
module OtwSanitize
class MediaSanitizer
- # Attribute whitelists
+ # Attribute allowlists
AUDIO_ATTRIBUTES = %w[
class controls crossorigin dir
loop muted preload src title
@@ -17,7 +17,7 @@ class controls crossorigin dir height loop
SOURCE_ATTRIBUTES = %w[src type].freeze
TRACK_ATTRIBUTES = %w[default kind label src srclang].freeze
- WHITELIST_CONFIG = {
+ ALLOWLIST_CONFIG = {
elements: %w[
audio video source track
] + Sanitize::Config::ARCHIVE[:elements],
@@ -61,7 +61,7 @@ class controls crossorigin dir height loop
def self.transformer
lambda do |env|
# Don't continue if this node is already safelisted.
- return if env[:is_whitelisted]
+ return if env[:is_allowlisted]
new(env[:node]).sanitized_node
end
@@ -74,15 +74,15 @@ def initialize(node)
@node = node
end
- # Skip if it's not media or if we don't want to whitelist it
+ # Skip if it's not media or if we don't want to allowlist it
def sanitized_node
return unless media_node?
return if banned_source?
- config = Sanitize::Config.merge(Sanitize::Config::ARCHIVE, WHITELIST_CONFIG)
+ config = Sanitize::Config.merge(Sanitize::Config::ARCHIVE, ALLOWLIST_CONFIG)
Sanitize.clean_node!(node, config)
tidy_boolean_attributes(node)
- { node_whitelist: [node] }
+ { node_allowlist: [node] }
end
def node_name
diff --git a/public/403.html b/public/403.html
index b70ff07612c..1c057e36a9d 100644
--- a/public/403.html
+++ b/public/403.html
@@ -29,76 +29,76 @@
Your IP address has been blocked from accessing the Archive of Our Own. These blocks are generally long-term. If you believe you have been blocked by mistake, please contact us.
If you are receiving this error repeatedly, please contact Support. In the form, please include a link to the page you're trying to reach and how you're trying to reach this page.
To combat bots, we are currently banning IP addresses that post too many works in a short time period. If you see this page repeatedly please pause a while between posting works. If you are banned, you will be unable to access the Archive. Access will be restored 24 hours after the ban started.
The default is: 'Lucida Grande', 'Lucida Sans Unicode', 'GNU Unifont', Verdana, Helvetica, sans-serif
+
The default is: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, sans-serif, 'GNU Unifont'
Put any font name in here, and if it's installed on your computer, it'll work for you. If you use several different devices, specify some fall-back fonts, with commas in between the names, in case one of your devices doesn't have the first font.
You can use either single or double quotation marks around fonts with multi-word names, e.g. "Lucida Grande" or 'Lucide Sans Unicode'.
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index 9d5d9066e8c..84aa957879a 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -361,7 +361,6 @@ function setupDropdown(){
'data-target': '#'
});
$j('.dropdown').find('.menu').addClass("dropdown-menu");
- $j('.dropdown').find('.menu').children('li').attr("role", "menuitem");
}
// Accordion-style collapsible widgets
diff --git a/public/javascripts/bootstrap/bootstrap-dropdown.js b/public/javascripts/bootstrap/bootstrap-dropdown.js
index 716b39924d6..c8c92ab2e7e 100644
--- a/public/javascripts/bootstrap/bootstrap-dropdown.js
+++ b/public/javascripts/bootstrap/bootstrap-dropdown.js
@@ -19,9 +19,10 @@
* OTWARCHIVE DEVS:
*
* When updating to the newest version, make sure to include the
- * customizations from LINES 61-69 AND 177-183 and UPDATE THIS
- * MESSAGE with the new line numbers. These lines ensure proper
- * behavior when both JS and CSS hover are used for menus
+ * customizations from LINES 62-70, 103, AND 177-184 and UPDATE THIS
+ * MESSAGE with the new line numbers. These lines ensure the code works
+ * without the ARIA menu role and ensure proper behavior when both JS and
+ * CSS hover are used for menus.
* ========================================================== */
@@ -99,7 +100,7 @@
return $this.click()
}
- $items = $('[role=menu] li:not(.divider):visible a', $parent)
+ $items = $('ul.menu li:not(.divider):visible a', $parent)
if (!$items.length) return
@@ -173,13 +174,13 @@
.on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.dropdown-menu', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+ .on('keydown.dropdown.data-api', toggle + ', ul.menu' , Dropdown.prototype.keydown)
.on('mouseenter', '.dropdown', function (e) {
var $parent = $(this)
if ($parent.siblings('.open').length) {
$parent.children('ul').hide()
- }
+ }
})
.on('mouseleave', '.dropdown', function (e) { $(this).children('ul').removeAttr('') })
-}(window.jQuery);
\ No newline at end of file
+}(window.jQuery);
diff --git a/public/javascripts/bootstrap/bootstrap-dropdown.min.js b/public/javascripts/bootstrap/bootstrap-dropdown.min.js
index 45ab63130c2..e017f9598ae 100644
--- a/public/javascripts/bootstrap/bootstrap-dropdown.min.js
+++ b/public/javascripts/bootstrap/bootstrap-dropdown.min.js
@@ -1 +1 @@
-!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;if(!n){n=t.attr("href");n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")}r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;s=i(n);o=s.hasClass("open");r();if(o){s.children("ul").hide();n.blur()}else{s.toggleClass("open").children("ul").removeAttr("style");n.focus()}n.focus();return false},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this);n.preventDefault();n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r);a=u.hasClass("open");if(!a||a&&n.keyCode==27){if(n.which==27)u.find(t).focus();return r.click()}s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus"));if(n.keyCode==38&&f>0)f--;if(n.keyCode==40&&f0&&s--,40==t.keyCode&&s