-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from RubyOnWorld/model-emoji
model-emoji
- Loading branch information
Showing
5 changed files
with
601 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_string_literal: true | ||
|
||
class EmailStyle | ||
include ActiveModel::Serialization | ||
|
||
attr_accessor :html, :css, :default_html, :default_css | ||
|
||
def id | ||
'email-style' | ||
end | ||
|
||
def html | ||
SiteSetting.email_custom_template.presence || default_html | ||
end | ||
|
||
def css | ||
SiteSetting.email_custom_css || default_css | ||
end | ||
|
||
def compiled_css | ||
SiteSetting.email_custom_css_compiled.presence || css | ||
end | ||
|
||
def default_html | ||
self.class.default_template | ||
end | ||
|
||
def default_css | ||
self.class.default_css | ||
end | ||
|
||
def self.default_template | ||
@_default_template ||= File.read( | ||
File.join(Rails.root, 'app', 'views', 'email', 'default_template.html') | ||
) | ||
end | ||
|
||
def self.default_css | ||
'' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# frozen_string_literal: true | ||
|
||
class EmailToken < ActiveRecord::Base | ||
class TokenAccessError < StandardError; end | ||
|
||
belongs_to :user | ||
|
||
validates :user_id, :email, :token_hash, presence: true | ||
|
||
scope :unconfirmed, -> { where(confirmed: false) } | ||
scope :active, -> { where(expired: false).where('created_at >= ?', SiteSetting.email_token_valid_hours.hours.ago) } | ||
|
||
after_initialize do | ||
if self.token_hash.blank? | ||
@token ||= SecureRandom.hex | ||
self.token_hash = self.class.hash_token(@token) | ||
end | ||
end | ||
|
||
after_create do | ||
EmailToken | ||
.where(user_id: self.user_id) | ||
.where(scope: [nil, self.scope]) | ||
.where.not(id: self.id) | ||
.update_all(expired: true) | ||
end | ||
|
||
before_validation do | ||
self.email = self.email.downcase if self.email | ||
end | ||
|
||
before_save do | ||
if self.scope.blank? | ||
Discourse.deprecate("EmailToken#scope cannot be empty.", output_in_test: true) | ||
end | ||
end | ||
|
||
# TODO(2022-01-01): Remove | ||
self.ignored_columns = %w{token} | ||
|
||
def self.scopes | ||
@scopes ||= Enum.new( | ||
signup: 1, | ||
password_reset: 2, | ||
email_login: 3, | ||
email_update: 4, | ||
) | ||
end | ||
|
||
def token | ||
raise TokenAccessError.new if @token.blank? | ||
|
||
@token | ||
end | ||
|
||
def self.confirm(token, scope: nil, skip_reviewable: false) | ||
User.transaction do | ||
email_token = confirmable(token, scope: scope) | ||
return if email_token.blank? | ||
|
||
email_token.update!(confirmed: true) | ||
|
||
user = email_token.user | ||
user.send_welcome_message = !user.active? | ||
user.email = email_token.email | ||
user.active = true | ||
user.custom_fields.delete('activation_reminder') | ||
user.save! | ||
user.create_reviewable if !skip_reviewable | ||
user.set_automatic_groups | ||
DiscourseEvent.trigger(:user_confirmed_email, user) | ||
Invite.redeem_from_email(user.email) if scope == EmailToken.scopes[:signup] | ||
|
||
user.reload | ||
end | ||
rescue ActiveRecord::RecordInvalid | ||
# If the user's email is already taken, just return nil (failure) | ||
end | ||
|
||
def self.confirmable(token, scope: nil) | ||
return nil if token.blank? | ||
|
||
relation = unconfirmed.active | ||
.includes(:user) | ||
.where(token_hash: hash_token(token)) | ||
|
||
# TODO(2022-01-01): All email tokens should have scopes by now | ||
if !scope | ||
relation.first | ||
else | ||
relation.where(scope: scope).first || relation.where(scope: nil).first | ||
end | ||
end | ||
|
||
def self.enqueue_signup_email(email_token, to_address: nil) | ||
Jobs.enqueue( | ||
:critical_user_email, | ||
type: "signup", | ||
user_id: email_token.user_id, | ||
email_token: email_token.token, | ||
to_address: to_address | ||
) | ||
end | ||
|
||
def self.hash_token(token) | ||
Digest::SHA256.hexdigest(token) | ||
end | ||
end | ||
|
||
# == Schema Information | ||
# | ||
# Table name: email_tokens | ||
# | ||
# id :integer not null, primary key | ||
# user_id :integer not null | ||
# email :string not null | ||
# confirmed :boolean default(FALSE), not null | ||
# expired :boolean default(FALSE), not null | ||
# created_at :datetime not null | ||
# updated_at :datetime not null | ||
# token_hash :string not null | ||
# scope :integer | ||
# | ||
# Indexes | ||
# | ||
# index_email_tokens_on_token_hash (token_hash) UNIQUE | ||
# index_email_tokens_on_user_id (user_id) | ||
# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# frozen_string_literal: true | ||
|
||
class EmbeddableHost < ActiveRecord::Base | ||
validate :host_must_be_valid | ||
belongs_to :category | ||
after_destroy :reset_embedding_settings | ||
|
||
before_validation do | ||
self.host.sub!(/^https?:\/\//, '') | ||
self.host.sub!(/\/.*$/, '') | ||
end | ||
|
||
# TODO(2021-07-23): Remove | ||
self.ignored_columns = ["path_whitelist"] | ||
|
||
def self.record_for_url(uri) | ||
if uri.is_a?(String) | ||
uri = begin | ||
URI(UrlHelper.normalized_encode(uri)) | ||
rescue URI::Error, Addressable::URI::InvalidURIError | ||
end | ||
end | ||
|
||
return false unless uri.present? | ||
|
||
host = uri.host | ||
return false unless host.present? | ||
|
||
if uri.port.present? && uri.port != 80 && uri.port != 443 | ||
host << ":#{uri.port}" | ||
end | ||
|
||
path = uri.path | ||
path << "?" << uri.query if uri.query.present? | ||
|
||
where("lower(host) = ?", host).each do |eh| | ||
return eh if eh.allowed_paths.blank? | ||
|
||
path_regexp = Regexp.new(eh.allowed_paths) | ||
return eh if path_regexp.match(path) || path_regexp.match(UrlHelper.unencode(path)) | ||
end | ||
|
||
nil | ||
end | ||
|
||
def self.url_allowed?(url) | ||
return false if url.nil? | ||
|
||
# Work around IFRAME reload on WebKit where the referer will be set to the Forum URL | ||
return true if url&.starts_with?(Discourse.base_url) && EmbeddableHost.exists? | ||
|
||
uri = begin | ||
URI(UrlHelper.normalized_encode(url)) | ||
rescue URI::Error | ||
end | ||
|
||
uri.present? && record_for_url(uri).present? | ||
end | ||
|
||
private | ||
|
||
def reset_embedding_settings | ||
unless EmbeddableHost.exists? | ||
Embedding.settings.each { |s| SiteSetting.set(s.to_s, SiteSetting.defaults[s]) } | ||
end | ||
end | ||
|
||
def host_must_be_valid | ||
if host !~ /\A[a-z0-9]+([\-\.]+{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i && | ||
host !~ /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(:[0-9]{1,5})?(\/.*)?\Z/ && | ||
host !~ /\A([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.)?localhost(\:[0-9]{1,5})?(\/.*)?\Z/i | ||
errors.add(:host, I18n.t('errors.messages.invalid')) | ||
end | ||
end | ||
end | ||
|
||
# == Schema Information | ||
# | ||
# Table name: embeddable_hosts | ||
# | ||
# id :integer not null, primary key | ||
# host :string not null | ||
# category_id :integer not null | ||
# created_at :datetime not null | ||
# updated_at :datetime not null | ||
# class_name :string | ||
# allowed_paths :string | ||
# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'has_errors' | ||
|
||
class Embedding < OpenStruct | ||
include HasErrors | ||
|
||
def self.settings | ||
%i(embed_by_username | ||
embed_post_limit | ||
embed_title_scrubber | ||
embed_truncate | ||
embed_unlisted | ||
allowed_embed_selectors | ||
blocked_embed_selectors | ||
allowed_embed_classnames) | ||
end | ||
|
||
def base_url | ||
Discourse.base_url | ||
end | ||
|
||
def save | ||
Embedding.settings.each do |s| | ||
SiteSetting.set(s, public_send(s)) | ||
end | ||
true | ||
rescue Discourse::InvalidParameters => p | ||
errors.add :base, p.to_s | ||
false | ||
end | ||
|
||
def embeddable_hosts | ||
EmbeddableHost.all.order(:host) | ||
end | ||
|
||
def self.find | ||
embedding_args = { id: 'default' } | ||
Embedding.settings.each { |s| embedding_args[s] = SiteSetting.get(s) } | ||
Embedding.new(embedding_args) | ||
end | ||
end |
Oops, something went wrong.