mirror of https://github.com/mastodon/mastodon
This commit is contained in:
parent
03fb6c16ec
commit
e8875c6046
|
@ -11,8 +11,8 @@ class Api::V1::TimelinesController < ApiController
|
||||||
@statuses = cache_collection(@statuses)
|
@statuses = cache_collection(@statuses)
|
||||||
|
|
||||||
set_maps(@statuses)
|
set_maps(@statuses)
|
||||||
set_counters_maps(@statuses)
|
# set_counters_maps(@statuses)
|
||||||
set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
||||||
|
|
||||||
next_path = api_v1_home_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
|
next_path = api_v1_home_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
|
||||||
prev_path = api_v1_home_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
|
prev_path = api_v1_home_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
|
||||||
|
@ -27,8 +27,8 @@ class Api::V1::TimelinesController < ApiController
|
||||||
@statuses = cache_collection(@statuses)
|
@statuses = cache_collection(@statuses)
|
||||||
|
|
||||||
set_maps(@statuses)
|
set_maps(@statuses)
|
||||||
set_counters_maps(@statuses)
|
# set_counters_maps(@statuses)
|
||||||
set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
||||||
|
|
||||||
next_path = api_v1_public_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
|
next_path = api_v1_public_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
|
||||||
prev_path = api_v1_public_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
|
prev_path = api_v1_public_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
|
||||||
|
@ -44,8 +44,8 @@ class Api::V1::TimelinesController < ApiController
|
||||||
@statuses = cache_collection(@statuses)
|
@statuses = cache_collection(@statuses)
|
||||||
|
|
||||||
set_maps(@statuses)
|
set_maps(@statuses)
|
||||||
set_counters_maps(@statuses)
|
# set_counters_maps(@statuses)
|
||||||
set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
|
||||||
|
|
||||||
next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id) unless @statuses.empty?
|
next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id) unless @statuses.empty?
|
||||||
prev_path = api_v1_hashtag_timeline_url(params[:id], since_id: @statuses.first.id) unless @statuses.empty?
|
prev_path = api_v1_hashtag_timeline_url(params[:id], since_id: @statuses.first.id) unless @statuses.empty?
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Settings::ImportsController < ApplicationController
|
||||||
|
layout 'admin'
|
||||||
|
|
||||||
|
before_action :authenticate_user!
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def show
|
||||||
|
@import = Import.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@import = Import.new(import_params)
|
||||||
|
@import.account = @account
|
||||||
|
|
||||||
|
if @import.save
|
||||||
|
ImportWorker.perform_async(@import.id)
|
||||||
|
redirect_to settings_import_path, notice: I18n.t('imports.success')
|
||||||
|
else
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = current_user.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def import_params
|
||||||
|
params.require(:import).permit(:data, :type)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Import < ApplicationRecord
|
||||||
|
self.inheritance_column = false
|
||||||
|
|
||||||
|
enum type: [:following, :blocking]
|
||||||
|
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
FILE_TYPES = ['text/plain', 'text/csv'].freeze
|
||||||
|
|
||||||
|
has_attached_file :data, url: '/system/:hash.:extension', hash_secret: ENV.fetch('PAPERCLIP_SECRET')
|
||||||
|
validates_attachment_content_type :data, content_type: FILE_TYPES
|
||||||
|
end
|
|
@ -12,6 +12,15 @@
|
||||||
.content-wrapper
|
.content-wrapper
|
||||||
.content
|
.content
|
||||||
%h2= yield :page_title
|
%h2= yield :page_title
|
||||||
|
|
||||||
|
- if flash[:notice]
|
||||||
|
.flash-message.notice
|
||||||
|
%strong= flash[:notice]
|
||||||
|
|
||||||
|
- if flash[:alert]
|
||||||
|
.flash-message.alert
|
||||||
|
%strong= flash[:alert]
|
||||||
|
|
||||||
= yield
|
= yield
|
||||||
|
|
||||||
= render template: "layouts/application", locals: { body_classes: 'admin' }
|
= render template: "layouts/application", locals: { body_classes: 'admin' }
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('settings.import')
|
||||||
|
|
||||||
|
%p.hint= t('imports.preface')
|
||||||
|
|
||||||
|
= simple_form_for @import, url: settings_import_path do |f|
|
||||||
|
= f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }
|
||||||
|
= f.input :data, wrapper: :with_label, hint: t('simple_form.hints.imports.data')
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('imports.upload'), type: :submit
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
|
class ImportWorker
|
||||||
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options retry: false
|
||||||
|
|
||||||
|
def perform(import_id)
|
||||||
|
import = Import.find(import_id)
|
||||||
|
|
||||||
|
case import.type
|
||||||
|
when 'blocking'
|
||||||
|
process_blocks(import)
|
||||||
|
when 'following'
|
||||||
|
process_follows(import)
|
||||||
|
end
|
||||||
|
|
||||||
|
import.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_blocks(import)
|
||||||
|
from_account = import.account
|
||||||
|
|
||||||
|
CSV.foreach(import.data.path) do |row|
|
||||||
|
next if row.size != 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
target_account = FollowRemoteAccountService.new.call(row[0])
|
||||||
|
next if target_account.nil?
|
||||||
|
BlockService.new.call(from_account, target_account)
|
||||||
|
rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_follows(import)
|
||||||
|
from_account = import.account
|
||||||
|
|
||||||
|
CSV.foreach(import.data.path) do |row|
|
||||||
|
next if row.size != 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
FollowService.new.call(from_account, row[0])
|
||||||
|
rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -85,6 +85,13 @@ en:
|
||||||
validation_errors:
|
validation_errors:
|
||||||
one: Something isn't quite right yet! Please review the error below
|
one: Something isn't quite right yet! Please review the error below
|
||||||
other: Something isn't quite right yet! Please review %{count} errors below
|
other: Something isn't quite right yet! Please review %{count} errors below
|
||||||
|
imports:
|
||||||
|
preface: You can import certain data like all the people you are following or blocking into your account on this instance, from files created by an export on another instance.
|
||||||
|
success: Your data was successfully uploaded and will now be processed in due time
|
||||||
|
types:
|
||||||
|
blocking: Blocking list
|
||||||
|
following: Following list
|
||||||
|
upload: Upload
|
||||||
landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
|
landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
|
||||||
notification_mailer:
|
notification_mailer:
|
||||||
digest:
|
digest:
|
||||||
|
@ -124,6 +131,7 @@ en:
|
||||||
back: Back to Mastodon
|
back: Back to Mastodon
|
||||||
edit_profile: Edit profile
|
edit_profile: Edit profile
|
||||||
export: Data export
|
export: Data export
|
||||||
|
import: Import
|
||||||
preferences: Preferences
|
preferences: Preferences
|
||||||
settings: Settings
|
settings: Settings
|
||||||
two_factor_auth: Two-factor Authentication
|
two_factor_auth: Two-factor Authentication
|
||||||
|
|
|
@ -8,12 +8,15 @@ en:
|
||||||
header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px
|
header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px
|
||||||
locked: Requires you to manually approve followers and defaults post privacy to followers-only
|
locked: Requires you to manually approve followers and defaults post privacy to followers-only
|
||||||
note: At most 160 characters
|
note: At most 160 characters
|
||||||
|
imports:
|
||||||
|
data: CSV file exported from another Mastodon instance
|
||||||
labels:
|
labels:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: Avatar
|
avatar: Avatar
|
||||||
confirm_new_password: Confirm new password
|
confirm_new_password: Confirm new password
|
||||||
confirm_password: Confirm password
|
confirm_password: Confirm password
|
||||||
current_password: Current password
|
current_password: Current password
|
||||||
|
data: Data
|
||||||
display_name: Display name
|
display_name: Display name
|
||||||
email: E-mail address
|
email: E-mail address
|
||||||
header: Header
|
header: Header
|
||||||
|
@ -24,6 +27,7 @@ en:
|
||||||
otp_attempt: Two-factor code
|
otp_attempt: Two-factor code
|
||||||
password: Password
|
password: Password
|
||||||
setting_default_privacy: Post privacy
|
setting_default_privacy: Post privacy
|
||||||
|
type: Import type
|
||||||
username: Username
|
username: Username
|
||||||
interactions:
|
interactions:
|
||||||
must_be_follower: Block notifications from non-followers
|
must_be_follower: Block notifications from non-followers
|
||||||
|
|
|
@ -9,6 +9,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url
|
settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url
|
||||||
settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url
|
settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url
|
||||||
settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url
|
settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url
|
||||||
|
settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url
|
||||||
settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url
|
settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url
|
||||||
settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
|
settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,7 @@ Rails.application.routes.draw do
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
resource :profile, only: [:show, :update]
|
resource :profile, only: [:show, :update]
|
||||||
resource :preferences, only: [:show, :update]
|
resource :preferences, only: [:show, :update]
|
||||||
|
resource :import, only: [:show, :create]
|
||||||
|
|
||||||
resource :export, only: [:show] do
|
resource :export, only: [:show] do
|
||||||
collection do
|
collection do
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
class CreateImports < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :imports do |t|
|
||||||
|
t.integer :account_id, null: false
|
||||||
|
t.integer :type, null: false
|
||||||
|
t.boolean :approved
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
class AddAttachmentDataToImports < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
change_table :imports do |t|
|
||||||
|
t.attachment :data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_attachment :imports, :data
|
||||||
|
end
|
||||||
|
end
|
14
db/schema.rb
14
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20170330021336) do
|
ActiveRecord::Schema.define(version: 20170330164118) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -93,6 +93,18 @@ ActiveRecord::Schema.define(version: 20170330021336) do
|
||||||
t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree
|
t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "imports", force: :cascade do |t|
|
||||||
|
t.integer "account_id", null: false
|
||||||
|
t.integer "type", null: false
|
||||||
|
t.boolean "approved"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.string "data_file_name"
|
||||||
|
t.string "data_content_type"
|
||||||
|
t.integer "data_file_size"
|
||||||
|
t.datetime "data_updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "media_attachments", force: :cascade do |t|
|
create_table "media_attachments", force: :cascade do |t|
|
||||||
t.bigint "status_id"
|
t.bigint "status_id"
|
||||||
t.string "file_file_name"
|
t.string "file_file_name"
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fabricator(:import) do
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Import, type: :model do
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue