Merge branch 'master' into gs-master

master
David Yip 7 years ago
commit 870d71b78b
  1. 10
      app/controllers/admin/custom_emojis_controller.rb
  2. 4
      app/helpers/jsonld_helper.rb
  3. 2
      app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js
  4. 8
      app/lib/activitypub/activity/create.rb
  5. 1
      app/models/custom_emoji.rb
  6. 2
      app/serializers/rest/custom_emoji_serializer.rb
  7. 7
      app/views/admin/custom_emojis/_custom_emoji.html.haml
  8. 4
      config/locales/en.yml
  9. 2
      config/routes.rb
  10. 84
      db/migrate/20170920032311_fix_reblogs_in_feeds.rb
  11. 7
      db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb
  12. 1
      db/schema.rb

@ -22,6 +22,14 @@ module Admin
end end
end end
def update
if @custom_emoji.update(resource_params)
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg')
else
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.update_failed_msg')
end
end
def destroy def destroy
@custom_emoji.destroy @custom_emoji.destroy
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg')
@ -56,7 +64,7 @@ module Admin
end end
def resource_params def resource_params
params.require(:custom_emoji).permit(:shortcode, :image) params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
end end
def filtered_custom_emojis def filtered_custom_emojis

@ -9,6 +9,10 @@ module JsonLdHelper
value.is_a?(Array) ? value.first : value value.is_a?(Array) ? value.first : value
end end
def as_array(value)
value.is_a?(Array) ? value : [value]
end
def value_or_id(value) def value_or_id(value)
value.is_a?(String) || value.nil? ? value : value['id'] value.is_a?(String) || value.nil? ? value : value['id']
end end

