diff options
author | kibigo! <marrus-sh@users.noreply.github.com> | 2017-07-15 14:33:15 -0700 |
---|---|---|
committer | kibigo! <marrus-sh@users.noreply.github.com> | 2017-07-15 14:33:15 -0700 |
commit | 09cfc079b0958c42fe619e2d88c3f9fd1d7c7900 (patch) | |
tree | 156de790a5bec0fdf050e392bee8a64b220d3a9d /app/models | |
parent | 08d021916db9e350259b925d7e562aa13ba37422 (diff) | |
parent | 695439775eacea081c7257aabab39d0ec6b492dc (diff) |
Merge upstream (#81)
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account.rb | 12 | ||||
-rw-r--r-- | app/models/concerns/remotable.rb | 3 | ||||
-rw-r--r-- | app/models/domain_block.rb | 2 | ||||
-rw-r--r-- | app/models/import.rb | 2 | ||||
-rw-r--r-- | app/models/session_activation.rb | 12 | ||||
-rw-r--r-- | app/models/status.rb | 16 | ||||
-rw-r--r-- | app/models/subscription.rb | 6 | ||||
-rw-r--r-- | app/models/tag.rb | 7 | ||||
-rw-r--r-- | app/models/user.rb | 12 | ||||
-rw-r--r-- | app/models/web/push_subscription.rb | 190 |
10 files changed, 246 insertions, 16 deletions
diff --git a/app/models/account.rb b/app/models/account.rb index 49d2c88f6..9f8e22adf 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -47,6 +47,7 @@ class Account < ApplicationRecord include AccountInteractions include Attachmentable include Remotable + include EmojiHelper # Local users has_one :user, inverse_of: :account @@ -129,7 +130,7 @@ class Account < ApplicationRecord end def subscription(webhook_url) - OStatus2::Subscription.new(remote_url, secret: secret, lease_seconds: 86_400 * 30, webhook: webhook_url, hub: hub_url) + OStatus2::Subscription.new(remote_url, secret: secret, lease_seconds: 30.days.seconds, webhook: webhook_url, hub: hub_url) end def save_with_optional_media! @@ -240,9 +241,18 @@ class Account < ApplicationRecord before_create :generate_keys before_validation :normalize_domain + before_validation :prepare_contents, if: :local? private + def prepare_contents + display_name&.strip! + note&.strip! + + self.display_name = emojify(display_name) + self.note = emojify(note) + end + def generate_keys return unless local? diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index b4f169649..1bd87a642 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true module Remotable - include HttpHelper extend ActiveSupport::Concern included do @@ -20,7 +19,7 @@ module Remotable return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? || self[attribute_name] == url begin - response = http_client.get(url) + response = Request.new(:get, url).perform return if response.code != 200 diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 99dae9c1d..f26e8183f 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -8,7 +8,7 @@ # created_at :datetime not null # updated_at :datetime not null # severity :integer default("silence") -# reject_media :boolean +# reject_media :boolean default(FALSE), not null # class DomainBlock < ApplicationRecord diff --git a/app/models/import.rb b/app/models/import.rb index 8c6253d49..815e02589 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -6,7 +6,7 @@ # id :integer not null, primary key # account_id :integer not null # type :integer not null -# approved :boolean +# approved :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # data_file_name :string diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index 887e3e3bd..7eb16af8f 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -3,6 +3,17 @@ # # Table name: session_activations # +# id :integer not null, primary key +# user_id :integer not null +# session_id :string not null +# created_at :datetime not null +# updated_at :datetime not null +# user_agent :string default(""), not null +# ip :inet +# access_token_id :integer +# web_push_subscription_id :integer +# + # id :integer not null, primary key # user_id :integer not null # session_id :string not null @@ -15,6 +26,7 @@ class SessionActivation < ApplicationRecord belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy + belongs_to :web_push_subscription, class_name: 'Web::PushSubscription', dependent: :destroy delegate :token, to: :access_token, diff --git a/app/models/status.rb b/app/models/status.rb index 791d96df1..24eaf7071 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -12,12 +12,12 @@ # in_reply_to_id :integer # reblog_of_id :integer # url :string -# sensitive :boolean default(FALSE) +# sensitive :boolean default(FALSE), not null # visibility :integer default("public"), not null # in_reply_to_account_id :integer # application_id :integer # spoiler_text :text default(""), not null -# reply :boolean default(FALSE) +# reply :boolean default(FALSE), not null # favourites_count :integer default(0), not null # reblogs_count :integer default(0), not null # language :string @@ -29,6 +29,7 @@ class Status < ApplicationRecord include Streamable include Cacheable include StatusThreadingConcern + include EmojiHelper enum visibility: [:public, :unlisted, :private, :direct], _suffix: :visibility @@ -120,10 +121,11 @@ class Status < ApplicationRecord !sensitive? && media_attachments.any? end - before_validation :prepare_contents + before_validation :prepare_contents, if: :local? before_validation :set_reblog before_validation :set_visibility before_validation :set_conversation + before_validation :set_sensitivity class << self def not_in_filtered_languages(account) @@ -240,6 +242,9 @@ class Status < ApplicationRecord def prepare_contents text&.strip! spoiler_text&.strip! + + self.text = emojify(text) + self.spoiler_text = emojify(spoiler_text) end def set_reblog @@ -248,6 +253,11 @@ class Status < ApplicationRecord def set_visibility self.visibility = (account.locked? ? :private : :public) if visibility.nil? + self.sensitive = false if sensitive.nil? + end + + def set_sensitivity + self.sensitive = sensitive || spoiler_text.present? end def set_conversation diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 35a228df0..bf643c1f9 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true - # == Schema Information # # Table name: subscriptions @@ -13,11 +12,12 @@ # created_at :datetime not null # updated_at :datetime not null # last_successful_delivery_at :datetime +# domain :string # class Subscription < ApplicationRecord - MIN_EXPIRATION = 7.days.seconds.to_i - MAX_EXPIRATION = 30.days.seconds.to_i + MIN_EXPIRATION = 1.day.to_i + MAX_EXPIRATION = 30.days.to_i belongs_to :account, required: true diff --git a/app/models/tag.rb b/app/models/tag.rb index 08e3c1b03..0fa08e157 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -12,9 +12,10 @@ class Tag < ApplicationRecord has_and_belongs_to_many :statuses - HASHTAG_RE = /(?:^|[^\/\)\w])#([[:word:]_]*[[:alpha:]_][[:word:]_]*)/i + HASHTAG_NAME_RE = '[[:word:]_]*[[:alpha:]_][[:word:]_]*' + HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i - validates :name, presence: true, uniqueness: true + validates :name, presence: true, uniqueness: true, format: { with: /\A#{HASHTAG_NAME_RE}\z/i } def to_param name @@ -23,7 +24,7 @@ class Tag < ApplicationRecord class << self def search_for(term, limit = 5) pattern = sanitize_sql_like(term) + '%' - Tag.where('name like ?', pattern).order(:name).limit(limit) + Tag.where('lower(name) like lower(?)', pattern).order(:name).limit(limit) end end end diff --git a/app/models/user.rb b/app/models/user.rb index c80115a08..becf0018f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -17,7 +17,7 @@ # last_sign_in_at :datetime # current_sign_in_ip :inet # last_sign_in_ip :inet -# admin :boolean default(FALSE) +# admin :boolean default(FALSE), not null # confirmation_token :string # confirmed_at :datetime # confirmation_sent_at :datetime @@ -27,7 +27,7 @@ # encrypted_otp_secret_iv :string # encrypted_otp_secret_salt :string # consumed_timestep :integer -# otp_required_for_login :boolean +# otp_required_for_login :boolean default(FALSE), not null # last_emailed_at :datetime # otp_backup_codes :string is an Array # filtered_languages :string default([]), not null, is an Array @@ -99,6 +99,10 @@ class User < ApplicationRecord settings.system_font_ui end + def setting_noindex + settings.noindex + end + def activate_session(request) session_activations.activate(session_id: SecureRandom.hex, user_agent: request.user_agent, @@ -113,6 +117,10 @@ class User < ApplicationRecord session_activations.active? id end + def web_push_subscription(session) + session.web_push_subscription.nil? ? nil : session.web_push_subscription.as_payload + end + protected def send_devise_notification(notification, *args) diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb new file mode 100644 index 000000000..4440706a6 --- /dev/null +++ b/app/models/web/push_subscription.rb @@ -0,0 +1,190 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: web_push_subscriptions +# +# id :integer not null, primary key +# endpoint :string not null +# key_p256dh :string not null +# key_auth :string not null +# data :json +# created_at :datetime not null +# updated_at :datetime not null +# + +class Web::PushSubscription < ApplicationRecord + include RoutingHelper + include StreamEntriesHelper + include ActionView::Helpers::TranslationHelper + include ActionView::Helpers::SanitizeHelper + + has_one :session_activation + + before_create :send_welcome_notification + + def push(notification) + return unless pushable? notification + + name = display_name notification.from_account + title = title_str(name, notification) + body = body_str notification + dir = dir_str body + url = url_str notification + image = image_str notification + actions = actions_arr notification + + access_token = actions.empty? ? nil : find_or_create_access_token(notification).token + nsfw = notification.target_status.nil? || notification.target_status.spoiler_text.empty? ? nil : notification.target_status.spoiler_text + + # TODO: Make sure that the payload does not exceed 4KB - Webpush::PayloadTooLarge + # TODO: Queue the requests - Webpush::TooManyRequests + Webpush.payload_send( + message: JSON.generate( + title: title, + dir: dir, + image: image, + badge: full_asset_url('badge.png'), + tag: notification.id, + timestamp: notification.created_at, + icon: notification.from_account.avatar_static_url, + data: { + content: decoder.decode(strip_tags(body)), + nsfw: nsfw.nil? ? nil : decoder.decode(strip_tags(nsfw)), + url: url, + actions: actions, + access_token: access_token, + } + ), + endpoint: endpoint, + p256dh: key_p256dh, + auth: key_auth, + vapid: { + # subject: "mailto:#{Setting.site_contact_email}", + private_key: Rails.configuration.x.vapid_private_key, + public_key: Rails.configuration.x.vapid_public_key, + }, + ttl: 40 * 60 * 60 # 48 hours + ) + end + + def as_payload + payload = { + id: id, + endpoint: endpoint, + } + + payload[:alerts] = data['alerts'] if data && data.key?('alerts') + + payload + end + + private + + def title_str(name, notification) + case notification.type + when :mention then translate('push_notifications.mention.title', name: name) + when :follow then translate('push_notifications.follow.title', name: name) + when :favourite then translate('push_notifications.favourite.title', name: name) + when :reblog then translate('push_notifications.reblog.title', name: name) + end + end + + def body_str(notification) + case notification.type + when :mention then notification.target_status.text + when :follow then notification.from_account.note + when :favourite then notification.target_status.text + when :reblog then notification.target_status.text + end + end + + def url_str(notification) + case notification.type + when :mention then web_url("statuses/#{notification.target_status.id}") + when :follow then web_url("accounts/#{notification.from_account.id}") + when :favourite then web_url("statuses/#{notification.target_status.id}") + when :reblog then web_url("statuses/#{notification.target_status.id}") + end + end + + def actions_arr(notification) + actions = + case notification.type + when :mention then [ + { + title: translate('push_notifications.mention.action_favourite'), + icon: full_asset_url('emoji/2764.png'), + todo: 'request', + method: 'POST', + action: "/api/v1/statuses/#{notification.target_status.id}/favourite", + }, + ] + else [] + end + + should_hide = notification.type.equal?(:mention) && !notification.target_status.nil? && (notification.target_status.sensitive || !notification.target_status.spoiler_text.empty?) + can_boost = notification.type.equal?(:mention) && !notification.target_status.nil? && !notification.target_status.hidden? + + if should_hide + actions.insert(0, title: translate('push_notifications.mention.action_expand'), icon: full_asset_url('emoji/1f441.png'), todo: 'expand', action: 'expand') + end + + if can_boost + actions << { title: translate('push_notifications.mention.action_boost'), icon: full_asset_url('emoji/1f504.png'), todo: 'request', method: 'POST', action: "/api/v1/statuses/#{notification.target_status.id}/reblog" } + end + + actions + end + + def image_str(notification) + return nil if notification.target_status.nil? || notification.target_status.media_attachments.empty? + + full_asset_url(notification.target_status.media_attachments.first.file.url(:small)) + end + + def dir_str(body) + rtl?(body) ? 'rtl' : 'ltr' + end + + def pushable?(notification) + data && data.key?('alerts') && data['alerts'][notification.type.to_s] + end + + def send_welcome_notification + Webpush.payload_send( + message: JSON.generate( + title: translate('push_notifications.subscribed.title'), + icon: full_asset_url('android-chrome-192x192.png'), + badge: full_asset_url('badge.png'), + data: { + content: translate('push_notifications.subscribed.body'), + actions: [], + url: web_url('notifications'), + } + ), + endpoint: endpoint, + p256dh: key_p256dh, + auth: key_auth, + vapid: { + # subject: "mailto:#{Setting.site_contact_email}", + private_key: Rails.configuration.x.vapid_private_key, + public_key: Rails.configuration.x.vapid_public_key, + }, + ttl: 5 * 60 # 5 minutes + ) + end + + def find_or_create_access_token(notification) + Doorkeeper::AccessToken.find_or_create_for( + Doorkeeper::Application.find_by(superapp: true), + notification.account.user.id, + Doorkeeper::OAuth::Scopes.from_string('read write follow'), + Doorkeeper.configuration.access_token_expires_in, + Doorkeeper.configuration.refresh_token_enabled? + ) + end + + def decoder + @decoder ||= HTMLEntities.new + end +end |