Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:

# Try to retrieve the gems from the cache
- name: 'Cache Gems'
uses: actions/cache@v1
uses: actions/cache@v2.1.5
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
Expand All @@ -62,7 +62,7 @@ jobs:

# Try to retrieve the yarn JS dependencies from the cache
- name: 'Cache Yarn Packages'
uses: actions/cache@v1
uses: actions/cache@v2.1.5
with:
path: node_modules/
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:

# Try to retrieve the gems from the cache
- name: 'Cache Gems'
uses: actions/cache@v1
uses: actions/cache@v2.1.5
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
Expand All @@ -79,7 +79,7 @@ jobs:

# Try to retrieve the yarn JS dependencies from the cache
- name: 'Cache Yarn Packages'
uses: actions/cache@v1
uses: actions/cache@v2.1.5
with:
path: node_modules/
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
Expand Down
49 changes: 49 additions & 0 deletions app/controllers/super_admin/orgs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,51 @@ def destroy
end
end

# POST /super_admin/:id/merge_analyze
def merge_analyze
@org = Org.includes(:templates, :tracker, :annotations,
:departments, :token_permission_types, :funded_plans,
identifiers: [:identifier_scheme],
guidance_groups: [guidances: [:themes]],
users: [identifiers: [:identifier_scheme]])
.find(params[:id])
authorize @org

lookup = OrgSelection::HashToOrgService.to_org(
hash: JSON.parse(merge_params[:id]), allow_create: false
)
@target_org = Org.includes(:templates, :tracker, :annotations,
:departments, :token_permission_types, :funded_plans,
identifiers: [:identifier_scheme],
guidance_groups: [guidances: [:themes]],
users: [identifiers: [:identifier_scheme]])
.find(lookup.id)
end

# POST /super_admin/:id/merge_commit
def merge_commit
@org = Org.find(params[:id])
authorize @org

@target_org = Org.find_by(id: merge_params[:target_org])

if @target_org.present?
if @target_org.merge!(to_be_merged: @org)
msg = "Successfully merged '#{@org.name}' into '#{@target_org.name}'"
redirect_to super_admin_orgs_path, notice: msg
else
msg = _("An error occurred while trying to merge the Organisations.")
redirect_to admin_edit_org_path(@org), alert: msg
end
else
msg = _("Unable to merge the two Organisations at this time.")
redirect_to admin_edit_org_path(@org), alert: msg
end
rescue JSON::ParserError
msg = _("Unable to determine what records need to be merged.")
redirect_to admin_edit_org_path(@org), alert: msg
end

private

def org_params
Expand All @@ -110,6 +155,10 @@ def org_params
:org_id, :org_name, :org_crosswalk)
end

def merge_params
params.require(:org).permit(:org_name, :org_sources, :org_crosswalk, :id, :target_org)
end

end

end
66 changes: 66 additions & 0 deletions app/helpers/super_admin/orgs/merge_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

module SuperAdmin

module Orgs

module MergeHelper

def org_column_content(attributes:)
return "No mergeable attributes" unless attributes.present? && attributes.keys.any?

html = "<ul>"
attributes.each_key do |key|
html += "<li><strong>#{key}</strong>: #{attributes[key]}</li>"
end
"#{html}</ul>"
end

# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def column_content(entries:, orcid:)
return _("None") unless entries.present? && entries.any?

html = "<ul>"
entries.each do |entry|
text = case entry.class.name
when "Annotation"
[entry.id, entry.text[0..20]].join(" - ")
when "Department"
[entry.id, entry.name].join(" - ")
when "Guidance"
_("Guidance for: %{themes}") % {
themes: entry.themes.collect(&:title).join(", ")
}
when "Identifier"
[entry.identifier_scheme&.name, entry.value].join(" - ")
when "TokenPermissionType"
entry.token_type.capitalize
when "Tracker"
entry.code
when "User"
[entry.email, entry.identifier_for_scheme(scheme: orcid)&.value].compact
.join(" - ")
else
[entry.id, entry.title].join(" - ")
end
html += "<li>#{text}</li>"
end
"#{html}</ul>"
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength

def merge_column_content(entries:, orcid:, to_org_name:)
return _("Nothing to merge") unless entries.present? && entries.any?

html = _("<p>The following %{object_types} will be moved over to '%{org_name}':</p>") % {
object_types: entries.first.class.name.pluralize,
org_name: to_org_name
}
html + column_content(entries: entries, orcid: orcid)
end

end

end

end
3 changes: 3 additions & 0 deletions app/javascript/src/orgs/adminEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,7 @@ $(() => {
e.preventDefault();
$(e.target).parent('a').tooltip('toggle');
});

initAutocomplete('#org-merge-controls .autocomplete');
scrubOrgSelectionParamsOnSubmit('form.edit_org');
});
2 changes: 1 addition & 1 deletion app/models/concerns/identifiable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def identifier_for_scheme(scheme:)

# Combines the existing identifiers with the new ones
def consolidate_identifiers!(array:)
return false unless array.present? && array.is_a?(Array)
return false unless array.present? && array.any?

array.each do |id|
next unless id.is_a?(Identifier) && id.value.present?
Expand Down
31 changes: 29 additions & 2 deletions app/models/guidance_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ def self.all_viewable(user)
all_viewable_groups = default_org_groups +
funder_groups +
organisation_groups
all_viewable_groups = all_viewable_groups.flatten.uniq
all_viewable_groups
all_viewable_groups.flatten.uniq
end

def self.create_org_default(org)
Expand All @@ -134,4 +133,32 @@ def self.create_org_default(org)
)
end

# ====================
# = Instance methods =
# ====================

def merge!(to_be_merged:)
return self unless to_be_merged.is_a?(GuidanceGroup)

GuidanceGroup.transaction do
# Reassociate any Plan-GuidanceGroup connections
to_be_merged.plans.each do |plan|
plan.guidance_groups << self
plan.save
end
# Reassociate the Guidances
to_be_merged.guidances.update_all(guidance_group_id: id)
to_be_merged.plans.delete_all

# Terminate the transaction if the resulting Org is not valid
raise ActiveRecord::Rollback unless save

# Reload and then drop the specified Org. The reload prevents ActiveRecord
# from also destroying associations that we've already reassociated above
raise ActiveRecord::Rollback unless to_be_merged.reload.destroy.present?

reload
end
end

end
91 changes: 91 additions & 0 deletions app/models/org.rb
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,48 @@ def grant_api!(token_permission_type)
token_permission_types.include? token_permission_type
end

# Merges the specified Org into this Org
# rubocop:disable Metrics/AbcSize
def merge!(to_be_merged:)
return self unless to_be_merged.is_a?(Org)

transaction do
merge_attributes!(to_be_merged: to_be_merged)

# Merge all of the associated objects
to_be_merged.annotations.update_all(org_id: id)
merge_departments!(to_be_merged: to_be_merged)
to_be_merged.funded_plans.update_all(funder_id: id)
merge_guidance_groups!(to_be_merged: to_be_merged)
consolidate_identifiers!(array: to_be_merged.identifiers)

# TODO: Use this commented out version once we've stopped overriding the
# default association method called `plans` on this model
# to_be_merged.plans.update_all(org_id: id)
Plan.where(org_id: to_be_merged.id).update_all(org_id: id)

to_be_merged.templates.update_all(org_id: id)
merge_token_permission_types!(to_be_merged: to_be_merged)
self.tracker = to_be_merged.tracker unless tracker.present?
to_be_merged.users.update_all(org_id: id)

# Terminate the transaction if the resulting Org is not valid
raise ActiveRecord::Rollback unless save

# Remove all of the remaining identifiers and token_permission_types
# that were not merged
to_be_merged.identifiers.delete_all
to_be_merged.token_permission_types.delete_all

# Reload and then drop the specified Org. The reload prevents ActiveRecord
# from also destroying associations that we've already reassociated above
raise ActiveRecord::Rollback unless to_be_merged.reload.destroy.present?

reload
end
end
# rubocop:enable Metrics/AbcSize

private

##
Expand All @@ -308,6 +350,55 @@ def resize_image
self.logo = logo.thumb("x100") # resize height and maintain aspect ratio
end

# rubocop:disable Metrics/AbcSize
def merge_attributes!(to_be_merged:)
return false unless to_be_merged.is_a?(Org)

self.target_url = to_be_merged.target_url unless target_url.present?
self.managed = true if !managed? && to_be_merged.managed?
self.links = to_be_merged.links unless links.nil? || links == "{\"org\":[]}"
self.logo_uid = to_be_merged.logo_uid unless logo.present?
self.logo_name = to_be_merged.logo_name unless logo.present?
self.contact_email = to_be_merged.contact_email unless contact_email.present?
self.contact_name = to_be_merged.contact_name unless contact_name.present?
self.feedback_enabled = to_be_merged.feedback_enabled unless feedback_enabled?
self.feedback_email_msg = to_be_merged.feedback_email_msg unless feedback_email_msg.present?
# rubocop:disable Layout/LineLength
self.feedback_email_subject = to_be_merged.feedback_email_subject unless feedback_email_subject.present?
# rubocop:enable Layout/LineLength
end
# rubocop:enable Metrics/AbcSize

def merge_departments!(to_be_merged:)
return false unless to_be_merged.is_a?(Org) && to_be_merged.departments.any?

existing = departments.collect { |dpt| dpt.name.downcase.strip }
# Do not attach departments if we already have an existing one
to_be_merged.departments.each do |department|
department.destroy if existing.include?(department.name.downcase)
department.update(org_id: id) unless existing.include?(department.name.downcase)
end
end

def merge_guidance_groups!(to_be_merged:)
return false unless to_be_merged.is_a?(Org) && to_be_merged.guidance_groups.any?

to_gg = guidance_groups.first
# Create a new GuidanceGroup if one does not already exist
to_gg = GuidanceGroup.create(org: self, name: abbreviation) unless to_gg.present?

# Merge the GuidanceGroups
to_be_merged.guidance_groups.each { |from_gg| to_gg.merge!(to_be_merged: from_gg) }
end

def merge_token_permission_types!(to_be_merged:)
return false unless to_be_merged.is_a?(Org) && to_be_merged.token_permission_types.any?

to_be_merged.token_permission_types.each do |perm_type|
token_permission_types << perm_type unless token_permission_types.include?(perm_type)
end
end

def affiliated_plan_ids
Rails.cache.fetch("org[#{id}].plans", expires_in: 2.seconds) do
Role.administrator.where(user_id: users.pluck(:id), active: true)
Expand Down
8 changes: 8 additions & 0 deletions app/policies/org_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ def templates?
true
end

def merge_analyze?
user.can_super_admin?
end

def merge_commit?
user.can_super_admin?
end

end
Loading