@ -46,7 +46,7 @@ const getFrequentlyUsedEmojis = createSelector([
const getCustomEmojis = createSelector([ const getCustomEmojis = createSelector([
state => state.get('custom_emojis'), state => state.get('custom_emojis'),
], emojis => emojis.sort((a, b) => { ], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => {
const aShort = a.get('shortcode').toLowerCase(); const aShort = a.get('shortcode').toLowerCase();
const bShort = b.get('shortcode').toLowerCase(); const bShort = b.get('shortcode').toLowerCase();

@ -53,9 +53,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end end
def process_tags(status) def process_tags(status)
return unless @object['tag'].is_a?(Array) return if @object['tag'].nil?
@object['tag'].each do |tag| as_array(@object['tag']).each do |tag|
case tag['type'] case tag['type']
when 'Hashtag' when 'Hashtag'
process_hashtag tag, status process_hashtag tag, status
@ -103,9 +103,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end end
def process_attachments(status) def process_attachments(status)
return unless @object['attachment'].is_a?(Array) return if @object['attachment'].nil?
@object['attachment'].each do |attachment| as_array(@object['attachment']).each do |attachment|
next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank? next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank?
href = Addressable::URI.parse(attachment['url']).normalize.to_s href = Addressable::URI.parse(attachment['url']).normalize.to_s

@ -15,6 +15,7 @@
# disabled :boolean default(FALSE), not null # disabled :boolean default(FALSE), not null
# uri :string # uri :string
# image_remote_url :string # image_remote_url :string
# visible_in_picker :boolean default(TRUE), not null
# #
class CustomEmoji < ApplicationRecord class CustomEmoji < ApplicationRecord

@ -3,7 +3,7 @@
class REST::CustomEmojiSerializer < ActiveModel::Serializer class REST::CustomEmojiSerializer < ActiveModel::Serializer
include RoutingHelper include RoutingHelper
attributes :shortcode, :url, :static_url attributes :shortcode, :url, :static_url, :visible_in_picker
def url def url
full_asset_url(object.image.url) full_asset_url(object.image.url)

@ -9,7 +9,12 @@
- else - else
= custom_emoji.domain = custom_emoji.domain
%td %td
- unless custom_emoji.local? - if custom_emoji.local?
- if custom_emoji.visible_in_picker
= table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }), method: :patch
- else
= table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }), method: :patch
- else
= table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post
%td %td
- if custom_emoji.disabled? - if custom_emoji.disabled?

@ -130,11 +130,15 @@ en:
enable: Enable enable: Enable
enabled_msg: Successfully enabled that emoji enabled_msg: Successfully enabled that emoji
image_hint: PNG up to 50KB image_hint: PNG up to 50KB
listed: Listed
new: new:
title: Add new custom emoji title: Add new custom emoji
shortcode: Shortcode shortcode: Shortcode
shortcode_hint: At least 2 characters, only alphanumeric characters and underscores shortcode_hint: At least 2 characters, only alphanumeric characters and underscores
title: Custom emojis title: Custom emojis
unlisted: Unlisted
update_failed_msg: Could not update that emoji
updated_msg: Emoji successfully updated!
upload: Upload upload: Upload
domain_blocks: domain_blocks:
add_new: Add new add_new: Add new

@ -147,7 +147,7 @@ Rails.application.routes.draw do
resource :two_factor_authentication, only: [:destroy] resource :two_factor_authentication, only: [:destroy]
end end
resources :custom_emojis, only: [:index, :new, :create, :destroy] do resources :custom_emojis, only: [:index, :new, :create, :update, :destroy] do
member do member do
post :copy post :copy
post :enable post :enable

@ -3,48 +3,62 @@ class FixReblogsInFeeds < ActiveRecord::Migration[5.1]
redis = Redis.current redis = Redis.current
fm = FeedManager.instance fm = FeedManager.instance
# find_each is batched on the database side. # Old scheme:
User.includes(:account).find_each do |user| # Each user's feed zset had a series of score:value entries,
account = user.account # where "regular" statuses had the same score and value (their
# ID). Reblogs had a score of the reblogging status' ID, and a
# value of the reblogged status' ID.
# Old scheme: # New scheme:
# Each user's feed zset had a series of score:value entries, # The feed contains only entries with the same score and value.
# where "regular" statuses had the same score and value (their # Reblogs result in the reblogging status being added to the
# ID). Reblogs had a score of the reblogging status' ID, and a # feed, with an entry in a reblog tracking zset (where the score
# value of the reblogged status' ID. # is once again set to the reblogging status' ID, and the value
# is set to the reblogged status' ID). This is safe for Redis'
# New scheme: # float coersion because in this reblog tracking zset, we only
# The feed contains only entries with the same score and value. # need the rebloggging status' ID to be able to stop tracking
# Reblogs result in the reblogging status being added to the # entries after they have gotten too far down the feed, which
# feed, with an entry in a reblog tracking zset (where the score # does not require an exact value.
# is once again set to the reblogging status' ID, and the value
# is set to the reblogged status' ID). This is safe for Redis' # This process reads all feeds and writes 3 times for each reblogs.
# float coersion because in this reblog tracking zset, we only # So we use Lua script to avoid overhead between Ruby and Redis.
# need the rebloggging status' ID to be able to stop tracking script = <<-LUA
# entries after they have gotten too far down the feed, which local timeline_key = KEYS[1]
# does not require an exact value. local reblog_key = KEYS[2]
# So, first, we iterate over the user's feed to find any reblogs.
timeline_key = fm.key(:home, account.id)
reblog_key = fm.key(:home, account.id, 'reblogs')
redis.zrange(timeline_key, 0, -1, with_scores: true).each do |entry|
next if entry[0] == entry[1]
# The score and value don't match, so this is a reblog. -- So, first, we iterate over the user's feed to find any reblogs.
# (note that we're transitioning from IDs < 53 bits so we local items = redis.call('zrange', timeline_key, 0, -1, 'withscores')
# don't have to worry about the loss of precision)
for i = 1, #items, 2 do
local reblogged_id = items[i]
local reblogging_id = items[i + 1]
if (reblogged_id ~= reblogging_id) then
reblogged_id, reblogging_id = entry -- The score and value don't match, so this is a reblog.
-- (note that we're transitioning from IDs < 53 bits so we
-- don't have to worry about the loss of precision)
# Remove the old entry -- Remove the old entry
redis.zrem(timeline_key, reblogged_id) redis.call('zrem', timeline_key, reblogged_id)
# Add a new one for the reblogging status -- Add a new one for the reblogging status
redis.zadd(timeline_key, reblogging_id, reblogging_id) redis.call('zadd', timeline_key, reblogging_id, reblogging_id)
# Track the fact that this was a reblog -- Track the fact that this was a reblog
redis.zadd(reblog_key, reblogging_id, reblogged_id) redis.call('zadd', reblog_key, reblogging_id, reblogged_id)
end
end end
LUA
script_hash = redis.script(:load, script)
# find_each is batched on the database side.
User.includes(:account).find_each do |user|
account = user.account
timeline_key = fm.key(:home, account.id)
reblog_key = fm.key(:home, account.id, 'reblogs')
redis.evalsha(script_hash, [timeline_key, reblog_key])
end end
end end

@ -0,0 +1,7 @@
class AddVisibleInPickerToCustomEmoji < ActiveRecord::Migration[5.1]
def change
safety_assured {
add_column :custom_emojis, :visible_in_picker, :boolean, default: true, null: false
}
end
end

@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20171021191900) do
t.boolean "disabled", default: false, null: false t.boolean "disabled", default: false, null: false
t.string "uri" t.string "uri"
t.string "image_remote_url" t.string "image_remote_url"
t.boolean "visible_in_picker", default: true, null: false
t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
end end

Loading…
Cancel
Save