Skip to content

Commit

Permalink
Upload migration files from profile settings and start processing
Browse files Browse the repository at this point in the history
  • Loading branch information
tclaus committed Jul 23, 2021
1 parent 8d5abe8 commit afa3462
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 9 deletions.
20 changes: 20 additions & 0 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def user_params
:post_default_public,
:otp_required_for_login,
:otp_secret,
:exported_photos_file,
:export,
email_preferences: UserPreference::VALID_EMAIL_TYPES.map(&:to_sym)
)
end
Expand All @@ -172,6 +174,8 @@ def update_user(user_data)
change_post_default(user_data)
elsif user_data[:color_theme]
change_settings(user_data, "users.update.color_theme_changed", "users.update.color_theme_not_changed")
elsif user_data[:export] || user_data[:exported_photos_file]
upload_export_files(user_data)
else
change_settings(user_data)
end
Expand Down Expand Up @@ -235,6 +239,22 @@ def change_email(user_data)
end
end

def upload_export_files(user_data)
logger.info "Start importing profile"
@user.export = user_data[:export] if user_data[:export]
@user.exported_photos_file = user_data[:exported_photos_file] if user_data[:exported_photos_file]
if @user.save
flash.now[:notice] = "A profile migration is scheduled"
else
flash.now[:error] = "An error occured scheduling a migration: #{@user.errors.full_messages}"
end
start_migration_account
end

def start_migration_account
Workers::ImportProfile.perform_async(@user.username)
end

def change_settings(user_data, successful="users.update.settings_updated", error="users.update.settings_not_updated")
if @user.update_attributes(user_data)
flash.now[:notice] = t(successful)
Expand Down
6 changes: 3 additions & 3 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ def like_for(target)
######### Data export ##################
mount_uploader :export, ExportedUser

######### Photos export ##################
mount_uploader :exported_photos_file, ExportedPhotos

def queue_export
update exporting: true, export: nil, exported_at: nil
Workers::ExportUser.perform_async(id)
Expand All @@ -335,9 +338,6 @@ def compressed_export
ActiveSupport::Gzip.compress Diaspora::Exporter.new(self).execute
end

######### Photos export ##################
mount_uploader :exported_photos_file, ExportedPhotos

def queue_export_photos
update exporting_photos: true, exported_photos_file: nil, exported_photos_at: nil
Workers::ExportPhotos.perform_async(id)
Expand Down
10 changes: 10 additions & 0 deletions app/services/migration_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def validate
archive_validator.validate
raise ArchiveValidationFailed, errors.join("\n") if errors.any?
raise MigrationAlreadyExists if AccountMigration.where(old_person: old_person).any?
raise SelfMigrationNotAllowed if self_import?
end

def perform!
Expand All @@ -21,6 +22,12 @@ def perform!
run_migration
end

def self_import?
source_diaspora_id = archive_validator.archive_author_diaspora_id
target_diaspora_id = "#{new_user_name}#{User.diaspora_id_host}"
source_diaspora_id.eql?(target_diaspora_id)
end

# when old person can't be resolved we still import data but we don't create&perform AccountMigration instance
def only_import?
old_person.nil?
Expand Down Expand Up @@ -73,4 +80,7 @@ class ArchiveValidationFailed < RuntimeError

class MigrationAlreadyExists < RuntimeError
end

class SelfMigrationNotAllowed < RuntimeError
end
end
7 changes: 6 additions & 1 deletion app/uploaders/exported_photos.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ def store_dir
"uploads/users"
end

def extension_white_list
%w[zip]
end

def filename
"#{model.username}_photos_#{secure_token}.zip" if original_filename.present?
extension = File.extname(@filename) if @filename
"#{model.username}_photos_#{secure_token}#{extension}"
end
end
7 changes: 4 additions & 3 deletions app/uploaders/exported_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ def store_dir
"uploads/users"
end

def extension_allowlist
%w[gz]
def extension_whitelist
%w[gz zip json]
end

def filename
"#{model.username}_diaspora_data_#{secure_token}.json.gz" if original_filename.present?
extension = File.extname(@filename) if @filename
"#{model.username}_data_#{secure_token}#{extension}"
end
end
25 changes: 23 additions & 2 deletions app/views/users/_edit.haml
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,11 @@
%hr

.row
.col-md-6.account-data
.col-md-12.account-data
%h3= t(".export_data")
%p If you want to migrate this account to another pod, start by exporting your data using the buttons below.
.row
.col-md-6
%h4= t("profile")
.form-group
- if current_user.exporting
Expand All @@ -210,6 +213,7 @@
= link_to t(".request_export"), export_profile_user_path, method: :post,
class: "btn btn-default"

