Skip to content

Commit 630d0dc

Browse files
authored
Merge pull request #2769 from DMPRoadmap/development
Bug fixes and a few maDMP features for v3.0.1
2 parents 44936d8 + 2ae57ff commit 630d0dc

103 files changed

Lines changed: 2929 additions & 1229 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/mysql.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ jobs:
1010
DB_ADAPTER: mysql2
1111
MYSQL_PWD: root
1212
RAILS_ENV: test
13-
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
1413

1514
steps:
1615
# Checkout the repo
@@ -40,7 +39,6 @@ jobs:
4039
cp config/database.yml.sample config/database.yml
4140
cp config/initializers/contact_us.rb.example config/initializers/contact_us.rb
4241
cp config/initializers/wicked_pdf.rb.example config/initializers/wicked_pdf.rb
43-
cp config/credentials.yml.enc.workflow config/credentials.yml.enc
4442
4543
# Try to retrieve the gems from the cache
4644
- name: 'Cache Gems'
@@ -57,6 +55,11 @@ jobs:
5755
bundle config path vendor/bundle
5856
bundle install --jobs 4 --retry 3 --without pgsql rollbar aws
5957
58+
- name: 'Setup Credentials'
59+
run: |
60+
# generate a default credential file and key
61+
EDITOR='echo "$(cat config/credentials.yml.example)" >' bundle exec rails credentials:edit
62+
6063
# Try to retrieve the yarn JS dependencies from the cache
6164
- name: 'Cache Yarn Packages'
6265
uses: actions/cache@v1

.github/workflows/postgres.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ jobs:
2626
env:
2727
RAILS_ENV: test
2828
DATABASE_URL: postgres://postgres:@localhost:5432/roadmap_test
29-
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
3029

