app/liquid_tags i18n (#16125)
This commit is contained in:
parent
5ec47d99dc
commit
617d66c4e5
46 changed files with 599 additions and 364 deletions
|
|
@ -25,7 +25,7 @@ class AsciinemaTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def validate(id)
|
||||
raise "Invalid Asciinema ID: #{id}" unless id.match?(/\A\d+\z/)
|
||||
raise I18n.t("liquid_tags.asciinema_tag.invalid_asciinema_id", id: id) unless id.match?(/\A\d+\z/)
|
||||
|
||||
id
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class BlogcastTag < LiquidTagBase
|
|||
|
||||
def parse_id_or_url(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Blogcast ID" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.blogcast_tag.invalid_blogcast_id") unless match
|
||||
|
||||
match[:video_id]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ class CodepenTag < LiquidTagBase
|
|||
|
||||
# Validation
|
||||
validated_options = options.filter_map { |option| valid_option(option) }
|
||||
raise StandardError, "Invalid Options" unless options.empty? || !validated_options.empty?
|
||||
unless options.empty? || !validated_options.empty?
|
||||
raise StandardError, I18n.t("liquid_tags.codepen_tag.invalid_options")
|
||||
end
|
||||
|
||||
option = validated_options.join("&")
|
||||
|
||||
|
|
@ -52,7 +54,7 @@ class CodepenTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid CodePen URL"
|
||||
raise StandardError, I18n.t("liquid_tags.codepen_tag.invalid_codepen_url")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class CodesandboxTag < LiquidTagBase
|
|||
return [match[:id], parse_options(match[:options]&.split("&"))] if match
|
||||
|
||||
id = input.split.first
|
||||
raise StandardError, "CodeSandbox Error: Invalid ID" unless valid_id?(id)
|
||||
raise StandardError, I18n.t("liquid_tags.codesandbox_tag.invalid_id") unless valid_id?(id)
|
||||
|
||||
[id, parse_options(extract_options(input))]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CommentTag < LiquidTagBase
|
|||
|
||||
def parse_id_code(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Comment ID or URL" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.comment_tag.invalid_comment") unless match
|
||||
|
||||
match[:comment_id]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class DotnetFiddleTag < LiquidTagBase
|
|||
|
||||
def parse_link(link)
|
||||
match = pattern_match_for(link, [REGISTRY_REGEXP])
|
||||
raise StandardError, "Invalid DotnetFiddle URL" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.dotnet_fiddle_tag.invalid_dotnetfiddle_url") unless match
|
||||
|
||||
insert_widget(link, match)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class GistTag < LiquidTagBase
|
|||
def initialize(_tag_name, link, _parse_context)
|
||||
super
|
||||
|
||||
raise StandardError, "Invalid Gist link: You must provide a Gist link" if link.blank?
|
||||
raise StandardError, I18n.t("liquid_tags.gist_tag.invalid_gist_link") if link.blank?
|
||||
|
||||
@uri = build_uri(link)
|
||||
end
|
||||
|
|
@ -40,7 +40,7 @@ class GistTag < LiquidTagBase
|
|||
input_no_space
|
||||
else
|
||||
raise StandardError,
|
||||
"Invalid Gist link: #{link} Links must follow this format: https://gist.github.com/username/gist_id"
|
||||
I18n.t("liquid_tags.gist_tag.invalid_format", link: link)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ class GistTag < LiquidTagBase
|
|||
option_no_space = option.strip
|
||||
return "?#{option_no_space}" if valid_option?(option_no_space)
|
||||
|
||||
raise StandardError, "Invalid Filename"
|
||||
raise StandardError, I18n.t("liquid_tags.gist_tag.invalid_filename")
|
||||
end
|
||||
|
||||
def valid_link?(link)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class GitPitchTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid GitPitch URL"
|
||||
raise StandardError, I18n.t("liquid_tags.git_pitch_tag.invalid_gitpitch_url")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class GithubTag
|
|||
locals: {
|
||||
body: @body,
|
||||
created_at: @created_at.rfc3339,
|
||||
date: @created_at.utc.strftime("%b %d, %Y"),
|
||||
date: I18n.l(@created_at.utc, format: :github),
|
||||
html_url: @content_json[:html_url],
|
||||
issue_number: issue_number,
|
||||
tagline: tagline,
|
||||
|
|
@ -53,7 +53,7 @@ class GithubTag
|
|||
end
|
||||
|
||||
def generate_api_link(input)
|
||||
uri = URI.parse(input).normalize
|
||||
uri = Addressable::URI.parse(input).normalize
|
||||
uri.host = nil if uri.host == "github.com"
|
||||
|
||||
# public PRs URLs are "/pull/{id}" but the API requires "/pulls/{id}"
|
||||
|
|
@ -65,7 +65,7 @@ class GithubTag
|
|||
if uri.fragment&.start_with?("issuecomment-")
|
||||
uri.path = uri.path.gsub(%r{(issues|pulls)/\d+}, "issues/comments/")
|
||||
comment_id = uri.fragment.split("-").last
|
||||
uri.merge!(comment_id)
|
||||
uri.join!(comment_id)
|
||||
end
|
||||
|
||||
# fragments and query params are not needed in the API call
|
||||
|
|
@ -75,7 +75,7 @@ class GithubTag
|
|||
# remove leading forward slash in the path
|
||||
path = uri.path.delete_prefix("/")
|
||||
|
||||
URI.parse(API_BASE_ENDPOINT).merge(path).to_s
|
||||
Addressable::URI.parse(API_BASE_ENDPOINT).join(path).to_s
|
||||
end
|
||||
|
||||
def valid_link?(link)
|
||||
|
|
@ -89,11 +89,11 @@ class GithubTag
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid GitHub issue, pull request or comment link"
|
||||
raise StandardError, I18n.t("liquid_tags.github_tag.github_issue_tag.invalid_github_issue_pull")
|
||||
end
|
||||
|
||||
def title
|
||||
content_json[:title] || "Comment for"
|
||||
content_json[:title] || I18n.t("liquid_tags.github_tag.github_issue_tag.comment_for")
|
||||
end
|
||||
|
||||
def issue_number
|
||||
|
|
@ -101,7 +101,7 @@ class GithubTag
|
|||
end
|
||||
|
||||
def tagline
|
||||
@is_issue ? "posted on" : "commented on"
|
||||
@is_issue ? I18n.t("liquid_tags.github_tag.github_issue_tag.posted_on") : I18n.t("liquid_tags.github_tag.github_issue_tag.commented_on") # rubocop:disable Layout/LineLength
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class GithubTag
|
|||
validate_options!(*options)
|
||||
|
||||
path.delete_suffix!("/") # remove optional trailing forward slash
|
||||
repository_path = URI.parse(path)
|
||||
repository_path = Addressable::URI.parse(path)
|
||||
repository_path.query = repository_path.fragment = nil
|
||||
|
||||
[repository_path.normalize.to_s, options]
|
||||
|
|
@ -50,7 +50,8 @@ class GithubTag
|
|||
return if options.empty?
|
||||
return if options.all? { |o| VALID_OPTIONS.include?(o) }
|
||||
|
||||
message = "GitHub tag: invalid options: #{options - VALID_OPTIONS} - supported options: #{VALID_OPTIONS}"
|
||||
message = I18n.t("liquid_tags.github_tag.github_readme_tag.invalid_options",
|
||||
invalid: (options - VALID_OPTIONS), valid: VALID_OPTIONS)
|
||||
raise StandardError, message
|
||||
end
|
||||
|
||||
|
|
@ -72,7 +73,7 @@ class GithubTag
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid GitHub repository path or URL"
|
||||
raise StandardError, I18n.t("liquid_tags.github_tag.github_readme_tag.invalid_github_repository")
|
||||
end
|
||||
|
||||
def clean_relative_path!(readme_html, url)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class GlitchTag < LiquidTagBase
|
|||
def parse_id(input)
|
||||
id = input.split.first
|
||||
id.sub!(TILDE_PREFIX_REGEXP, "")
|
||||
raise StandardError, "Invalid Glitch ID" unless valid_id?(id)
|
||||
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_glitch_id") unless valid_id?(id)
|
||||
|
||||
id
|
||||
end
|
||||
|
|
@ -68,7 +68,9 @@ class GlitchTag < LiquidTagBase
|
|||
|
||||
# Validation
|
||||
validated_options = options.filter_map { |option| valid_option(option) }
|
||||
raise StandardError, "Invalid Options" unless options.empty? || !validated_options.empty?
|
||||
unless options.empty? || !validated_options.empty?
|
||||
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_options")
|
||||
end
|
||||
|
||||
build_options(options)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class InstagramTag < LiquidTagBase
|
|||
|
||||
def parse_id_or_url(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Instagram ID" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.instagram_tag.invalid_instagram_id") unless match
|
||||
|
||||
match[:video_id]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ class JsFiddleTag < LiquidTagBase
|
|||
|
||||
# Validation
|
||||
validated_options = options.filter_map { |option| valid_option(option) }
|
||||
raise StandardError, "Invalid Options" unless options.empty? || !validated_options.empty?
|
||||
unless options.empty? || !validated_options.empty?
|
||||
raise StandardError, I18n.t("liquid_tags.js_fiddle_tag.invalid_options")
|
||||
end
|
||||
|
||||
validated_options.length.zero? ? "" : validated_options.join(",").concat("/")
|
||||
end
|
||||
|
|
@ -40,7 +42,7 @@ class JsFiddleTag < LiquidTagBase
|
|||
def parse_link(link)
|
||||
stripped_link = ActionController::Base.helpers.strip_tags(link)
|
||||
the_link = stripped_link.split.first
|
||||
raise StandardError, "Invalid JSFiddle URL" unless valid_link?(the_link)
|
||||
raise StandardError, I18n.t("liquid_tags.js_fiddle_tag.invalid_jsfiddle_url") unless valid_link?(the_link)
|
||||
|
||||
the_link
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class JsitorTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def jsitor_error
|
||||
raise StandardError, "Invalid JSitor link. Please read the editor guide for more information"
|
||||
raise StandardError, I18n.t("liquid_tags.jsitor_tag.invalid_jsitor_link")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class KotlinTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid Kotlin Playground URL"
|
||||
raise StandardError, I18n.t("liquid_tags.kotlin_tag.invalid_kotlin_playground")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ class LinkTag < LiquidTagBase
|
|||
|
||||
# If domain is present in url check if it belongs to the app
|
||||
unless domain.blank? || domain&.casecmp?(Settings::General.app_domain)
|
||||
raise StandardError, "The article you're looking for does not exist: {% link #{slug} %}"
|
||||
raise StandardError, I18n.t("liquid_tags.link_tag.not_exist_link", slug: slug)
|
||||
end
|
||||
|
||||
path.slice!(0) if path.starts_with?("/") # remove leading slash if present
|
||||
path.slice!(-1) if path.ends_with?("/") # remove trailing slash if present
|
||||
extracted_hash = Addressable::Template.new("{username}/{slug}").extract(path)&.symbolize_keys
|
||||
raise StandardError, "The article you're looking for does not exist: #{slug}" unless extracted_hash
|
||||
raise StandardError, I18n.t("liquid_tags.link_tag.not_exist", slug: slug) unless extracted_hash
|
||||
|
||||
extracted_hash
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@ class LiquidTagBase < Liquid::Tag
|
|||
return unless self.class.const_defined? :VALID_CONTEXTS
|
||||
|
||||
source = parse_context.partial_options[:source]
|
||||
raise LiquidTags::Errors::InvalidParseContext, "No source found" unless source
|
||||
raise LiquidTags::Errors::InvalidParseContext, I18n.t("liquid_tags.liquid_tag_base.no_source_found") unless source
|
||||
|
||||
is_valid_source = self.class::VALID_CONTEXTS.include? source.class.name
|
||||
return if is_valid_source
|
||||
|
||||
valid_contexts = self.class::VALID_CONTEXTS.map(&:pluralize).join(", ")
|
||||
invalid_source_error_msg = "Invalid context. This liquid tag can only be used in #{valid_contexts}."
|
||||
invalid_source_error_msg = I18n.t("liquid_tags.liquid_tag_base.invalid_context", valid: valid_contexts)
|
||||
raise LiquidTags::Errors::InvalidParseContext, invalid_source_error_msg
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ class ListingTag < LiquidTagBase
|
|||
|
||||
def get_listing(url)
|
||||
hash = get_hash(url)
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." if hash.nil?
|
||||
raise StandardError, I18n.t("liquid_tags.listing_tag.invalid_url_or_slug") if hash.nil?
|
||||
|
||||
listing = Listing.in_category(hash[:category]).find_by(slug: hash[:slug])
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." unless listing
|
||||
raise StandardError, I18n.t("liquid_tags.listing_tag.invalid_url_or_slug") unless listing
|
||||
|
||||
listing
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class MediumTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid link URL or link URL does not exist"
|
||||
raise StandardError, I18n.t("liquid_tags.medium_tag.invalid_link_url")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ class NextTechTag < LiquidTagBase
|
|||
# Returns the share token from the end of the share URL.
|
||||
def parse_share_url(share_url)
|
||||
clean_share_url = ActionController::Base.helpers.strip_tags(share_url).delete(" ").gsub(/\?.*/, "")
|
||||
raise StandardError, "Invalid Next Tech share URL" unless valid_share_url?(clean_share_url)
|
||||
unless valid_share_url?(clean_share_url)
|
||||
raise StandardError, I18n.t("liquid_tags.next_tech_tag.invalid_url")
|
||||
end
|
||||
|
||||
clean_share_url.split("/").last
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class NullTag < Liquid::Block
|
||||
def initialize(tag_name, _markup, _options) # rubocop:disable Lint/MissingSuper
|
||||
raise StandardError, "Liquid##{tag_name} tag is disabled"
|
||||
raise StandardError, I18n.t("liquid_tags.null_tag.liquid_tag_is_disabled", tag_name: tag_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ class OrganizationTag < LiquidTagBase
|
|||
forem_domain = URL.url
|
||||
if organization.starts_with?(forem_domain)
|
||||
match = pattern_match_for(organization, [REGISTRY_REGEXP])
|
||||
raise StandardError, "Invalid Organization URL" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.organization_tag.invalid_url") unless match
|
||||
|
||||
organization = Organization.find_by(slug: match[:org_slug])
|
||||
else
|
||||
organization = Organization.find_by(slug: organization)
|
||||
end
|
||||
|
||||
raise StandardError, "Invalid organization slug" if organization.nil?
|
||||
raise StandardError, I18n.t("liquid_tags.organization_tag.invalid_slug") if organization.nil?
|
||||
|
||||
organization
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ParlerTag < LiquidTagBase
|
|||
def parse_id(input)
|
||||
input_no_space = input.delete(" ")
|
||||
input_no_space = input_no_space.scan(%r{\bhttps?://[a-z./0-9-]+\b}).first
|
||||
raise StandardError, "Invalid Parler URL" unless valid_id?(input_no_space)
|
||||
raise StandardError, I18n.t("liquid_tags.parler_tag.invalid_parler_url") unless valid_id?(input_no_space)
|
||||
|
||||
input_no_space
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class PodcastTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid podcast link"
|
||||
raise StandardError, I18n.t("liquid_tags.podcast_tag.invalid_podcast_link")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class PollTag < LiquidTagBase
|
|||
def find_poll(id_code)
|
||||
Poll.find(id_code.to_i(26))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise StandardError, "Invalid poll ID"
|
||||
raise StandardError, I18n.t("liquid_tags.poll_tag.invalid_poll_id")
|
||||
end
|
||||
|
||||
def self.script
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class RedditTag < LiquidTagBase
|
|||
author: data["author"],
|
||||
title: data["title"],
|
||||
post_url: @url,
|
||||
created_at: Time.zone.at(data["created_utc"]).strftime("%b %e '%y"),
|
||||
created_at: I18n.l(Time.zone.at(data["created_utc"]), format: :reddit),
|
||||
post_hint: data["post_hint"],
|
||||
image_url: data["url"],
|
||||
thumbnail: data["thumbnail"],
|
||||
|
|
@ -63,12 +63,12 @@ class RedditTag < LiquidTagBase
|
|||
def validate_url
|
||||
return true if valid_url?(@url.delete(" ")) && (@url =~ REGISTRY_REGEXP)&.zero?
|
||||
|
||||
raise StandardError, "Invalid Reddit link: #{@url}"
|
||||
raise StandardError, I18n.t("liquid_tags.reddit_tag.invalid_reddit_link", url: @url)
|
||||
end
|
||||
|
||||
def valid_url?(url)
|
||||
url = URI.parse(url)
|
||||
url.is_a?(URI::HTTP)
|
||||
url = Addressable::URI.parse(url)
|
||||
%w[http https].include? url.scheme
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ReplitTag < LiquidTagBase
|
|||
|
||||
def parse_input(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Replit URL or @user/slug" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.replit_tag.invalid_replit_id") unless match
|
||||
|
||||
match[:address]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class RunkitTag < Liquid::Block
|
|||
end
|
||||
|
||||
def sanitized_preamble(markup)
|
||||
raise StandardError, "Runkit tag is invalid" if markup.include? "\">"
|
||||
raise StandardError, I18n.t("liquid_tags.runkit_tag.runkit_tag_is_invalid") if markup.include? "\">"
|
||||
|
||||
ActionView::Base.full_sanitizer.sanitize(markup, tags: [])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ class SlideshareTag < LiquidTagBase
|
|||
private
|
||||
|
||||
def validate(key)
|
||||
raise StandardError, "Invalid Slideshare Key" unless key.match?(/\A[a-zA-Z0-9]{12,14}\Z/)
|
||||
unless key.match?(/\A[a-zA-Z0-9]{12,14}\Z/)
|
||||
raise StandardError,
|
||||
I18n.t("liquid_tags.slideshare_tag.invalid_slideshare_key")
|
||||
end
|
||||
|
||||
key
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class SoundcloudTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid Soundcloud URL - try taking off any URL params: '?something=value'"
|
||||
raise StandardError, I18n.t("liquid_tags.soundcloud_tag.invalid_soundcloud_url")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class SpeakerdeckTag < LiquidTagBase
|
|||
|
||||
def parse_id(input)
|
||||
input_no_space = input.delete(" ")
|
||||
raise StandardError, "Invalid Speakerdeck Id" unless valid_id?(input_no_space)
|
||||
raise StandardError, I18n.t("liquid_tags.speakerdeck_tag.invalid_speakerdeck_id") unless valid_id?(input_no_space)
|
||||
|
||||
input_no_space
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class SpotifyTag < LiquidTagBase
|
|||
|
||||
def parse_input(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Spotify URI or URL." unless match
|
||||
raise StandardError, I18n.t("liquid_tags.spotify_tag.invalid_spotify_uri") unless match
|
||||
|
||||
[match[:type], match[:id]]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class StackblitzTag < LiquidTagBase
|
|||
|
||||
def parse_id(input)
|
||||
input_no_space = input.split.first
|
||||
raise StandardError, "Invalid Stackblitz Id" unless valid_id?(input_no_space)
|
||||
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_stackblitz_id") unless valid_id?(input_no_space)
|
||||
|
||||
input_no_space
|
||||
end
|
||||
|
|
@ -42,7 +42,8 @@ class StackblitzTag < LiquidTagBase
|
|||
|
||||
# Validation
|
||||
validated_views = inputs.filter_map { |input_option| validator.call(input_option) }
|
||||
raise StandardError, "Invalid Options" unless validated_views.length.between?(0, 1)
|
||||
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_options") unless validated_views.length.between?(0,
|
||||
1)
|
||||
|
||||
validated_views.length.zero? ? "" : validated_views.join.to_s
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class StackeryTag < LiquidTagBase
|
|||
def validate_items(owner, repo)
|
||||
return unless owner.blank? || repo.blank?
|
||||
|
||||
raise StandardError, "Stackery - Missing owner and/or repository name arguments"
|
||||
raise StandardError, I18n.t("liquid_tags.stackery_tag.missing_argument")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ class StackexchangeTag < LiquidTagBase
|
|||
return "stackoverflow" if tag_name == "stackoverflow"
|
||||
|
||||
site = input.match(/[a-z.]+/i)[0]
|
||||
raise StandardError, "Invalid Stack Exchange site: {% #{tag_name} #{input} %}" unless valid_site?(site)
|
||||
unless valid_site?(site)
|
||||
raise StandardError, I18n.t("liquid_tags.stackexchange_tag.invalid_site", tag: tag_name, input: input)
|
||||
end
|
||||
|
||||
site
|
||||
end
|
||||
|
|
@ -66,11 +68,13 @@ class StackexchangeTag < LiquidTagBase
|
|||
|
||||
return unless response["items"].length.zero?
|
||||
|
||||
raise StandardError, "Couldn't find a post with that ID: {% #{tag_name} #{input} %}"
|
||||
raise StandardError, I18n.t("liquid_tags.stackexchange_tag.post_not_found", tag: tag_name, input: input)
|
||||
end
|
||||
|
||||
def get_data(input)
|
||||
raise StandardError, "Invalid Stack Exchange ID: {% #{tag_name} #{input} %}" unless valid_input?(input)
|
||||
unless valid_input?(input)
|
||||
raise StandardError, I18n.t("liquid_tags.stackexchange_tag.invalid_id", tag: tag_name, input: input)
|
||||
end
|
||||
|
||||
id = input.split.first
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class TagTag < LiquidTagBase
|
|||
|
||||
def parse_tag_name_to_tag(input)
|
||||
tag = Tag.find_by(name: input)
|
||||
raise StandardError, "invalid tag name" if tag.nil?
|
||||
raise StandardError, I18n.t("liquid_tags.tag_tag.invalid_tag_name") if tag.nil?
|
||||
|
||||
tag
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class TweetTag < LiquidTagBase
|
|||
|
||||
def parse_id(input)
|
||||
input_no_space = input.delete(" ")
|
||||
raise StandardError, "Invalid Twitter Id" unless valid_id?(input_no_space)
|
||||
raise StandardError, I18n.t("liquid_tags.tweet_tag.invalid_twitter_id") unless valid_id?(input_no_space)
|
||||
|
||||
input_no_space
|
||||
end
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class TwitchTag < LiquidTagBase
|
|||
input = input.split("&")[0] # prevent param injection
|
||||
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid Twitch ID, Slug or URL" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.twitch_tag.invalid_twitch_id") unless match
|
||||
|
||||
return player_url(match[:video_id]) if match.names.include?("video_id")
|
||||
return clip_url(match[:clip_slug]) if match.names.include?("clip_slug")
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class TwitterTimelineTag < LiquidTagBase
|
|||
end
|
||||
|
||||
def raise_error
|
||||
raise StandardError, "Invalid Twitter Timeline URL"
|
||||
raise StandardError, I18n.t("liquid_tags.twitter_timeline_tag.invalid_url")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,328 +11,331 @@ class UserSubscriptionTag < LiquidTagBase
|
|||
:super_admin,
|
||||
].freeze
|
||||
|
||||
SCRIPT = <<~JAVASCRIPT.freeze
|
||||
var subscribeBtn = document.getElementById('subscribe-btn');
|
||||
def self.script
|
||||
<<~JAVASCRIPT.freeze
|
||||
var subscribeBtn = document.getElementById('subscribe-btn');
|
||||
|
||||
function isUserSignedIn() {
|
||||
return document.head.querySelector('meta[name="user-signed-in"][content="true"]') !== null;
|
||||
}
|
||||
|
||||
// Hiding/showing elements
|
||||
// If clearSubscribeButton is false, we will not clear out the subscription-signed-in area,
|
||||
// which will allow users to re-submit their subscription if they see any error messages.
|
||||
// ***************************************
|
||||
function clearSubscriptionArea({ clearSubscribeButton = true } = {}) {
|
||||
if (!clearSubscribeButton) {
|
||||
// Allow users to try submitting again if they see an error.
|
||||
hideSubscriptionSignedIn();
|
||||
function isUserSignedIn() {
|
||||
return document.head.querySelector('meta[name="user-signed-in"][content="true"]') !== null;
|
||||
}
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.add("hidden");
|
||||
// Hiding/showing elements
|
||||
// If clearSubscribeButton is false, we will not clear out the subscription-signed-in area,
|
||||
// which will allow users to re-submit their subscription if they see any error messages.
|
||||
// ***************************************
|
||||
function clearSubscriptionArea({ clearSubscribeButton = true } = {}) {
|
||||
if (!clearSubscribeButton) {
|
||||
// Allow users to try submitting again if they see an error.
|
||||
hideSubscriptionSignedIn();
|
||||
}
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.add("hidden");
|
||||
}
|
||||
|
||||
hideResponseMessage();
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.add("hidden");
|
||||
}
|
||||
|
||||
hideConfirmationModal();
|
||||
}
|
||||
|
||||
hideResponseMessage();
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.add("hidden");
|
||||
}
|
||||
|
||||
hideConfirmationModal();
|
||||
}
|
||||
|
||||
// Hides the response message (which displays success/error messages) if it exists.
|
||||
// ***************************************
|
||||
function hideResponseMessage() {
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showSignedIn() {
|
||||
clearSubscriptionArea();
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-out");
|
||||
profileImages.classList.add("signed-in");
|
||||
}
|
||||
}
|
||||
|
||||
function showSignedOut() {
|
||||
clearSubscriptionArea();
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-in");
|
||||
profileImages.classList.add("signed-out");
|
||||
}
|
||||
|
||||
const subscriberProfileImage = document.getElementsByClassName('ltag__user-subscription-tag__subscriber-profile-image')[0];
|
||||
if (subscriberProfileImage) {
|
||||
subscriberProfileImage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showResponseMessage(noticeType, msg) {
|
||||
clearSubscriptionArea(clearSubscribeButton = false);
|
||||
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.remove("hidden");
|
||||
responseMessage.classList.add(`crayons-notice--${noticeType}`);
|
||||
responseMessage.textContent = msg;
|
||||
|
||||
if (noticeType === 'danger') {
|
||||
// Allow users to try resubscribing if they see an error message.
|
||||
subscribeBtn.textContent = "Submit";
|
||||
subscribeBtn.disabled = false;
|
||||
// Hides the response message (which displays success/error messages) if it exists.
|
||||
// ***************************************
|
||||
function hideResponseMessage() {
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showAppleAuthMessage() {
|
||||
clearSubscriptionArea();
|
||||
const subscriber = userData();
|
||||
if (subscriber) {
|
||||
function showSignedIn() {
|
||||
clearSubscriptionArea();
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-out");
|
||||
profileImages.classList.add("signed-in");
|
||||
}
|
||||
}
|
||||
|
||||
function showSignedOut() {
|
||||
clearSubscriptionArea();
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-in");
|
||||
profileImages.classList.add("signed-out");
|
||||
}
|
||||
|
||||
const subscriberProfileImage = document.getElementsByClassName('ltag__user-subscription-tag__subscriber-profile-image')[0];
|
||||
if (subscriberProfileImage) {
|
||||
subscriberProfileImage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showResponseMessage(noticeType, msg) {
|
||||
clearSubscriptionArea(clearSubscribeButton = false);
|
||||
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.remove("hidden");
|
||||
responseMessage.classList.add(`crayons-notice--${noticeType}`);
|
||||
responseMessage.textContent = msg;
|
||||
|
||||
if (noticeType === 'danger') {
|
||||
// Allow users to try resubscribing if they see an error message.
|
||||
subscribeBtn.textContent = "#{I18n.t('liquid_tags.user_subscription_tag.submit')}";
|
||||
subscribeBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showAppleAuthMessage() {
|
||||
clearSubscriptionArea();
|
||||
const subscriber = userData();
|
||||
if (subscriber) {
|
||||
updateProfileImages('.ltag__user-subscription-tag__subscriber-profile-image', subscriber);
|
||||
}
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showSubscribed() {
|
||||
hideSubscriptionSignedIn();
|
||||
updateSubscriberData();
|
||||
const authorUsername = document.getElementById('user-subscription-tag')?.dataset.authorUsername;
|
||||
const alreadySubscribedMsg = `#{I18n.t('liquid_tags.user_subscription_tag.subscribed')}`;
|
||||
showResponseMessage('success', alreadySubscribedMsg);
|
||||
}
|
||||
|
||||
function showConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function hideConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Updating DOM elements
|
||||
// ***************************************
|
||||
function updateSubscriberData() {
|
||||
const subscriber = userData();
|
||||
if (subscriber.email) {
|
||||
updateElementsTextContent('.ltag__user-subscription-tag__subscriber-email', subscriber.email);
|
||||
}
|
||||
|
||||
updateProfileImages('.ltag__user-subscription-tag__subscriber-profile-image', subscriber);
|
||||
}
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
function updateElementsTextContent(identifier, value) {
|
||||
const elements = document.querySelectorAll(identifier);
|
||||
|
||||
function showSubscribed() {
|
||||
hideSubscriptionSignedIn();
|
||||
updateSubscriberData();
|
||||
const authorUsername = document.getElementById('user-subscription-tag')?.dataset.authorUsername;
|
||||
const alreadySubscribedMsg = `You are already subscribed.`;
|
||||
showResponseMessage('success', alreadySubscribedMsg);
|
||||
}
|
||||
|
||||
function showConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function hideConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Updating DOM elements
|
||||
// ***************************************
|
||||
function updateSubscriberData() {
|
||||
const subscriber = userData();
|
||||
if (subscriber.email) {
|
||||
updateElementsTextContent('.ltag__user-subscription-tag__subscriber-email', subscriber.email);
|
||||
}
|
||||
|
||||
updateProfileImages('.ltag__user-subscription-tag__subscriber-profile-image', subscriber);
|
||||
}
|
||||
|
||||
function updateElementsTextContent(identifier, value) {
|
||||
const elements = document.querySelectorAll(identifier);
|
||||
|
||||
elements.forEach(function(element) {
|
||||
element.textContent = value;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProfileImages(identifier, subscriber) {
|
||||
const profileImages = document.querySelectorAll(`img${identifier}`);
|
||||
|
||||
profileImages.forEach(function(profileImage) {
|
||||
profileImage.src = subscriber.profile_image_90;
|
||||
profileImage.alt = `${subscriber.username} profile image`;
|
||||
});
|
||||
}
|
||||
|
||||
function hideSubscriptionSignedIn() {
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Adding event listeners for 'click'
|
||||
// ***************************************
|
||||
function addSignInClickHandler() {
|
||||
const signInBtn = document.getElementById('sign-in-btn');
|
||||
if (signInBtn) {
|
||||
signInBtn.addEventListener('click', function(e) {
|
||||
if (typeof showLoginModal !== 'undefined') {
|
||||
showLoginModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addConfirmationModalClickHandlers() {
|
||||
if (subscribeBtn) {
|
||||
subscribeBtn.addEventListener('click', function(e) {
|
||||
showConfirmationModal();
|
||||
elements.forEach(function(element) {
|
||||
element.textContent = value;
|
||||
});
|
||||
}
|
||||
|
||||
const cancelBtn = document.getElementById('cancel-btn');
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
function updateProfileImages(identifier, subscriber) {
|
||||
const profileImages = document.querySelectorAll(`img${identifier}`);
|
||||
|
||||
profileImages.forEach(function(profileImage) {
|
||||
profileImage.src = subscriber.profile_image_90;
|
||||
profileImage.alt = `${subscriber.username} profile image`;
|
||||
});
|
||||
}
|
||||
|
||||
const closeConfirmationModal = document.getElementById('close-confirmation-modal');
|
||||
if (closeConfirmationModal) {
|
||||
closeConfirmationModal.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
});
|
||||
function hideSubscriptionSignedIn() {
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
const confirmationModal = document.getElementById('confirmation-btn')
|
||||
if (confirmationModal) {
|
||||
confirmationModal.addEventListener('click', function(e) {
|
||||
handleSubscription();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// API calls
|
||||
// ***************************************
|
||||
function submitSubscription() {
|
||||
// Hide any error messages previously rendered.
|
||||
hideResponseMessage();
|
||||
|
||||
subscribeBtn.textContent = "Submitting...";
|
||||
subscribeBtn.disabled = true;
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const subscriber = userData();
|
||||
const body = JSON.stringify(
|
||||
{
|
||||
user_subscription: {
|
||||
source_type: 'Article',
|
||||
source_id: articleId,
|
||||
subscriber_email: subscriber.email
|
||||
// Adding event listeners for 'click'
|
||||
// ***************************************
|
||||
function addSignInClickHandler() {
|
||||
const signInBtn = document.getElementById('sign-in-btn');
|
||||
if (signInBtn) {
|
||||
signInBtn.addEventListener('click', function(e) {
|
||||
if (typeof showLoginModal !== 'undefined') {
|
||||
showLoginModal();
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return fetch('/user_subscriptions', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
body: body,
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
|
||||
function fetchIsSubscribed() {
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const params = new URLSearchParams({
|
||||
source_type: 'Article',
|
||||
source_id: articleId
|
||||
}).toString();
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(`/user_subscriptions/subscribed?${params}`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
}).then(function(response) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
console.error(`Base data error: ${response.status} - ${response.statusText}`);
|
||||
function addConfirmationModalClickHandlers() {
|
||||
if (subscribeBtn) {
|
||||
subscribeBtn.addEventListener('click', function(e) {
|
||||
showConfirmationModal();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle API responses
|
||||
// ***************************************
|
||||
function handleSubscription() {
|
||||
hideConfirmationModal(); // Close the modal once the user has confirmed.
|
||||
|
||||
submitSubscription().then(function(response) {
|
||||
if (response.success) {
|
||||
const userSubscriptionTag = document.getElementById('user-subscription-tag');
|
||||
const authorUsername = (userSubscriptionTag ? userSubscriptionTag.dataset.authorUsername : null);
|
||||
const successMsg = `You are now subscribed and may receive emails from ${authorUsername}`;
|
||||
showResponseMessage('success', successMsg);
|
||||
hideSubscriptionSignedIn();
|
||||
} else {
|
||||
showResponseMessage('danger', response.error);
|
||||
const cancelBtn = document.getElementById('cancel-btn');
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfSubscribed() {
|
||||
fetchIsSubscribed().then(function(response) {
|
||||
const closeConfirmationModal = document.getElementById('close-confirmation-modal');
|
||||
if (closeConfirmationModal) {
|
||||
closeConfirmationModal.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
});
|
||||
}
|
||||
|
||||
const confirmationModal = document.getElementById('confirmation-btn')
|
||||
if (confirmationModal) {
|
||||
confirmationModal.addEventListener('click', function(e) {
|
||||
handleSubscription();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// API calls
|
||||
// ***************************************
|
||||
function submitSubscription() {
|
||||
// Hide any error messages previously rendered.
|
||||
hideResponseMessage();
|
||||
|
||||
subscribeBtn.textContent = "#{I18n.t('liquid_tags.user_subscription_tag.submitting')}";
|
||||
subscribeBtn.disabled = true;
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const subscriber = userData();
|
||||
const isSubscriberAuthedWithApple = (subscriber.email ? subscriber.email.endsWith('@privaterelay.appleid.com') : false);
|
||||
const body = JSON.stringify(
|
||||
{
|
||||
user_subscription: {
|
||||
source_type: 'Article',
|
||||
source_id: articleId,
|
||||
subscriber_email: subscriber.email
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (response.is_subscribed) {
|
||||
showSubscribed();
|
||||
} else if (isSubscriberAuthedWithApple) {
|
||||
showAppleAuthMessage();
|
||||
} else {
|
||||
updateSubscriberData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// We load this JS on every Article. This is to only run it on Articles
|
||||
// where the UserSubscription liquid tag is present
|
||||
if (document.getElementById('user-subscription-tag')) {
|
||||
// The markup defaults to signed out UX
|
||||
if (isUserSignedIn()) {
|
||||
showSignedIn();
|
||||
addConfirmationModalClickHandlers();
|
||||
|
||||
// We need access to some DOM elements (i.e. csrf token, article id, userData, etc.)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkIfSubscribed();
|
||||
return fetch('/user_subscriptions', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
body: body,
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
});
|
||||
} else {
|
||||
showSignedOut();
|
||||
addSignInClickHandler();
|
||||
}
|
||||
}
|
||||
JAVASCRIPT
|
||||
|
||||
function fetchIsSubscribed() {
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const params = new URLSearchParams({
|
||||
source_type: 'Article',
|
||||
source_id: articleId
|
||||
}).toString();
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
return fetch(`/user_subscriptions/subscribed?${params}`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
}).then(function(response) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
console.error(`Base data error: ${response.status} - ${response.statusText}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle API responses
|
||||
// ***************************************
|
||||
function handleSubscription() {
|
||||
hideConfirmationModal(); // Close the modal once the user has confirmed.
|
||||
|
||||
submitSubscription().then(function(response) {
|
||||
if (response.success) {
|
||||
const userSubscriptionTag = document.getElementById('user-subscription-tag');
|
||||
const authorUsername = (userSubscriptionTag ? userSubscriptionTag.dataset.authorUsername : null);
|
||||
// TODO: [yheuhtozr] currently no way of i18n via Ruby
|
||||
const successMsg = `You are now subscribed and may receive emails from ${authorUsername}`;
|
||||
showResponseMessage('success', successMsg);
|
||||
hideSubscriptionSignedIn();
|
||||
} else {
|
||||
showResponseMessage('danger', response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfSubscribed() {
|
||||
fetchIsSubscribed().then(function(response) {
|
||||
const subscriber = userData();
|
||||
const isSubscriberAuthedWithApple = (subscriber.email ? subscriber.email.endsWith('@privaterelay.appleid.com') : false);
|
||||
|
||||
if (response.is_subscribed) {
|
||||
showSubscribed();
|
||||
} else if (isSubscriberAuthedWithApple) {
|
||||
showAppleAuthMessage();
|
||||
} else {
|
||||
updateSubscriberData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// We load this JS on every Article. This is to only run it on Articles
|
||||
// where the UserSubscription liquid tag is present
|
||||
if (document.getElementById('user-subscription-tag')) {
|
||||
// The markup defaults to signed out UX
|
||||
if (isUserSignedIn()) {
|
||||
showSignedIn();
|
||||
addConfirmationModalClickHandlers();
|
||||
|
||||
// We need access to some DOM elements (i.e. csrf token, article id, userData, etc.)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkIfSubscribed();
|
||||
});
|
||||
} else {
|
||||
showSignedOut();
|
||||
addSignInClickHandler();
|
||||
}
|
||||
}
|
||||
JAVASCRIPT
|
||||
end
|
||||
|
||||
def initialize(_tag_name, cta_text, parse_context)
|
||||
super
|
||||
|
|
@ -351,10 +354,6 @@ class UserSubscriptionTag < LiquidTagBase
|
|||
},
|
||||
)
|
||||
end
|
||||
|
||||
def self.script
|
||||
SCRIPT
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("user_subscription", UserSubscriptionTag)
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ class WikipediaTag < LiquidTagBase
|
|||
|
||||
def get_data(input)
|
||||
url = ActionController::Base.helpers.strip_tags(input).strip
|
||||
raise StandardError, "Invalid Wikipedia URL" unless valid_url?(url)
|
||||
raise StandardError, I18n.t("liquid_tags.wikipedia_tag.invalid_wikipedia_url") unless valid_url?(url)
|
||||
|
||||
uri = URI.parse(url)
|
||||
uri = Addressable::URI.parse(url)
|
||||
lang = uri.host.split(".", 2).first
|
||||
title = uri.path.split("/").last
|
||||
anchor = uri.fragment
|
||||
|
|
@ -72,7 +72,8 @@ class WikipediaTag < LiquidTagBase
|
|||
def handle_response_error(response, input)
|
||||
return if response.code == 200
|
||||
|
||||
raise StandardError, "Couldn't find the Wikipedia article - #{input}: #{response['detail']}"
|
||||
raise StandardError,
|
||||
I18n.t("liquid_tags.wikipedia_tag.article_not_found", input: input, detail: (response["detail"]))
|
||||
end
|
||||
|
||||
def get_section_contents(response, anchor, input)
|
||||
|
|
@ -86,7 +87,7 @@ class WikipediaTag < LiquidTagBase
|
|||
|
||||
return [text, title] if title.present?
|
||||
|
||||
raise StandardError, "Couldn't find the section of the Wikipedia article - #{input}"
|
||||
raise StandardError, I18n.t("liquid_tags.wikipedia_tag.section_not_found", input: input)
|
||||
end
|
||||
|
||||
def text_clean_up(text)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class YoutubeTag < LiquidTagBase
|
|||
|
||||
def parse_id_or_url(input)
|
||||
match = pattern_match_for(input, REGEXP_OPTIONS)
|
||||
raise StandardError, "Invalid YouTube ID or URL" unless match
|
||||
raise StandardError, I18n.t("liquid_tags.youtube_tag.invalid_youtube_id") unless match
|
||||
|
||||
video_id = match[:video_id]
|
||||
time_parameter = match[:time_parameter]
|
||||
|
|
|
|||
|
|
@ -170,8 +170,10 @@ en:
|
|||
other: is the wrong length (should be %{count} characters)
|
||||
time:
|
||||
formats:
|
||||
json: "%b %e, %Y"
|
||||
comment: "%B %d, %Y"
|
||||
github: "%b %d, %Y"
|
||||
json: "%b %e, %Y"
|
||||
reddit: "%b %e '%y"
|
||||
short: "%b %-e"
|
||||
short_with_year: "%b %-e, %Y"
|
||||
stackexchange: "%b %e '%y"
|
||||
|
|
|
|||
|
|
@ -193,8 +193,10 @@ fr:
|
|||
other: est la mauvaise longueur (devrait être %{count} caractères)
|
||||
time:
|
||||
formats:
|
||||
json: "%b %e, %Y"
|
||||
comment: "%B %d, %Y"
|
||||
github: "%b %d, %Y"
|
||||
json: "%b %e, %Y"
|
||||
reddit: "%b %e '%y"
|
||||
short: "%-e %b"
|
||||
short_with_year: "%-e %b, %Y"
|
||||
stackexchange: "%b %e '%y"
|
||||
|
|
|
|||
107
config/locales/liquid_tags/en.yml
Normal file
107
config/locales/liquid_tags/en.yml
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
en:
|
||||
liquid_tags:
|
||||
asciinema_tag:
|
||||
invalid_asciinema_id: 'Invalid Asciinema ID: %{id}'
|
||||
blogcast_tag:
|
||||
invalid_blogcast_id: Invalid Blogcast ID
|
||||
codepen_tag:
|
||||
invalid_codepen_url: Invalid CodePen URL
|
||||
invalid_options: Invalid Options
|
||||
codesandbox_tag:
|
||||
invalid_id: 'CodeSandbox Error: Invalid ID'
|
||||
comment_tag:
|
||||
invalid_comment: Invalid Comment ID or URL
|
||||
dotnet_fiddle_tag:
|
||||
invalid_dotnetfiddle_url: Invalid DotnetFiddle URL
|
||||
gist_tag:
|
||||
invalid_filename: Invalid Filename
|
||||
invalid_format: 'Invalid Gist link: %{link} Links must follow this format: https://gist.github.com/username/gist_id'
|
||||
invalid_gist_link: 'Invalid Gist link: You must provide a Gist link'
|
||||
git_pitch_tag:
|
||||
invalid_gitpitch_url: Invalid GitPitch URL
|
||||
github_tag:
|
||||
github_issue_tag:
|
||||
comment_for: Comment for
|
||||
commented_on: commented on
|
||||
invalid_github_issue_pull: Invalid GitHub issue, pull request or comment link
|
||||
posted_on: posted on
|
||||
github_readme_tag:
|
||||
invalid_options: 'GitHub tag: invalid options: %{invalid} - supported options: %{valid}'
|
||||
invalid_github_repository: Invalid GitHub repository path or URL
|
||||
glitch_tag:
|
||||
invalid_glitch_id: Invalid Glitch ID
|
||||
invalid_options: Invalid Options
|
||||
instagram_tag:
|
||||
invalid_instagram_id: Invalid Instagram ID
|
||||
js_fiddle_tag:
|
||||
invalid_jsfiddle_url: Invalid JSFiddle URL
|
||||
invalid_options: Invalid Options
|
||||
jsitor_tag:
|
||||
invalid_jsitor_link: Invalid JSitor link. Please read the editor guide for more information
|
||||
kotlin_tag:
|
||||
invalid_kotlin_playground: Invalid Kotlin Playground URL
|
||||
link_tag:
|
||||
not_exist: 'The article you''re looking for does not exist: %{slug}'
|
||||
not_exist_link: 'The article you''re looking for does not exist: {% link %{slug} %}'
|
||||
liquid_tag_base:
|
||||
invalid_context: Invalid context. This liquid tag can only be used in %{valid}.
|
||||
no_source_found: No source found
|
||||
listing_tag:
|
||||
invalid_url_or_slug: Invalid URL or slug. Listing not found.
|
||||
medium_tag:
|
||||
invalid_link_url: Invalid link URL or link URL does not exist
|
||||
next_tech_tag:
|
||||
invalid_url: Invalid Next Tech share URL
|
||||
null_tag:
|
||||
liquid_tag_is_disabled: Liquid#%{tag_name} tag is disabled
|
||||
organization_tag:
|
||||
invalid_slug: Invalid organization slug
|
||||
invalid_url: Invalid Organization URL
|
||||
parler_tag:
|
||||
invalid_parler_url: Invalid Parler URL
|
||||
podcast_tag:
|
||||
invalid_podcast_link: Invalid podcast link
|
||||
poll_tag:
|
||||
invalid_poll_id: Invalid poll ID
|
||||
reddit_tag:
|
||||
invalid_reddit_link: 'Invalid Reddit link: %{url}'
|
||||
replit_tag:
|
||||
invalid_replit_id: Invalid Replit URL or @user/slug
|
||||
runkit_tag:
|
||||
runkit_tag_is_invalid: Runkit tag is invalid
|
||||
slideshare_tag:
|
||||
invalid_slideshare_key: Invalid Slideshare Key
|
||||
soundcloud_tag:
|
||||
invalid_soundcloud_url: 'Invalid Soundcloud URL - try taking off any URL params: ''?something=value'''
|
||||
speakerdeck_tag:
|
||||
invalid_speakerdeck_id: Invalid Speakerdeck Id
|
||||
spotify_tag:
|
||||
invalid_spotify_uri: Invalid Spotify URI or URL.
|
||||
stackblitz_tag:
|
||||
invalid_options: Invalid Options
|
||||
invalid_stackblitz_id: Invalid Stackblitz Id
|
||||
stackery_tag:
|
||||
missing_argument: Stackery - Missing owner and/or repository name arguments
|
||||
stackexchange_tag:
|
||||
post_not_found: 'Couldn''t find a post with that ID: {% %{tag} %{input} %}'
|
||||
invalid_id: 'Invalid Stack Exchange ID: {% %{tag} %{input} %}'
|
||||
invalid_site: 'Invalid Stack Exchange site: {% %{tag} %{input} %}'
|
||||
tag_tag:
|
||||
invalid_tag_name: invalid tag name
|
||||
tweet_tag:
|
||||
invalid_twitter_id: Invalid Twitter Id
|
||||
twitch_tag:
|
||||
invalid_twitch_id: Invalid Twitch ID, Slug or URL
|
||||
twitter_timeline_tag:
|
||||
invalid_url: Invalid Twitter Timeline URL
|
||||
user_subscription_tag:
|
||||
submit: Submit
|
||||
submitting: Submitting...
|
||||
subscribed: You are already subscribed.
|
||||
wikipedia_tag:
|
||||
section_not_found: Couldn't find the section of the Wikipedia article - %{input}
|
||||
article_not_found: 'Couldn''t find the Wikipedia article - %{input}: %{response_detail}'
|
||||
invalid_wikipedia_url: Invalid Wikipedia URL
|
||||
youtube_tag:
|
||||
invalid_youtube_id: Invalid YouTube ID or URL
|
||||
107
config/locales/liquid_tags/fr.yml
Normal file
107
config/locales/liquid_tags/fr.yml
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
fr:
|
||||
liquid_tags:
|
||||
asciinema_tag:
|
||||
invalid_asciinema_id: 'Invalid Asciinema ID: %{id}'
|
||||
blogcast_tag:
|
||||
invalid_blogcast_id: Invalid Blogcast ID
|
||||
codepen_tag:
|
||||
invalid_codepen_url: Invalid CodePen URL
|
||||
invalid_options: Invalid Options
|
||||
codesandbox_tag:
|
||||
invalid_id: 'CodeSandbox Error: Invalid ID'
|
||||
comment_tag:
|
||||
invalid_comment: Invalid Comment ID or URL
|
||||
dotnet_fiddle_tag:
|
||||
invalid_dotnetfiddle_url: Invalid DotnetFiddle URL
|
||||
gist_tag:
|
||||
invalid_filename: Invalid Filename
|
||||
invalid_format: 'Invalid Gist link: %{link} Links must follow this format: https://gist.github.com/username/gist_id'
|
||||
invalid_gist_link: 'Invalid Gist link: You must provide a Gist link'
|
||||
git_pitch_tag:
|
||||
invalid_gitpitch_url: Invalid GitPitch URL
|
||||
github_tag:
|
||||
github_issue_tag:
|
||||
comment_for: Comment for
|
||||
commented_on: commented on
|
||||
invalid_github_issue_pull: Invalid GitHub issue, pull request or comment link
|
||||
posted_on: posted on
|
||||
github_readme_tag:
|
||||
invalid_options: 'GitHub tag: invalid options: %{invalid} - supported options: %{valid}'
|
||||
invalid_github_repository: Invalid GitHub repository path or URL
|
||||
glitch_tag:
|
||||
invalid_glitch_id: Invalid Glitch ID
|
||||
invalid_options: Invalid Options
|
||||
instagram_tag:
|
||||
invalid_instagram_id: Invalid Instagram ID
|
||||
js_fiddle_tag:
|
||||
invalid_jsfiddle_url: Invalid JSFiddle URL
|
||||
invalid_options: Invalid Options
|
||||
jsitor_tag:
|
||||
invalid_jsitor_link: Invalid JSitor link. Please read the editor guide for more information
|
||||
kotlin_tag:
|
||||
invalid_kotlin_playground: Invalid Kotlin Playground URL
|
||||
link_tag:
|
||||
not_exist: 'The article you''re looking for does not exist: %{slug}'
|
||||
not_exist_link: 'The article you''re looking for does not exist: {% link %{slug} %}'
|
||||
liquid_tag_base:
|
||||
invalid_context: Invalid context. This liquid tag can only be used in %{valid}.
|
||||
no_source_found: No source found
|
||||
listing_tag:
|
||||
invalid_url_or_slug: Invalid URL or slug. Listing not found.
|
||||
medium_tag:
|
||||
invalid_link_url: Invalid link URL or link URL does not exist
|
||||
next_tech_tag:
|
||||
invalid_url: Invalid Next Tech share URL
|
||||
null_tag:
|
||||
liquid_tag_is_disabled: Liquid#%{tag_name} tag is disabled
|
||||
organization_tag:
|
||||
invalid_slug: Invalid organization slug
|
||||
invalid_url: Invalid Organization URL
|
||||
parler_tag:
|
||||
invalid_parler_url: Invalid Parler URL
|
||||
podcast_tag:
|
||||
invalid_podcast_link: Invalid podcast link
|
||||
poll_tag:
|
||||
invalid_poll_id: Invalid poll ID
|
||||
reddit_tag:
|
||||
invalid_reddit_link: 'Invalid Reddit link: %{url}'
|
||||
replit_tag:
|
||||
invalid_replit_id: Invalid Replit URL or @user/slug
|
||||
runkit_tag:
|
||||
runkit_tag_is_invalid: Runkit tag is invalid
|
||||
slideshare_tag:
|
||||
invalid_slideshare_key: Invalid Slideshare Key
|
||||
soundcloud_tag:
|
||||
invalid_soundcloud_url: 'Invalid Soundcloud URL - try taking off any URL params: ''?something=value'''
|
||||
speakerdeck_tag:
|
||||
invalid_speakerdeck_id: Invalid Speakerdeck Id
|
||||
spotify_tag:
|
||||
invalid_spotify_uri: Invalid Spotify URI or URL.
|
||||
stackblitz_tag:
|
||||
invalid_options: Invalid Options
|
||||
invalid_stackblitz_id: Invalid Stackblitz Id
|
||||
stackery_tag:
|
||||
missing_argument: Stackery - Missing owner and/or repository name arguments
|
||||
stackexchange_tag:
|
||||
post_not_found: 'Couldn''t find a post with that ID: {% %{tag} %{input} %}'
|
||||
invalid_id: 'Invalid Stack Exchange ID: {% %{tag} %{input} %}'
|
||||
invalid_site: 'Invalid Stack Exchange site: {% %{tag} %{input} %}'
|
||||
tag_tag:
|
||||
invalid_tag_name: invalid tag name
|
||||
tweet_tag:
|
||||
invalid_twitter_id: Invalid Twitter Id
|
||||
twitch_tag:
|
||||
invalid_twitch_id: Invalid Twitch ID, Slug or URL
|
||||
twitter_timeline_tag:
|
||||
invalid_url: Invalid Twitter Timeline URL
|
||||
user_subscription_tag:
|
||||
submit: Submit
|
||||
submitting: Submitting...
|
||||
subscribed: You are already subscribed.
|
||||
wikipedia_tag:
|
||||
section_not_found: Couldn't find the section of the Wikipedia article - %{input}
|
||||
article_not_found: 'Couldn''t find the Wikipedia article - %{input}: %{response_detail}'
|
||||
invalid_wikipedia_url: Invalid Wikipedia URL
|
||||
youtube_tag:
|
||||
invalid_youtube_id: Invalid YouTube ID or URL
|
||||
Loading…
Add table
Reference in a new issue