.col-md-6
%h4= t("javascripts.profile.photos")
.form-group
- if current_user.exporting_photos
Expand All @@ -224,7 +228,24 @@
= link_to t(".request_export_photos"), export_photos_user_path, method: :post,
class: "btn btn-default"

.col-md-6
.row
.col-md-12
%h3
Import another account into
= current_user.diaspora_handle
%p
diaspora* allows you to merge an account into another, even if the accounts are in two different pods.
This way, you can migrate from one pod to another one.
For this, you need to upload into this account the data and the photo archives you have exported from the
settings in the other account.
%strong There is no way back,
an account merged into another one can't be restored.
.form-group
.btn.btn-primary{id: "import_account", data: {toggle: "modal", target: "#importAccountModal"}}
Import another account...
= render "import_account_modal"
.row
.col-md-12
%h3
= t(".close_account_text")
.form-group
Expand Down
53 changes: 53 additions & 0 deletions app/views/users/_import_account_modal.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.modal.fade{id: "importAccountModal",
tabindex: "-1",
role: "dialog",
aria: {labelledby: "importAccountModalLabel", hidden: "true"}}
.modal-dialog
.modal-content
.modal-header
%button.close{type: "button", data: {dismiss: "modal"}, aria: {hidden: "true"}}
&times;
%h3.modal-title{id: "importAccountModalLabel"}
Import another account
.modal-body
%p
You are about to import and merge another account into
= current_user.diaspora_handle
/.
Here is what's going to happen:
%ol
%li
Your profile (name, bio, birthday, gender...) will be updated with the content from the imported account
%li
Your settings (theme, visibility...) will be updated with those from the imported account
%li
All your aspects and contacts will be added to this account. This will be transparent for them, they
will be linked to that account as they were linked with the account you are importing
%li
All posts and comments you made with the imported account will be imported in that account and seen
as posted by this accounts by the other users
%li
%strong
The imported account and all its linked data will be deleted from the previous pod.
%p
If the pod of the account you are importing is offline, your contacts and posts will still be imported,
but your contacts will receive a notification as if you started sharing with them.
= form_for current_user, url: edit_user_path, html: {method: :put, multipart: true, class: "form-horizontal"} do |f|
.row
.col-md-6
%label Select the data archive:
= f.file_field :export, accept: "application/json, application/zip, application/gzip"
.col-md-6
%label Select the photos archive:
= f.file_field :exported_photos_file, accept: "application/zip"
%h4
The original account will be imported
%strong
and deleted. This cannot be undone!
%p
.clearfix
.btn.btn-default{data: {dismiss: "modal"}}
Cancel
= f.submit 'Import and merge', class: "btn btn-primary.pull-right", id: "change_email_preferences"
57 changes: 57 additions & 0 deletions app/workers/import_profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module Workers
class ImportProfile < Base
sidekiq_options queue: :medium

include Diaspora::Logging

def perform(user_id)
user = User.find_by(username: user_id)
if user.nil?
logger.error "A user with name #{user_id} not a local user"
else
logger.info "Import for profile #{user_id} at path #{user.export.current_path} requested"
import_user_profile(user.export.current_path, user_id)

logger.info("Moving photos from impport file")
import_user_photos(user)
end
end

private
def import_user_profile(path_to_profile, username)
service = MigrationService.new(path_to_profile, username)
logger.info "Start validating user profile #{username}"
service.validate
logger.info "Start importing user profile for #{username}"
service.perform!
logger.info "Successfully imported profile: #{username}"
rescue MigrationService::ArchiveValidationFailed => e
logger.error "Errors in the archive found: #{e.message}"
rescue MigrationService::MigrationAlreadyExists
logger.error "Migration record already exists for the user, can't continue"
rescue MigrationService::SelfMigrationNotAllowed
logger.error "You can't migrate onto your own account"
ensure
service.remove_intermediate_file
end

def import_user_photos(user)
unzip_photos_file(photo_file_path)
user.posts.find_in_batches do |photo|
photo_filename = photo.remote_photo_name
copy sorce photo.unprocessed_image.store_dir
photo.remote_photo_name = nil # migration sets this attribute, but file needs to be procesed
photo.unprocessed_image = photo_filename
photo.save(touch: false)

end
end

def unzip_photos_file(photo_file_path)
system("unzip", photo_file_path)
end

end
end
1 change: 1 addition & 0 deletions lib/archive_importer/post_importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def substitute_author

entity_data["photos"].each do |photo|
photo["entity_data"]["author"] = user.diaspora_handle
photo["entity_data"]["remote_photo_path"] = "#{AppConfig.pod_uri}uploads\/images\/"
end
end

Expand Down

0 comments on commit afa3462

Please sign in to comment.