3130
steps:
3231
# Checkout the repo
@@ -57,7 +56,6 @@ jobs:
5756
cp config/database.yml.sample config/database.yml
5857
cp config/initializers/contact_us.rb.example config/initializers/contact_us.rb
5958
cp config/initializers/wicked_pdf.rb.example config/initializers/wicked_pdf.rb
60-
cp config/credentials.yml.enc.workflow config/credentials.yml.enc
6159
6260
# Try to retrieve the gems from the cache
6361
- name: 'Cache Gems'
@@ -74,6 +72,11 @@ jobs:
7472
bundle config path vendor/bundle
7573
bundle install --jobs 4 --retry 3 --without mysql rollbar aws
7674
75+
- name: 'Setup Credentials'
76+
run: |
77+
# generate a default credential file and key
78+
EDITOR='echo "$(cat config/credentials.yml.example)" >' bundle exec rails credentials:edit
79+
7780
# Try to retrieve the yarn JS dependencies from the cache
7881
- name: 'Cache Yarn Packages'
7982
uses: actions/cache@v1

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![Actions Status](https://github.com/DMPRoadmap/roadmap/workflows/Tests%20-%20PostgreSQL/badge.svg)](https://github.com/DMPRoadmap/roadmap/actions)
77
[![Actions Status](https://github.com/DMPRoadmap/roadmap/workflows/Tests%20-%20MySQL/badge.svg)](https://github.com/DMPRoadmap/roadmap/actions)
88

9-
DMP Roadmap is a Data Management Planning tool. Management and development of DMP Roadmap is jointly provided by the Digital Curation Centre (DCC), http://www.dcc.ac.uk/, and the University of California Curation Center (UC3), http://www.cdlib.org/services/uc3/
9+
DMP Roadmap is a Data Management Planning tool. Management and development of DMP Roadmap is jointly provided by the Digital Curation Centre (DCC), http://www.dcc.ac.uk/, and the University of California Curation Center (UC3), http://www.cdlib.org/services/uc3/.
1010

1111
The tool has four main functions:
1212

@@ -23,7 +23,7 @@ Roadmap is a Ruby on Rails application and you will need to have:
2323
* Rails = 4.2
2424
* MySQL >= 5.0 OR PostgreSQL
2525

26-
Further detail on how to install Ruby on Rails applications are available from the Ruby on Rails site: http://rubyonrails.org
26+
Further detail on how to install Ruby on Rails applications are available from the Ruby on Rails site: http://rubyonrails.org.
2727

2828
Further details on how to install MySQL and create your first user and database. Be sure to follow the instructions for your particular environment.
2929
* Install: http://dev.mysql.com/downloads/mysql/
@@ -36,10 +36,10 @@ You may also find the following resources handy:
3636
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
3737

3838
#### Installation
39-
See the [Installation Guide](https://github.com/DMPRoadmap/roadmap/wiki/Installation) on the Wiki
39+
See the [Installation Guide](https://github.com/DMPRoadmap/roadmap/wiki/Installation) on the Wiki.
4040

4141
#### Troubleshooting
42-
See the [Troubleshooting Guide](https://github.com/DMPRoadmap/roadmap/wiki/Troubleshooting) on the Wiki
42+
See the [Troubleshooting Guide](https://github.com/DMPRoadmap/roadmap/wiki/Troubleshooting) on the Wiki.
4343

4444
#### Support
4545
Issues should be reported here on [Github Issues](https://github.com/DMPRoadmap/roadmap/issues)
@@ -56,7 +56,7 @@ If you would like to contribute to the project. Please follow these steps to sub
5656
* Then create a new Pull Request (PR) from your branch to this project's '_**development**_' branch in GitHub
5757
* The project team will then review your PR and communicate with you to convey any additional changes that would ensure that your work adheres to our guidelines.
5858

59-
See the [Contribution Guide](https://github.com/DMPRoadmap/roadmap/blob/development/CONTRIBUTING.md) on the Wiki for more details
59+
See the [Contribution Guide](https://github.com/DMPRoadmap/roadmap/blob/development/CONTRIBUTING.md) on the Wiki for more details.
6060

6161
#### License
6262
The DMP Roadmap project uses the <a href="./LICENSE.md">MIT License</a>.

app/controllers/api/v1/base_api_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def heartbeat
3333

3434
def render_error(errors:, status:)
3535
@payload = { errors: [errors] }
36-
render "/api/v1/error", status: status and return
36+
render "/api/v1/error", status: status
3737
end
3838

3939
private

app/controllers/api/v1/plans_controller.rb

Lines changed: 82 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,85 +9,74 @@ class PlansController < BaseApiController
99
respond_to :json
1010

1111
# GET /api/v1/plans/:id
12-
# rubocop:disable Metrics/AbcSize
1312
def show
14-
plans = Plan.where(id: params[:id]).limit(1)
15-
16-
if plans.any?
17-
if client.is_a?(User)
18-
# If the specified plan does not belong to the org or the owner's org
19-
if plans.first.org_id != client.org_id &&
20-
plans.first.owner&.org_id != client.org_id
21-
22-
# Kaminari pagination requires an ActiveRecord resultset :/
23-
plans = Plan.where(id: nil).limit(1)
24-
end
25-
26-
elsif client.is_a?(ApiClient) && plans.first.api_client_id != client.id &&
27-
!plans.first.publicly_visible?
28-
# Kaminari pagination requires an ActiveRecord resultset :/
29-
plans = Plan.where(id: nil).limit(1)
30-
end
31-
32-
if plans.present? && plans.any?
33-
@items = paginate_response(results: plans)
34-
render "/api/v1/plans/index", status: :ok
35-
else
36-
render_error(errors: [_("Plan not found")], status: :not_found)
37-
end
13+
plans = Api::V1::PlansPolicy::Scope.new(client, Plan).resolve
14+
.where(id: params[:id]).limit(1)
15+
16+
if plans.present? && plans.any?
17+
@items = paginate_response(results: plans)
18+
render "/api/v1/plans/index", status: :ok
3819
else
3920
render_error(errors: [_("Plan not found")], status: :not_found)
4021
end
4122
end
42-
# rubocop:enable Metrics/AbcSize
43-
# rubocop:enable
4423

4524
# POST /api/v1/plans
46-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/BlockNesting
25+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
4726
def create
4827
dmp = @json.with_indifferent_access.fetch(:items, []).first.fetch(:dmp, {})
4928

50-
# If a dmp_id was passed in try to find it
51-
if dmp[:dmp_id].present? && dmp[:dmp_id][:identifier].present?
52-
scheme = IdentifierScheme.by_name(dmp[:dmp_id][:type]).first
53-
dmp_id = Identifier.where(value: dmp[:dmp_id][:identifier],
54-
identifier_scheme: scheme)
55-
end
56-
57-
# Skip if this is an existing DMP
58-
if dmp_id.present?
59-
render_error(errors: _("Plan already exists. Send an update instead."),
60-
status: :bad_request)
29+
# Do a pass through the raw JSON and check to make sure all required fields
30+
# were present. If not, return the specific errors
31+
errs = Api::V1::JsonValidationService.validation_errors(json: dmp)
32+
render_error(errors: errs, status: :bad_request) and return if errs.any?
33+
34+
# Convert the JSON into a Plan and it's associations
35+
plan = Api::V1::Deserialization::Plan.deserialize(json: dmp)
36+
if plan.present?
37+
save_err = _("Unable to create your DMP")
38+
exists_err = _("Plan already exists. Send an update instead.")
39+
no_org_err = _("Could not determine ownership of the DMP. Please add an
40+
:affiliation to the :contact")
41+
42+
# Try to determine the Plan's owner
43+
owner = determine_owner(client: client, plan: plan)
44+
plan.org = owner.org if owner.present? && plan.org.blank?
45+
render_error(errors: no_org_err, status: :bad_request) and return unless plan.org.present?
46+
47+
# Validate the plan and it's associations and return errors with context
48+
# e.g. 'Contact affiliation name can't be blank' instead of 'name can't be blank'
49+
errs = Api::V1::ContextualErrorService.process_plan_errors(plan: plan)
50+
51+
# The resulting plan (our its associations were invalid)
52+
render_error(errors: errs, status: :bad_request) and return if errs.any?
53+
# Skip if this is an existing DMP
54+
render_error(errors: exists_err, status: :bad_request) and return unless plan.new_record?
55+
56+
# If we cannot save for some reason then return an error
57+
plan = Api::V1::PersistenceService.safe_save(plan: plan)
58+
# rubocop:disable Layout/LineLength
59+
render_error(errors: save_err, status: :internal_server_error) and return if plan.new_record?
60+
61+
# rubocop:enable Layout/LineLength
62+
63+
# If the plan was generated by an ApiClient then associate them
64+
plan.update(api_client_id: client.id) if client.is_a?(ApiClient)
65+
66+
# Invite the Owner if they are a Contributor then attach the Owner to the Plan
67+
owner = invite_contributor(contributor: owner) if owner.is_a?(Contributor)
68+
plan.add_user!(owner.id, :creator)
69+
70+
# Kaminari Pagination requires an ActiveRecord result set :/
71+
@items = paginate_response(results: Plan.where(id: plan.id))
72+
render "/api/v1/plans/index", status: :created
6173
else
62-
# Time prior to JSON parser service call which will create the plan so
63-
# we can stop the creation of duplicate plans below
64-
now = (Time.now - 1.minute)
65-
plan = Api::V1::Deserialization::Plan.deserialize!(json: dmp)
66-
67-
if plan.present?
68-
if plan.created_at.utc < now.utc
69-
render_error(errors: _("Plan already exists. Send an update instead."),
70-
status: :bad_request)
71-
72-
else
73-
# If the plan was generated by an ApiClient then associate them
74-
plan.update(api_client_id: client.id) if client.is_a?(ApiClient)
75-
76-
assign_roles(plan: plan)
77-
78-
# Kaminari Pagination requires an ActiveRecord result set :/
79-
@items = paginate_response(results: Plan.where(id: plan.id))
80-
render "/api/v1/plans/index", status: :created
81-
end
82-
else
83-
render_error(errors: [_("Invalid JSON")], status: :bad_request)
84-
end
74+
render_error(errors: [_("Invalid JSON!")], status: :bad_request)
8575
end
8676
rescue JSON::ParserError
8777
render_error(errors: [_("Invalid JSON")], status: :bad_request)
8878
end
89-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/BlockNesting
90-
# rubocop:enable
79+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
9180

9281
# GET /api/v1/plans
9382
def index
@@ -111,33 +100,42 @@ def dmp_params
111100
params.require(:dmp).permit(plan_permitted_params).to_h
112101
end
113102

114-
def assign_roles(plan:)
115-
# Attach all of the authors and then invite them if necessary
116-
owner = nil
117-
plan.contributors.data_curation.each do |contributor|
118-
user = contributor_to_user(contributor: contributor)
119-
next unless user.present?
120-
121-
# Attach the role
122-
role = Role.new(user: user, plan: plan)
123-
role.creator = true if contributor.data_curation?
124-
# We only want one owner/creator so jusst use the 1st contributor
125-
# which should be the contact in the JSON input
126-
owner = contributor if contributor.data_curation?
127-
role.administrator = true if contributor.data_curation? &&
128-
!contributor.present?
129-
role.save
130-
end
103+
def plan_exists?(json:)
104+
return false unless json.present? &&
105+
json[:dmp_id].present? &&
106+
json[:dmp_id][:identifier].present?
107+
108+
scheme = IdentifierScheme.by_name(json[:dmp_id][:type]).first
109+
Identifier.where(value: json[:dmp_id][:identifier], identifier_scheme: scheme).any?
110+
end
111+
112+
# Get the Plan's owner
113+
def determine_owner(client:, plan:)
114+
contact = plan.contributors.select(&:data_curation?).first
115+
# Use the contact if it was sent in and has an affiliation defined
116+
return contact if contact.present? && contact.org.present?
117+
118+
# If the contact has no affiliation defined, see if they are already a User
119+
user = lookup_user(contributor: contact)
120+
return user if user.present?
121+
122+
# Otherwise just return the client
123+
client
131124
end
132125

133-
# rubocop:disable Metrics/AbcSize
134-
def contributor_to_user(contributor:)
126+
def lookup_user(contributor:)
127+
return nil unless contributor.present?
128+
135129
identifiers = contributor.identifiers.map do |id|
136130
{ name: id.identifier_scheme&.name, value: id.value }
137131
end
138132
user = User.from_identifiers(array: identifiers) if identifiers.any?
139133
user = User.find_by(email: contributor.email) unless user.present?
140-
return user if user.present?
134+
user
135+
end
136+
137+
def invite_contributor(contributor:)
138+
return nil unless contributor.present?
141139

142140
# If the user was not found, invite them and attach any know identifiers
143141
names = contributor.name&.split || [""]
@@ -155,7 +153,6 @@ def contributor_to_user(contributor:)
155153
end
156154
user
157155
end
158-
# rubocop:enable Metrics/AbcSize
159156

160157
end
161158

app/controllers/contacts_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class ContactUs::ContactsController < ApplicationController
55
def create
66
@contact = ContactUs::Contact.new(params[:contact_us_contact])
77

8-
unless user_signed_in?
8+
if !user_signed_in? && Rails.configuration.x.recaptcha.enabled
99
unless verify_recaptcha(model: @contact) && @contact.save
1010
flash[:alert] = _("Captcha verification failed, please retry.")
1111
render_new_page and return

app/controllers/orgs_controller.rb

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ def admin_update
3333
authorize @org
3434
@org.logo = attrs[:logo] if attrs[:logo]
3535
tab = (attrs[:feedback_enabled].present? ? "feedback" : "profile")
36-
if attrs[:org_links].present?
37-
@org.links = ActiveSupport::JSON.decode(attrs[:org_links])
38-
attrs.delete(:org_links)
39-
end
36+
@org.links = ActiveSupport::JSON.decode(params[:org_links]) if params[:org_links].present?
4037

4138
# Only allow super admins to change the org types and shib info
4239
if current_user.can_super_admin?
@@ -105,6 +102,7 @@ def shibboleth_ds
105102
# Display the custom Shibboleth discovery service page.
106103
@orgs = Identifier.by_scheme_name("shibboleth", "Org")
107104
.sort { |a, b| a.identifiable.name <=> b.identifiable.name }
105+
.map(&:identifiable)
108106

109107
# Disabling the rubocop check here because it would not be clear what happens
110108
# if the ``@orgs` array has items ... it renders the shibboleth_ds view
@@ -120,10 +118,10 @@ def shibboleth_ds
120118
# POST /orgs/shibboleth_ds
121119
# rubocop:disable Metrics/AbcSize
122120
def shibboleth_ds_passthru
123-
if !shib_params["shib-ds"][:org_name].blank?
124-
session["org_id"] = shib_params["shib-ds"][:org_name]
121+
if !shib_params[:org_id].blank?
122+
session["org_id"] = shib_params[:org_id]
125123

126-
org = Org.where(id: shib_params["shib-ds"][:org_id])
124+
org = Org.where(id: shib_params[:org_id])
127125
shib_entity = Identifier.by_scheme_name("shibboleth", "Org")
128126
.where(identifiable: org)
129127

@@ -211,14 +209,15 @@ def search
211209
def org_params
212210
params.require(:org)
213211
.permit(:name, :abbreviation, :logo, :contact_email, :contact_name,
214-
:remove_logo, :org_type, :managed, :feedback_enabled, :org_links,
212+
:remove_logo, :managed, :feedback_enabled, :org_links,
213+
:funder, :institution, :organisation,
215214
:feedback_email_msg, :org_id, :org_name, :org_crosswalk,
216215
identifiers_attributes: %i[identifier_scheme_id value],
217-
tracker_attributes: %i[code])
216+
tracker_attributes: %i[code id])
218217
end
219218

220219
def shib_params
221-
params.permit("shib-ds": %i[org_id org_name])
220+
params.permit("org_id")
222221
end
223222

224223
def search_params

app/controllers/plan_exports_controller.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def show
4545
format.text { show_text }
4646
format.docx { show_docx }
4747
format.pdf { show_pdf }
48+
format.json { show_json }
4849
end
4950
end
5051
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
@@ -91,6 +92,11 @@ def show_pdf
9192
}
9293
end
9394

95+
def show_json
96+
json = render_to_string(partial: "/api/v1/plans/show", locals: { plan: @plan })
97+
render json: "{\"dmp\":#{json}}"
98+
end
99+
94100
def file_name
95101
# Sanitize bad characters and replace spaces with underscores
96102
ret = @plan.title

0 commit comments

Comments
 (0)