diff --git a/Capfile b/Capfile
new file mode 100644
index 0000000000..af3b01630b
--- /dev/null
+++ b/Capfile
@@ -0,0 +1,20 @@
+# Load DSL and set up stages
+require "capistrano/setup"
+
+# Include default deployment tasks
+require "capistrano/deploy"
+
+# Load the SCM plugin appropriate to your project:
+require "capistrano/scm/git"
+install_plugin Capistrano::SCM::Git
+
+require 'capistrano/bundler'
+
+# TODO: Uncomment this for deployments to dmp-dev or roadmap-stg and permenantly once we have
+# merged the latest changes into dmptool stage and prod
+require "capistrano/rails/assets"
+
+require 'capistrano/rails/migrations'
+
+# Load custom tasks from `lib/capistrano/tasks` if you have any defined
+Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
diff --git a/Gemfile b/Gemfile
index c38f3a255c..2042606f8d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -53,7 +53,9 @@ group :puma do
end
# Bit fields for ActiveRecord (https://github.com/pboling/flag_shih_tzu)
-gem 'flag_shih_tzu' # Allows for bitfields in activereccord
+gem 'flag_shih_tzu', '~> 0.3.23' # Allows for bitfields in activereccord
+# Pinned here because we're using a private method in Role.rb
+# if this gets updated, check this method still exists
# ------------------------------------------------
# JSON DSL - USED BY API
@@ -110,8 +112,16 @@ gem 'recaptcha'
# Ideal gem for handling attachments in Rails, Sinatra and Rack applications. (http://github.com/markevans/dragonfly)
gem 'dragonfly'
-group :aws, optional: true do
-
+# ------------------------------------------------
+# Start DMPTool customization
+# For some reason capistrano's `set :bundle_with` is not working
+# so we remove the optional flag to ensure this gem gets installed
+# ------------------------------------------------
+#group :aws, optional: true do
+group :aws do
+# ------------------------------------------------
+# End DMPTool customization
+# ------------------------------------------------
gem 'dragonfly-s3_data_store'
end
@@ -143,17 +153,28 @@ gem 'mini_racer'
# ------------------------------------------------
# EXPORTING
# Provides binaries for WKHTMLTOPDF project in an easily accessible package.
-gem 'wkhtmltopdf-binary'
+# ------------------------------------------------
+# Start DMPTool customization
+# 0.12.5 does not work on our new linux2 instances. Pegging at 0.12.4 for now
+# ------------------------------------------------
+#gem 'wkhtmltopdf-binary'
+gem 'wkhtmltopdf-binary', '0.12.4'
+# ------------------------------------------------
+# End DMPTool customization
+# ------------------------------------------------
# PDF generator (from HTML) gem for Ruby on Rails (https://github.com/mileszs/wicked_pdf)
gem 'wicked_pdf', '~> 1.1.0'
# This simple gem allows you to create MS Word docx documents from simple html documents. This makes it easy to create dynamic reports and forms that can be downloaded by your users as simple MS Word docx files. (http://github.com/karnov/htmltoword)
-gem 'htmltoword'
+gem 'htmltoword', '1.1.0'
# A feed fetching and parsing library (http://feedjira.com)
gem 'feedjira'
+# Filename sanitization for Ruby. This is useful when you generate filenames for downloads from user input
+gem 'zaru'
+
# ------------------------------------------------
# INTERNATIONALIZATION
# Simple FastGettext Rails integration. (http://github.com/grosser/gettext_i18n_rails)
@@ -206,6 +227,9 @@ group :development, :test do
# Guard gem for RSpec (https://github.com/guard/guard-rspec)
gem "guard-rspec"
+ gem "capistrano"
+
+ gem "capistrano-rails"
end
group :test do
@@ -236,11 +260,7 @@ group :test do
# Automatically create snapshots when Cucumber steps fail with Capybara and Rails (http://github.com/mattheworiordan/capybara-screenshot)
gem "capybara-screenshot"
- # The next generation developer focused tool for automated testing of webapps (https://github.com/SeleniumHQ/selenium)
- gem "selenium-webdriver", "~> 3.14"
-
- # Easy installation and use of chromedriver. (https://github.com/flavorjones/chromedriver-helper)
- gem "chromedriver-helper", ">= 1.2.0"
+ gem 'webdrivers', '~> 3.0'
gem "rspec-collection_matchers"
diff --git a/Gemfile.lock b/Gemfile.lock
index 74545293dc..7898555ec3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -43,17 +43,17 @@ GEM
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
- annotate (2.7.5)
+ airbrussh (1.4.0)
+ sshkit (>= 1.6.1, != 1.7.0)
+ annotate (3.0.3)
activerecord (>= 3.2, < 7.0)
- rake (>= 10.4, < 13.0)
+ rake (>= 10.4, < 14.0)
annotate_gem (0.0.14)
bundler (>= 1.1)
api-pagination (4.8.2)
- archive-zip (0.12.0)
- io-like (~> 0.3.0)
arel (6.0.4)
ast (2.4.0)
- autoprefixer-rails (9.6.1.1)
+ autoprefixer-rails (9.7.3)
execjs
bcrypt (3.1.13)
better_errors (2.5.1)
@@ -65,7 +65,7 @@ GEM
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
- brakeman (4.6.1)
+ brakeman (4.7.2)
builder (3.2.3)
bullet (6.0.2)
activesupport (>= 3.0.0)
@@ -76,6 +76,16 @@ GEM
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
byebug (11.0.1)
+ capistrano (3.11.2)
+ airbrussh (>= 1.0.0)
+ i18n
+ rake (>= 10.0.0)
+ sshkit (>= 1.9.0)
+ capistrano-bundler (1.6.0)
+ capistrano (~> 3.1)
+ capistrano-rails (1.4.0)
+ capistrano (~> 3.1)
+ capistrano-bundler (~> 1.1)
capybara (3.29.0)
addressable
mini_mime (>= 0.1.3)
@@ -84,21 +94,17 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (~> 1.5)
xpath (~> 3.2)
- capybara-screenshot (1.0.23)
+ capybara-screenshot (1.0.24)
capybara (>= 1.0, < 4)
launchy
- childprocess (2.0.0)
- rake (< 13.0)
- chromedriver-helper (2.1.1)
- archive-zip (~> 0.10)
- nokogiri (~> 1.8)
+ childprocess (3.0.0)
coderay (1.1.2)
concurrent-ruby (1.1.5)
contact_us (1.2.0)
rails (>= 4.2.0)
crack (0.4.3)
safe_yaml (~> 1.0.0)
- crass (1.0.4)
+ crass (1.0.5)
daemons (1.3.1)
database_cleaner (1.7.0)
debug_inspector (0.0.3)
@@ -127,22 +133,22 @@ GEM
erubi (1.9.0)
erubis (2.7.0)
eventmachine (1.2.7)
- excon (0.67.0)
+ excon (0.70.0)
execjs (2.7.0)
- factory_bot (5.1.0)
+ factory_bot (5.1.1)
activesupport (>= 4.2.0)
- factory_bot_rails (5.1.0)
+ factory_bot_rails (5.1.1)
factory_bot (~> 5.1.0)
railties (>= 4.2.0)
faker (2.2.1)
i18n (>= 0.8)
- faraday (0.15.4)
+ faraday (0.17.1)
multipart-post (>= 1.2, < 3)
fast_gettext (2.0.1)
- feedjira (3.0.0)
- loofah (>= 2.2.1)
+ feedjira (3.1.0)
+ loofah (>= 2.3.1)
sax-machine (>= 1.0)
- ffi (1.11.1)
+ ffi (1.11.3)
flag_shih_tzu (0.3.23)
fog-aws (3.5.2)
fog-core (~> 2.1)
@@ -163,7 +169,7 @@ GEM
font-awesome-sass (4.2.2)
sass (~> 3.2)
formatador (0.2.5)
- fuubar (2.4.1)
+ fuubar (2.5.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
gettext (3.2.9)
@@ -178,7 +184,7 @@ GEM
rails (>= 3.2.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
- guard (2.15.1)
+ guard (2.16.1)
formatador (>= 0.2.4)
listen (>= 2.7, < 4.0)
lumberjack (>= 1.0.12, < 2.0)
@@ -194,16 +200,15 @@ GEM
rspec (>= 2.99.0, < 4.0)
hashdiff (1.0.0)
hashie (3.6.0)
- highline (2.0.2)
+ highline (2.0.3)
htmltoword (1.1.0)
actionpack
nokogiri
rubyzip (>= 1.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
- io-like (0.3.0)
ipaddress (0.8.3)
- jaro_winkler (1.5.3)
+ jaro_winkler (1.5.4)
jbuilder (2.6.4)
activesupport (>= 3.0.0)
multi_json (>= 1.2)
@@ -226,42 +231,42 @@ GEM
ledermann-rails-settings (2.5.0)
activerecord (>= 4.2)
libv8 (7.3.492.27.1)
- listen (3.1.5)
- rb-fsevent (~> 0.9, >= 0.9.4)
- rb-inotify (~> 0.9, >= 0.9.7)
- ruby_dep (~> 1.2)
+ listen (3.2.1)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
locale (2.1.2)
- loofah (2.2.3)
+ loofah (2.4.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lumberjack (1.0.13)
mail (2.7.1)
mini_mime (>= 0.1.1)
- metaclass (0.0.4)
method_source (0.9.2)
mime-types (3.3)
mime-types-data (~> 3.2015)
- mime-types-data (3.2019.0904)
+ mime-types-data (3.2019.1009)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
- mini_racer (0.2.6)
+ mini_racer (0.2.8)
libv8 (>= 6.9.411)
- minitest (5.12.0)
- mocha (1.9.0)
- metaclass (~> 0.0.1)
- multi_json (1.13.1)
+ minitest (5.13.0)
+ mocha (1.10.1)
+ multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
mysql2 (0.4.10)
nenv (0.3.0)
+ net-scp (2.0.0)
+ net-ssh (>= 2.6.5, < 6.0.0)
+ net-ssh (5.2.0)
nio4r (2.5.2)
- nokogiri (1.10.4)
+ nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
notiffany (0.1.3)
nenv (~> 0.1)
shellany (~> 0.0)
- oauth2 (1.4.1)
- faraday (>= 0.8, < 0.16.0)
+ oauth2 (1.4.2)
+ faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
@@ -282,25 +287,25 @@ GEM
omniauth (>= 1.0.0)
options (2.3.2)
orm_adapter (0.5.0)
- parallel (1.17.0)
- parser (2.6.4.1)
+ parallel (1.19.1)
+ parser (2.6.5.0)
ast (~> 2.4.0)
pg (0.19.0)
po_to_json (1.0.1)
json (>= 1.6.0)
- progress_bar (1.3.0)
+ progress_bar (1.3.1)
highline (>= 1.6, < 3)
options (~> 2.3.0)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (4.0.1)
- puma (4.2.0)
+ puma (4.3.1)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
rack (1.6.11)
- rack-mini-profiler (1.0.2)
+ rack-mini-profiler (1.1.3)
rack (>= 1.2.0)
rack-proxy (0.6.5)
rack
@@ -323,8 +328,8 @@ GEM
activesupport (>= 4.2.0, < 5.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
- rails-html-sanitizer (1.2.0)
- loofah (~> 2.2, >= 2.2.2)
+ rails-html-sanitizer (1.3.0)
+ loofah (~> 2.3)
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
@@ -336,65 +341,64 @@ GEM
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (3.0.0)
- rake (12.3.3)
+ rake (13.0.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
- recaptcha (5.1.0)
+ recaptcha (5.2.1)
json
regexp_parser (1.6.0)
responders (2.4.1)
actionpack (>= 4.2.0, < 6.0)
railties (>= 4.2.0, < 6.0)
rollbar (2.22.1)
- rspec (3.8.0)
- rspec-core (~> 3.8.0)
- rspec-expectations (~> 3.8.0)
- rspec-mocks (~> 3.8.0)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
rspec-collection_matchers (1.2.0)
rspec-expectations (>= 2.99.0.beta1)
- rspec-core (3.8.2)
- rspec-support (~> 3.8.0)
- rspec-expectations (3.8.4)
+ rspec-core (3.9.0)
+ rspec-support (~> 3.9.0)
+ rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-mocks (3.8.1)
+ rspec-support (~> 3.9.0)
+ rspec-mocks (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-rails (3.8.2)
+ rspec-support (~> 3.9.0)
+ rspec-rails (3.9.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
- rspec-core (~> 3.8.0)
- rspec-expectations (~> 3.8.0)
- rspec-mocks (~> 3.8.0)
- rspec-support (~> 3.8.0)
- rspec-support (3.8.2)
- rubocop (0.74.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.0)
+ rubocop (0.77.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
- rubocop-dmp_roadmap (1.1.0)
+ rubocop-dmp_roadmap (1.1.2)
rubocop (>= 0.58.2)
rubocop-rails_config (>= 0.2.2)
rubocop-rspec (>= 1.27.0)
- rubocop-performance (1.4.1)
+ rubocop-performance (1.5.1)
rubocop (>= 0.71.0)
- rubocop-rails (2.3.2)
+ rubocop-rails (2.4.0)
rack (>= 1.1)
rubocop (>= 0.72.0)
- rubocop-rails_config (0.7.2)
+ rubocop-rails_config (0.9.0)
railties (>= 3.0)
- rubocop (~> 0.74)
+ rubocop (~> 0.77)
rubocop-performance (~> 1.3)
rubocop-rails (~> 2.0)
- rubocop-rspec (1.35.0)
- rubocop (>= 0.60.0)
+ rubocop-rspec (1.37.0)
+ rubocop (>= 0.68.1)
ruby-progressbar (1.10.1)
- ruby_dep (1.5.0)
ruby_dig (0.0.2)
rubyzip (1.3.0)
safe_yaml (1.0.5)
@@ -414,9 +418,9 @@ GEM
sprockets-rails
tilt
sax-machine (1.3.2)
- selenium-webdriver (3.142.4)
- childprocess (>= 0.5, < 3.0)
- rubyzip (~> 1.2, >= 1.2.2)
+ selenium-webdriver (3.142.6)
+ childprocess (>= 0.5, < 4.0)
+ rubyzip (>= 1.2.2)
shellany (0.0.1)
shoulda (3.6.0)
shoulda-context (~> 1.0, >= 1.0.1)
@@ -439,6 +443,9 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
+ sshkit (1.20.0)
+ net-scp (>= 1.1.2)
+ net-ssh (>= 2.8.0)
text (1.3.1)
thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
@@ -451,14 +458,18 @@ GEM
tzinfo (1.2.5)
thread_safe (~> 0.1)
unicode-display_width (1.6.0)
- uniform_notifier (1.12.1)
+ uniform_notifier (1.13.0)
warden (1.2.7)
rack (>= 1.0)
web-console (3.3.0)
activemodel (>= 4.2)
debug_inspector
railties (>= 4.2)
- webmock (3.7.5)
+ webdrivers (3.9.4)
+ nokogiri (~> 1.6)
+ rubyzip (~> 1.0)
+ selenium-webdriver (~> 3.0)
+ webmock (3.7.6)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -474,6 +485,7 @@ GEM
yard-tomdoc (0.7.1)
tomparse (>= 0.4.0)
yard
+ zaru (0.3.0)
PLATFORMS
ruby
@@ -491,9 +503,10 @@ DEPENDENCIES
bullet
bundle-audit
byebug
+ capistrano
+ capistrano-rails
capybara
capybara-screenshot
- chromedriver-helper (>= 1.2.0)
contact_us
database_cleaner
devise (>= 4.7.1)
@@ -504,7 +517,7 @@ DEPENDENCIES
factory_bot_rails
faker
feedjira
- flag_shih_tzu
+ flag_shih_tzu (~> 0.3.23)
font-awesome-sass (~> 4.2.0)
fuubar
gettext
@@ -512,7 +525,7 @@ DEPENDENCIES
gettext_i18n_rails_js
guard
guard-rspec
- htmltoword
+ htmltoword (= 1.1.0)
jbuilder (~> 2.6.0)
kaminari
ledermann-rails-settings
@@ -541,7 +554,6 @@ DEPENDENCIES
ruby_dig
sass-rails
sassc-rails
- selenium-webdriver (~> 3.14)
shoulda
simplecov
spring
@@ -549,15 +561,17 @@ DEPENDENCIES
text
thin
web-console
+ webdrivers (~> 3.0)
webmock
webpacker (~> 3.5)
wicked_pdf (~> 1.1.0)
- wkhtmltopdf-binary
+ wkhtmltopdf-binary (= 0.12.4)
yard
yard-tomdoc
+ zaru
RUBY VERSION
ruby 2.4.0p0
BUNDLED WITH
- 1.17.1
+ 1.17.2
diff --git a/app/assets/stylesheets/blocks/_accessibility.scss b/app/assets/stylesheets/blocks/_accessibility.scss
index 3dde3a0a80..67caed1e1b 100644
--- a/app/assets/stylesheets/blocks/_accessibility.scss
+++ b/app/assets/stylesheets/blocks/_accessibility.scss
@@ -1,3 +1,8 @@
+/* Setting font-family for improving screen legibility */
+body, .tooltip, .popover {
+ font-family: $font-family;
+}
+
/* Text meant only for screen readers. */
.screen-reader-text {
border: 0;
@@ -28,3 +33,36 @@ div.skip a:focus {
height:auto;
overflow:visible !important;
}
+
+/** Focus outline required for accessibility */
+a, input, select, .form-control {
+ &:focus,
+ &:hover,
+ &:active {
+ outline-style: solid !important;
+ outline-color: $color-focus-outline !important;
+ outline-width: 2px !important;
+ }
+}
+
+ /** button outline-width increased for better visibility of outline */
+ button {
+
+ &:focus,
+ &:hover,
+ &:active {
+ outline-style: solid !important;
+ outline-color: $color-focus-outline !important;
+ outline-width: 3px !important;
+ }
+ }
+
+ td {
+ &:focus-within,
+ &:focus,
+ &:hover {
+ outline-style: solid !important;
+ outline-color: $color-focus-outline !important;
+ outline-width: 2px !important;
+ }
+ }
diff --git a/app/assets/stylesheets/blocks/_new_window_popup.scss b/app/assets/stylesheets/blocks/_new_window_popup.scss
new file mode 100644
index 0000000000..a499e26a67
--- /dev/null
+++ b/app/assets/stylesheets/blocks/_new_window_popup.scss
@@ -0,0 +1,35 @@
+a.has-new-window-popup-info,
+button.has-new-window-popup-info {
+
+ position:relative;
+ z-index:24;
+
+ & > span.new-window-popup-info {
+ position: absolute;
+ left: -9000px;
+ width: 0;
+ overflow: hidden;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
+
+ z-index:25;
+
+ & > span.new-window-popup-info {
+ display:block;
+ position:absolute;
+ top:2em;
+ left:-1em;
+ width:12em;
+ border-radius: 5px;
+ border:1px solid #0cf;
+ background-color:#cff;
+ color:#000;
+ text-align: center;
+ }
+
+ }
+
+}
diff --git a/app/assets/stylesheets/blocks/_tinymce_content.scss b/app/assets/stylesheets/blocks/_tinymce_content.scss
new file mode 100644
index 0000000000..895c5a0eec
--- /dev/null
+++ b/app/assets/stylesheets/blocks/_tinymce_content.scss
@@ -0,0 +1,8 @@
+@import "../variables/tinymce";
+
+.mce-content-body {
+
+ a {
+ color: $color-link-text;
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/dmptool/blocks/_new_window_popup.scss b/app/assets/stylesheets/dmptool/blocks/_new_window_popup.scss
new file mode 100644
index 0000000000..4adcdf8ea7
--- /dev/null
+++ b/app/assets/stylesheets/dmptool/blocks/_new_window_popup.scss
@@ -0,0 +1,23 @@
+a.has-new-window-popup-info,
+button.has-new-window-popup-info {
+
+ & > span.new-window-popup-info {
+ left: auto;
+ right: 10px;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
+
+ & > span.new-window-popup-info {
+ border:1px solid $color-dark-blue;
+ background-color: $color-light-blue;
+ color: $color-dark-blue;
+ left: auto;
+ right: 10px;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/variables/_colours.scss b/app/assets/stylesheets/variables/_colours.scss
index 1c2842d441..6f173d8882 100644
--- a/app/assets/stylesheets/variables/_colours.scss
+++ b/app/assets/stylesheets/variables/_colours.scss
@@ -12,6 +12,7 @@ $color-grey-darker: #333;
$color-grey-light: #777;
$color-grey-lighter: #999;
$color-blue: #337ab7;
+$color-blue-alice-darkest: #107896;
$color-muted: #CCC;
$color-dark-blue: #0E5682;
@@ -74,3 +75,6 @@ $color-shadow-dark: $color-black;
// FontAwesome Colors
$color-fa: $color-grey;
+
+// Focus colors
+$color-focus-outline: $color-blue-alice-darkest;
\ No newline at end of file
diff --git a/app/assets/stylesheets/variables/_tinymce.scss b/app/assets/stylesheets/variables/_tinymce.scss
index d6fd4d7abf..a7c70a7715 100644
--- a/app/assets/stylesheets/variables/_tinymce.scss
+++ b/app/assets/stylesheets/variables/_tinymce.scss
@@ -1,2 +1,3 @@
// Import the desired TinyMCE skin name
//@import "lightgray/skin.min";
+@import "./colours";
\ No newline at end of file
diff --git a/app/assets/stylesheets/variables/_typography.scss b/app/assets/stylesheets/variables/_typography.scss
index 48a14fa833..7cc3a8faf2 100644
--- a/app/assets/stylesheets/variables/_typography.scss
+++ b/app/assets/stylesheets/variables/_typography.scss
@@ -3,3 +3,6 @@
src: font-url('GillSansLight.ttf') format('truetype');
font-weight: normal;
}
+
+// Chosen for Accessibility
+$font-family: Verdana, Trebuchet, "Helvetica Neue", Helvetica, Arial, sans-serif;
diff --git a/app/assets/xslt/htmltoword/functions.xslt b/app/assets/xslt/htmltoword/functions.xslt
new file mode 100644
index 0000000000..4624cd0e1c
--- /dev/null
+++ b/app/assets/xslt/htmltoword/functions.xslt
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rId
+
diff --git a/app/assets/xslt/htmltoword/numbering.xslt b/app/assets/xslt/htmltoword/numbering.xslt
new file mode 100644
index 0000000000..71cf55bc48
--- /dev/null
+++ b/app/assets/xslt/htmltoword/numbering.xslt
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lowerLetter
+ upperLetter
+ lowerRoman
+ upperRoman
+ none
+ decimal
+ bullet,●
+ bullet,o
+ bullet,■
+ none
+
+
+
+
+ decimal
+ bullet,●
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/controllers/api/v0/plans_controller.rb b/app/controllers/api/v0/plans_controller.rb
index 3863292234..783002dfb3 100644
--- a/app/controllers/api/v0/plans_controller.rb
+++ b/app/controllers/api/v0/plans_controller.rb
@@ -61,10 +61,46 @@ def index
if params[:per_page].present? && params[:per_page].to_i > Rails.configuration.branding[:application][:api_max_page_size]
params[:per_page] = Rails.configuration.branding[:application][:api_max_page_size]
end
- @plans = paginate @user.org.plans.includes( [ {answers: :question_options} ,
- template: [ { phases: { sections: { questions: :question_format } } }, :org]
+ @plans = @user.org.plans.includes( [ {roles: :user}, {answers: :question_options} ,
+ template: [ { phases: { sections: { questions: [:question_format, :themes ] } } }, :org]
])
+
+ # Filter on list of users
+ user_ids = extract_param_list(params, "user")
+ @plans = @plans.where(roles: {user_id: user_ids, access: Role.bit_values(:editor)}) if user_ids.present?
+ # filter on dates
+ if params["created_after"].present? || params["created_before"].present?
+ @plans = @plans.where(created_at: dates_to_range(params,"created_after","created_before"))
+ end
+ if params["updated_after"].present? || params["updated_before"].present?
+ @plans = @plans.where(updated_at: dates_to_range(params,"updated_after","updated_before"))
+ end
+ # filter on funder (dmptemplate_id)
+ template_ids = extract_param_list(params, "template")
+ @plans = @plans.where(templates: {family_id: template_ids}) if template_ids.present?
+ # filter on id(s)
+ plan_ids = extract_param_list(params, "plan")
+ @plans = @plans.where(id: plan_ids ) if plan_ids.present?
+ # apply pagination after filtering
+ @plans = paginate @plans
respond_with @plans
end
+ private
+
+ def extract_param_list(params, attribute)
+ list = params.fetch(attribute+"[]", [])
+ val = params.fetch(attribute, [])
+ list << val if val.present?
+ list
+ end
+
+ # takes in the params hash and converts to a date-range
+ def dates_to_range(hash,start,stop)
+ today = Date.today
+ start_date = Date.parse(hash.fetch(start, today.prev_month.to_date.to_s))
+ end_date = Date.parse(hash.fetch(stop, today.to_date.to_s)) + 1.day
+ start_date..end_date
+ end
+
end
diff --git a/app/controllers/api/v0/templates_controller.rb b/app/controllers/api/v0/templates_controller.rb
index e38bbe9b2f..d210159253 100644
--- a/app/controllers/api/v0/templates_controller.rb
+++ b/app/controllers/api/v0/templates_controller.rb
@@ -25,7 +25,7 @@ def index
.where(org_id: @user.org_id, published: true)
.where.not(customization_of: nil)
- Template.published.order(:org_id, :version).each do |temp|
+ published_templates.order(:org_id, :version).each do |temp|
if @org_templates[temp.org].present?
if @org_templates[temp.org][:own][temp.family_id].nil?
@org_templates[temp.org][:own][temp.family_id] = temp
@@ -37,7 +37,7 @@ def index
@org_templates[temp.org][:own][temp.family_id] = temp
end
end
- Template.published_customization(@user.org_id).each do |temp|
+ customized_templates.each do |temp|
if @org_templates[temp.org].present?
if @org_templates[temp.org][:cust][temp.family_id].nil?
@org_templates[temp.org][:cust][temp.family_id] = temp
diff --git a/app/controllers/feedback_requests_controller.rb b/app/controllers/feedback_requests_controller.rb
index 241c6ad38e..af26af1cea 100644
--- a/app/controllers/feedback_requests_controller.rb
+++ b/app/controllers/feedback_requests_controller.rb
@@ -14,12 +14,12 @@ def create
authorize @plan, :request_feedback?
begin
if @plan.request_feedback(current_user)
- redirect_to share_plan_path(@plan), notice: _(request_feedback_flash_notice)
+ redirect_to request_feedback_plan_path(@plan), notice: _(request_feedback_flash_notice)
else
- redirect_to share_plan_path(@plan), alert: ALERT
+ redirect_to request_feedback_plan_path(@plan), alert: ALERT
end
rescue Exception
- redirect_to share_plan_path(@plan), alert: ERROR
+ redirect_to request_feedback_plan_path(@plan), alert: ERROR
end
end
diff --git a/app/controllers/org_admin/templates_controller.rb b/app/controllers/org_admin/templates_controller.rb
index 4c69d0be6c..fc9fd960d4 100644
--- a/app/controllers/org_admin/templates_controller.rb
+++ b/app/controllers/org_admin/templates_controller.rb
@@ -174,7 +174,11 @@ def update
template = Template.find(params[:id])
authorize template
begin
- template.assign_attributes(template_params)
+ args = template_params
+ # Swap in the appropriate visibility enum value for the checkbox value
+ args[:visibility] = args.fetch(:visibility, '0') == '1' ? 'organisationally_visible' : 'publicly_visible'
+
+ template.assign_attributes(args)
if params["template-links"].present?
template.links = ActiveSupport::JSON.decode(params["template-links"])
end
@@ -282,6 +286,60 @@ def unpublish
redirect_to request.referrer.present? ? request.referrer : org_admin_templates_path
end
+ # GET template_export/:id
+ # -----------------------------------------------------
+ def template_export
+ @template = Template.find(params[:id])
+
+ authorize @template
+ # now with prefetching (if guidance is added, prefetch annottaions/guidance)
+ @template = Template.includes(
+ :org,
+ phases: {
+ sections: {
+ questions: [
+ :question_options,
+ :question_format,
+ :annotations
+ ]
+ }
+ }
+ ).find(@template.id)
+
+ @formatting = Settings::Template::DEFAULT_SETTINGS[:formatting]
+
+ begin
+ file_name = @template.title.gsub(/[^a-zA-Z\d\s]/, "").gsub(/ /, "_") + '_v' + @template.version.to_s
+ respond_to do |format|
+ format.docx do
+ render docx: "template_exports/template_export", filename: "#{file_name}.docx"
+ end
+
+ format.pdf do
+ # rubocop:disable LineLength
+ render pdf: file_name,
+ template: "template_exports/template_export",
+ margin: @formatting[:margin],
+ footer: {
+ center: _("Template created using the %{application_name} service. Last modified %{date}") % {
+ application_name: Rails.configuration.branding[:application][:name],
+ date: l(@template.updated_at.to_date, formats: :short)
+ },
+ font_size: 8,
+ spacing: (@formatting[:margin][:bottom] / 2) - 4,
+ right: "[page] of [topage]",
+ encoding: "utf8"
+ }
+ # rubocop:enable LineLength
+ end
+ end
+ rescue ActiveRecord::RecordInvalid => e
+ # What scenario is this triggered in? it's common to our export pages
+ redirect_to public_templates_path,
+ alert: _("Unable to download the DMP Template at this time.")
+ end
+ end
+
private
def template_params
diff --git a/app/controllers/org_admin/users_controller.rb b/app/controllers/org_admin/users_controller.rb
new file mode 100644
index 0000000000..e2c730ce6f
--- /dev/null
+++ b/app/controllers/org_admin/users_controller.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module OrgAdmin
+
+ class UsersController < ApplicationController
+
+ after_action :verify_authorized
+
+ def edit
+ @user = User.find(params[:id])
+ authorize @user
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
+ render "org_admin/users/edit",
+ locals: { user: @user,
+ departments: @departments,
+ plans: @plans,
+ languages: @languages,
+ orgs: @orgs,
+ identifier_schemes: @identifier_schemes,
+ default_org: @user.org }
+ end
+
+ def update
+ @user = User.find(params[:id])
+ authorize @user
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
+ # Replace the 'your' word from the canned responses so that it does
+ # not read 'Successfully updated your profile for John Doe'
+ topic = _("profile for %{username}") % { username: @user.name(false) }
+ if @user.update_attributes(user_params)
+ flash.now[:notice] = success_message(@user, _("updated"))
+ else
+ flash.now[:alert] = failure_message(@user, _("update"))
+ end
+ render :edit
+ end
+
+ def user_plans
+ @user = User.find(params[:id])
+ authorize @user
+ @plans = Plan.active(@user).page(1)
+ render "org_admin/users/plans"
+ end
+
+
+ private
+ def user_params
+ params.require(:user).permit(:department_id)
+ end
+
+ end
+
+end
diff --git a/app/controllers/orgs_controller.rb b/app/controllers/orgs_controller.rb
index 2e753e1087..e8693bf2f0 100644
--- a/app/controllers/orgs_controller.rb
+++ b/app/controllers/orgs_controller.rb
@@ -33,25 +33,22 @@ def admin_update
# Only allow super admins to change the org types and shib info
if current_user.can_super_admin?
# Handle Shibboleth identifiers if that is enabled
- if Rails.application.config.shibboleth_use_filtered_discovery_service &&
- params[:shib_id].present?
+ if Rails.application.config.shibboleth_use_filtered_discovery_service
shib = IdentifierScheme.find_by(name: "shibboleth")
shib_settings = @org.org_identifiers.select do |ids|
ids.identifier_scheme == shib
end.first
- if !params[:shib_id].blank?
+ if params[:shib_id].blank? && shib_settings.present?
+ # The user cleared the shib values so delete the object
+ shib_settings.destroy
+ else
unless shib_settings.present?
shib_settings = OrgIdentifier.new(org: @org, identifier_scheme: shib)
- shib_settings.identifier = params[:shib_id]
- shib_settings.attrs = { domain: params[:shib_domain] }
- shib_settings.save
- end
- else
- if shib_settings.present?
- # The user cleared the shib values so delete the object
- shib_settings.destroy
end
+ shib_settings.identifier = params[:shib_id]
+ shib_settings.attrs = { domain: params[:shib_domain] }
+ shib_settings.save
end
end
end
diff --git a/app/controllers/paginable/plans_controller.rb b/app/controllers/paginable/plans_controller.rb
index 6e06b9bffc..b82e459c7a 100644
--- a/app/controllers/paginable/plans_controller.rb
+++ b/app/controllers/paginable/plans_controller.rb
@@ -32,7 +32,6 @@ def organisationally_or_publicly_visible
def publicly_visible
paginable_renderise(
partial: "publicly_visible",
- scope: Plan.publicly_visible.includes(:template)
scope: Plan.publicly_visible.includes(:template),
query_params: { sort_field: 'plans.updated_at', sort_direction: :desc }
)
@@ -50,4 +49,18 @@ def org_admin
)
end
+ # GET /paginable/plans/org_admin/:page
+ def org_admin_other_user
+ @user = User.find(params[:id])
+ authorize @user
+ unless current_user.present? && current_user.can_org_admin? && @user.present?
+ raise Pundit::NotAuthorizedError
+ end
+ paginable_renderise(
+ partial: "org_admin_other_user",
+ scope: Plan.organisationally_or_publicly_visible(@user),
+ query_params: { sort_field: 'plans.updated_at', sort_direction: :desc }
+ )
+ end
+
end
diff --git a/app/controllers/plan_exports_controller.rb b/app/controllers/plan_exports_controller.rb
index fc8d3e8b30..42c583934b 100644
--- a/app/controllers/plan_exports_controller.rb
+++ b/app/controllers/plan_exports_controller.rb
@@ -88,7 +88,15 @@ def show_pdf
end
def file_name
- @plan.title.gsub(/ /, "_")
+ # Sanitize bad characters and replace spaces with underscores
+ ret = @plan.title
+ Zaru.sanitize! ret
+ ret = ret.strip.gsub(/\s+/, "_")
+ # limit the filename length to 100 chars. Windows systems have a MAX_PATH allowance
+ # of 255 characters, so this should provide enough of the title to allow the user
+ # to understand which DMP it is and still allow for the file to be saved to a deeply
+ # nested directory
+ ret[0, 100]
end
def publicly_authorized?
diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb
index 17ab569c18..2a3fccff17 100644
--- a/app/controllers/plans_controller.rb
+++ b/app/controllers/plans_controller.rb
@@ -17,6 +17,10 @@ def index
@organisationally_or_publicly_visible =
Plan.organisationally_or_publicly_visible(current_user).page(1)
end
+
+ if params[:plan].present?
+ @template = Template.find(params[:plan][:template_id])
+ end
end
# GET /plans/new
@@ -264,6 +268,16 @@ def share
end
end
+ def request_feedback
+ @plan = Plan.find(params[:id])
+ if @plan.present?
+ authorize @plan
+ @plan_roles = @plan.roles
+ else
+ redirect_to(plans_path)
+ end
+ end
+
def destroy
@plan = Plan.find(params[:id])
authorize @plan
diff --git a/app/controllers/public_pages_controller.rb b/app/controllers/public_pages_controller.rb
index 25fb04f05d..7d69fed5ab 100644
--- a/app/controllers/public_pages_controller.rb
+++ b/app/controllers/public_pages_controller.rb
@@ -2,8 +2,6 @@
class PublicPagesController < ApplicationController
- after_action :verify_authorized, except: [:template_index, :plan_index]
-
include Dmptool::Controller::PublicPages
# GET template_index
@@ -44,15 +42,16 @@ def template_export
@formatting = Settings::Template::DEFAULT_SETTINGS[:formatting]
begin
- file_name = @template.title.gsub(/[^a-zA-Z\d\s]/, "").gsub(/ /, "_")
+ file_name = @template.title.gsub(/[^a-zA-Z\d\s]/, "").gsub(/ /, "_") + '_v' + @template.version.to_s
respond_to do |format|
format.docx do
- render docx: "template_export", filename: "#{file_name}.docx"
+ render docx: "template_exports/template_export", filename: "#{file_name}.docx"
end
format.pdf do
# rubocop:disable LineLength
render pdf: file_name,
+ template: "template_exports/template_export",
margin: @formatting[:margin],
footer: {
center: _("Template created using the %{application_name} service. Last modified %{date}") % {
diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb
index bafc2d7f3f..46dd1eebb1 100644
--- a/app/controllers/roles_controller.rb
+++ b/app/controllers/roles_controller.rb
@@ -77,7 +77,7 @@ def update
# rubocop:disable LineLength
render json: {
code: 1,
- msg: _("Successfully changed the permissions for #{@role.user.email}. They have been notified via email.")
+ msg: _("Successfully changed the permissions for %{email}. They have been notified via email.") % { email: @role.user.email }
}
# rubocop:enable LineLength
else
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb
index 698b8fecbe..f68cc42998 100644
--- a/app/controllers/static_pages_controller.rb
+++ b/app/controllers/static_pages_controller.rb
@@ -6,7 +6,7 @@ class StaticPagesController < ApplicationController
def about_us
dcc_news_feed_url = "http://www.dcc.ac.uk/news/dmponline-0/feed"
- @dcc_news_feed = Feedjira::Feed.fetch_and_parse dcc_news_feed_url
+ #@dcc_news_feed = Feedjira::Feed.fetch_and_parse dcc_news_feed_url
respond_to do |format|
format.rss { redirect_to dcc_news_feed_url }
format.html
diff --git a/app/controllers/super_admin/users_controller.rb b/app/controllers/super_admin/users_controller.rb
index ab5d69cc48..a4d2a14ba0 100644
--- a/app/controllers/super_admin/users_controller.rb
+++ b/app/controllers/super_admin/users_controller.rb
@@ -9,8 +9,12 @@ class UsersController < ApplicationController
def edit
@user = User.find(params[:id])
authorize @user
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
render "super_admin/users/edit",
locals: { user: @user,
+ departments: @departments,
+ plans: @plans,
languages: @languages,
orgs: @orgs,
identifier_schemes: @identifier_schemes,
@@ -20,6 +24,8 @@ def edit
def update
@user = User.find(params[:id])
authorize @user
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
# Replace the 'your' word from the canned responses so that it does
# not read 'Successfully updated your profile for John Doe'
topic = _("profile for %{username}") % { username: @user.name(false) }
@@ -31,10 +37,63 @@ def update
render :edit
end
+ def merge
+ @user = User.find(params[:id])
+ authorize @user
+ remove = User.find(params[:merge_id])
+
+ topic = _("profile for %{remove} into %{keep}" % {
+ remove: remove.name(false), keep: @user.name(false)})
+ if @user.merge(remove)
+ flash.now[:notice] = success_message(@user, _("merged"))
+ else
+ flash.now[:alert] = failure_message(@user, _("merge"))
+ end
+ # After merge attempt get departments and plans
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
+ render :edit
+ end
+
+ def search
+ @user = User.find(params[:id])
+ @users = User.where('email LIKE ?', "%#{params[:email]}%")
+ authorize @users
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
+ # WHAT TO RETURN!?!?!
+ if @users.present? # found a user, or Users, submit for merge
+ render json: {
+ form: render_to_string(partial: 'super_admin/users/confirm_merge.html.erb'),
+ }
+ else # NO USER, re-render w/error?
+ flash.now[:alert] = "Unable to find user"
+ render :edit # re-do as responding w/ json
+ end
+ end
+
+ def archive
+ @user = User.find(params[:id])
+ authorize @user
+ @departments = @user.org.departments.order(:name)
+ @plans = Plan.active(@user).page(1)
+ if @user.archive
+ flash.now[:notice] = success_message(@user, _("archived"))
+ else
+ flash.now[:alert] = failure_message(@user, _("archive"))
+ end
+ render :edit
+ end
+
private
def user_params
- params.require(:user).permit(:email, :firstname, :surname, :org_id,
- :language_id, :other_organisation)
+ params.require(:user).permit(:email,
+ :firstname,
+ :surname,
+ :org_id,
+ :department_id,
+ :language_id,
+ :other_organisation)
end
end
diff --git a/app/helpers/template_helper.rb b/app/helpers/template_helper.rb
index 3905e98735..2a8392eddc 100644
--- a/app/helpers/template_helper.rb
+++ b/app/helpers/template_helper.rb
@@ -33,4 +33,22 @@ def links_to_a_elements(links, separator = ", ")
a.join(separator)
end
+ # Generate a direct plan creation link based on provided template
+ # @param template [Template] template used for plan creation
+ # @param hidden [Boolean] should the link be hidden?
+ # @param text [String] text for the link
+ # @param id [String] id for the link element
+ def direct_link(template, hidden = false, text = nil, id = nil)
+ params = { org_id: template.org.id, funder_id: '-1', template_id: template.id }
+ cls = text.nil? ? 'direct-link' : 'direct-link btn btn-default'
+ style = hidden ? 'display: none' : ''
+
+ link_to(plans_url(plan: params), method: :post, title: _('Create plan'), class: cls, id: id, style: style) do
+ if text.nil?
+ ''.html_safe
+ else
+ text.html_safe
+ end
+ end
+ end
end
diff --git a/app/javascript/dmptool/views/home/index.js b/app/javascript/dmptool/views/home/index.js
index 4eaa4ee445..4d39a0594d 100644
--- a/app/javascript/dmptool/views/home/index.js
+++ b/app/javascript/dmptool/views/home/index.js
@@ -1,4 +1,7 @@
/* eslint-env browser */ // This allows us to reference 'window' below
+
+import getConstant from '../../../constants';
+
$(() => {
// Rotate through the news items every 8 seconds
const articles = $('#home-news-array').val();
@@ -6,9 +9,9 @@ $(() => {
const news = JSON.parse(`${articles.replace(/\\"/g, '"').replace(/\\'/g, '\'')}`);
const updateNews = (item) => {
const text = $('#home-news-link');
- const sr = ' (new window)';
+ const span = `${getConstant('OPENS_IN_A_NEW_WINDOW_TEXT')}`;
text.hide();
- text.html(`${news[item].title}${sr}`);
+ text.html(`${news[item].title} ${span}`);
text.fadeIn(100);
};
const startNewsTimer = (item) => {
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 96e90920e1..988858c4a6 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -8,6 +8,7 @@ import '../utils/externalLink';
import '../utils/paginable';
import '../utils/panelHeading';
import '../utils/links';
+import '../utils/outOfFocus';
import '../utils/tabHelper';
import '../utils/tooltipHelper';
import '../utils/popoverHelper';
@@ -62,6 +63,7 @@ import '../views/usage/index';
import '../views/users/notification_preferences';
import '../views/users/admin_grant_permissions';
import '../views/super_admin/notifications/edit';
+import '../views/public_templates/show';
// ----------------------------------------
// START DMPTool customization
diff --git a/app/javascript/utils/externalLink.js b/app/javascript/utils/externalLink.js
index 9eb13059fa..128df44ef8 100644
--- a/app/javascript/utils/externalLink.js
+++ b/app/javascript/utils/externalLink.js
@@ -17,6 +17,30 @@ $(() => {
&& !(exceptions.ids.indexOf(link.attr('id')) >= 0)
) {
link.attr('target', '_blank');
+ link.addClass('has-new-window-popup-info');
+ // Add span as child of link.
+ link.append($(`${getConstant('OPENS_IN_A_NEW_WINDOW_TEXT')}`));
+ }
+ });
+
+ $('a[href^="http"]').each((index, value) => {
+ const link = $(value);
+ const protocol = new RegExp('^https?');
+ const regex = new RegExp(`^https?://${getConstant('HOST')}`);
+ const exceptions = {
+ ids: ['connect-orcid-button', 'view-all-templates'],
+ };
+ // Internal links are typically just the path, but also check for other domains
+ if (
+ !link.attr('target')
+ && protocol.test(link.attr('href'))
+ && !regex.test(link.attr('href'))
+ && !(exceptions.ids.indexOf(link.attr('id')) >= 0)
+ ) {
+ link.attr('target', '_blank');
+ link.addClass('has-new-window-popup-info');
+ // Add span as child of link.
+ link.append($(`${getConstant('OPENS_IN_A_NEW_WINDOW_TEXT')}`));
}
});
});
diff --git a/app/javascript/utils/outOfFocus.js b/app/javascript/utils/outOfFocus.js
new file mode 100644
index 0000000000..2d873e6a02
--- /dev/null
+++ b/app/javascript/utils/outOfFocus.js
@@ -0,0 +1,22 @@
+$(() => {
+ // This bit of code currently only closes dropdown
+ // menus in a table when the focus is no longer in the containing cell.
+
+ // List of focusable Elements on page
+ const focusables = $('td, .dropdown, .dropdown-toggle, a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex], [contenteditable]');
+
+ focusables.each((i, el) => {
+ const focusable = $(el); // JQuery object
+ // On entrying a new focusable element we respond to event
+ focusable.focusin(() => {
+ $('td').children('.dropdown.open').each((j, elj) => {
+ const td = $(elj).parent(); // DOM Element
+ const dropdownBtn = $(elj).find('.dropdown-toggle'); // JQuery object
+ // Close dropdown menu if the focus is not the table cell containing the dropdown
+ if (!($.contains(td.get(0), focusable.get(0)) || focusable.is(td))) {
+ dropdownBtn.dropdown('toggle');
+ }
+ });
+ });
+ });
+});
diff --git a/app/javascript/utils/tinymce.js b/app/javascript/utils/tinymce.js
index c064a52474..2e5a8e14c4 100644
--- a/app/javascript/utils/tinymce.js
+++ b/app/javascript/utils/tinymce.js
@@ -42,6 +42,7 @@ export const defaultOptions = {
// editorManager.baseURL is not resolved properly for IE since document.currentScript
// is not supported, see issue https://github.com/tinymce/tinymce/issues/358
skin_url: '/tinymce/skins/lightgray',
+ content_css: ['/assets/blocks/_tinymce_content.css'],
};
/*
This function is invoked anytime a new editor is initialised (e.g. Tinymce.init())
diff --git a/app/javascript/views/plans/index.js b/app/javascript/views/plans/index.js
index b030b67f4e..b3cd74c1eb 100644
--- a/app/javascript/views/plans/index.js
+++ b/app/javascript/views/plans/index.js
@@ -26,4 +26,6 @@ $(() => {
$(paginableSelector).on('ajax:error', '.set_test_plan', () => {
// TODO adequate error handling for network error
});
+
+ $('#create-modal').modal('show');
});
diff --git a/app/javascript/views/public_templates/show.js b/app/javascript/views/public_templates/show.js
new file mode 100644
index 0000000000..16bf07f7dc
--- /dev/null
+++ b/app/javascript/views/public_templates/show.js
@@ -0,0 +1,15 @@
+$(() => {
+ $('.copy-link').click((e) => {
+ const link = $(e.currentTarget).siblings('.direct-link');
+
+ $('#link-modal').on('show.bs.modal', () => {
+ $('#link').val(link.attr('href'));
+ });
+ });
+
+ $('#copy-link-btn').click(() => {
+ $('#link').select();
+ // eslint-disable-next-line
+ document.execCommand('copy');
+ });
+});
diff --git a/app/javascript/views/super_admin/users/edit.js b/app/javascript/views/super_admin/users/edit.js
index 0b3d18b054..b8ab0ebbe8 100644
--- a/app/javascript/views/super_admin/users/edit.js
+++ b/app/javascript/views/super_admin/users/edit.js
@@ -4,10 +4,29 @@ $(() => {
const options = { selector: '#super_admin_user_edit' };
initOrgSelection(options);
+ const updateMergeConfirmation = (userSelect) => {
+ // update the confirmation dialogue with the selected user's email address
+ const editingUserEmail = $('#superadmin_user_email').val();
+ const chosenUserEmail = userSelect.find('option:selected').text();
+ const submitButton = userSelect.closest('form').find(':submit');
+ submitButton.attr('data-confirm',
+ `Confirm Account Merge: The account for ${editingUserEmail} will be merged with ${chosenUserEmail}.
+ All plans and account information for ${chosenUserEmail} will now be accessible via ${editingUserEmail}.
+ The account for ${chosenUserEmail} will then be destroyed.`);
+ };
+
$('#super_admin_user_edit').on('submit', (e) => {
// Additional validation to force the user to choose an org or type something for other
if (!validateOrgSelection(options)) {
e.preventDefault();
}
});
+
+ $('#merge_form').on('ajax:success', (e, data) => {
+ // replace the search form with the merge form
+ $('#merge_form_container').html(data.form);
+ const userSelect = $('#merge_id');
+ userSelect.on('change', () => updateMergeConfirmation(userSelect));
+ userSelect.change();
+ });
});
diff --git a/app/models/answer.rb b/app/models/answer.rb
index dbda9d08c0..c6417be54e 100644
--- a/app/models/answer.rb
+++ b/app/models/answer.rb
@@ -93,7 +93,7 @@ def has_question_option(option_id)
# presence of text
#
# Returns Boolean
- def is_valid?
+ def answered?
if question.present?
if question.question_format.option_based?
return question_options.any?
diff --git a/app/models/phase.rb b/app/models/phase.rb
index 84c216c7cc..a6c9709313 100644
--- a/app/models/phase.rb
+++ b/app/models/phase.rb
@@ -52,6 +52,12 @@ class Phase < ActiveRecord::Base
has_many :sections, dependent: :destroy
+ has_many :questions, through: :sections
+
+ has_many :answers, through: :questions
+
+ has_many :annotations, through: :questions
+
has_many :template_sections, -> {
not_modifiable
}, class_name: "Section"
diff --git a/app/models/plan.rb b/app/models/plan.rb
index 867d900fe1..afafe82536 100644
--- a/app/models/plan.rb
+++ b/app/models/plan.rb
@@ -165,8 +165,11 @@ class Plan < ActiveRecord::Base
scope :search, lambda { |term|
search_pattern = "%#{term}%"
joins(:template)
- .where("lower(plans.title) LIKE lower(?) OR lower(templates.title) LIKE lower(?)",
- search_pattern, search_pattern)
+ .where("lower(plans.title) LIKE lower(:search_pattern)
+ OR lower(templates.title) LIKE lower(:search_pattern)
+ OR lower(plans.principal_investigator) LIKE lower(:search_pattern)
+ OR lower(plans.principal_investigator_identifier) LIKE lower(:search_pattern)",
+ search_pattern: search_pattern)
}
# Retrieves plan, template, org, phases, sections and questions
@@ -486,11 +489,13 @@ def authors
# The number of answered questions from the entire plan
#
# Returns Integer
- def num_answered_questions
- Answer.where(id: answers.map(&:id))
- .includes(:question_options, question: :question_format)
- .to_a
- .sum { |answer| answer.is_valid? ? 1 : 0 }
+ def num_answered_questions(phase = nil)
+ return answers.select { |answer| answer.answered? }.length unless phase.present?
+
+ answered = answers.select do |answer|
+ answer.answered? && phase.questions.include?(answer.question)
+ end
+ answered.length
end
# The number of questions for a plan.
@@ -526,7 +531,7 @@ def no_questions_matches_no_answers?
question: :question_format)
.where(id: answer_ids)
num_answers = pre_fetched_answers.reduce(0) do |m, a|
- m += 1 if a.is_valid?
+ m += 1 if a.answered?
m
end
num_questions == num_answers
diff --git a/app/models/role.rb b/app/models/role.rb
index d3ede86ff5..1588897a05 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -82,6 +82,23 @@ class Role < ActiveRecord::Base
where(access: access_values)
}
+
+ # =================
+ # = Class Methods =
+ # =================
+
+ ##
+ # Get the integer values that correspond to a given access flag
+ # Convert into a condition, take the numerical half, remove formatting
+ # split on commas, and then convert each to an integer
+ #
+ # access - The symbol corresponding to the user's access, i.e. :editor
+ #
+ # Returns [Integer]
+ def self.bit_values(access)
+ Role.send(:chained_flags_values, 'access', access)
+ end
+
# ===========================
# = Public instance methods =
# ===========================
diff --git a/app/models/section.rb b/app/models/section.rb
index afa0599031..c7e52e687e 100644
--- a/app/models/section.rb
+++ b/app/models/section.rb
@@ -103,10 +103,11 @@ def to_s
# Returns the number of answered questions for a given plan
def num_answered_questions(plan)
return 0 if plan.nil?
- plan.answers.includes({ question: :question_format }, :question_options)
- .where(question_id: question_ids)
- .to_a
- .count(&:is_valid?)
+
+ answered = plan.answers.select do |answer|
+ answer.answered? && questions.include?(answer.question)
+ end
+ answered.length
end
def deep_copy(**options)
diff --git a/app/models/user.rb b/app/models/user.rb
index 199339a849..12febe6e2e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -56,6 +56,7 @@ class User < ActiveRecord::Base
include ConditionalUserMailer
include ValidationMessages
include ValidationValues
+ extend UniqueRandom
include Dmptool::Model::User
@@ -330,11 +331,8 @@ def remove_token!
# Returns Boolean
def keep_or_generate_token!
if api_token.nil? || api_token.empty?
- self.api_token = loop do
- random_token = SecureRandom.urlsafe_base64(nil, false)
- break random_token unless User.exists?(api_token: random_token)
- end
- update_column(:api_token, api_token) unless new_record?
+ new_token = User.unique_random(field_name: 'api_token')
+ update_column(:api_token, new_token) unless new_record?
end
end
@@ -395,6 +393,43 @@ def acknowledge(notification)
notifications << notification if notification.dismissable?
end
+ # remove personal data from the user account and save
+ # leave account in-place, with org for statistics (until we refactor those)
+ #
+ # Returns boolean
+ def archive
+ self.firstname = 'Deleted'
+ self.surname = 'User'
+ self.email = User.unique_random(field_name: 'email',
+ prefix: 'user_',
+ suffix: Rails.configuration.branding[:application].fetch(:archived_accounts_email_suffix, '@example.org'),
+ length: 5)
+ self.recovery_email = nil
+ self.api_token = nil
+ self.encrypted_password = nil
+ self.last_sign_in_ip = nil
+ self.current_sign_in_ip = nil
+ self.active = false
+ return self.save
+ end
+
+ def merge(to_be_merged)
+ # merge logic
+ # => answers -> map id
+ to_be_merged.answers.update_all(user_id: self.id)
+ # => notes -> map id
+ to_be_merged.notes.update_all(user_id: self.id)
+ # => plans -> map on id roles
+ to_be_merged.roles.update_all(user_id: self.id)
+ # => prefs -> Keep's from self
+ # => auths -> map onto keep id only if keep does not have the identifier
+ to_be_merged.user_identifiers.
+ where.not(identifier_scheme_id: self.identifier_scheme_ids)
+ .update_all(user_id: self.id)
+ # => ignore any perms the deleted user has
+ to_be_merged.destroy
+ end
+
private
# ============================
diff --git a/app/policies/template_policy.rb b/app/policies/template_policy.rb
index de4b0bcdff..b70e201675 100644
--- a/app/policies/template_policy.rb
+++ b/app/policies/template_policy.rb
@@ -1,12 +1,12 @@
class TemplatePolicy < ApplicationPolicy
attr_reader :user, :template
-
+
def initialize(user, template = Template.new)
raise Pundit::NotAuthorizedError, _("must be logged in") unless user.is_a?(User)
@user = user
@template = template
end
-
+
def index?
user.can_super_admin?
end
@@ -14,11 +14,11 @@ def index?
def organisational?
user.can_modify_templates?
end
-
+
def customisable?
user.can_modify_templates?
end
-
+
def new?
user.can_super_admin? || user.can_modify_templates?
end
@@ -30,7 +30,7 @@ def create?
def show?
user.can_super_admin? || (user.can_modify_templates? && template.org_id == user.org_id)
end
-
+
def edit?
user.can_super_admin? || (user.can_modify_templates? && template.org_id == user.org_id)
end
@@ -42,7 +42,7 @@ def update?
def destroy?
user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id))
end
-
+
def history?
user.can_super_admin? || (user.can_modify_templates? && template.org_id == user.org_id)
end
@@ -55,6 +55,10 @@ def transfer_customization?
user.can_super_admin? || user.can_modify_templates?
end
+ def template_export?
+ user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id))
+ end
+
# AJAX Calls
def copy?
user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id))
@@ -82,4 +86,4 @@ def template_options?
end
-end
\ No newline at end of file
+end
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index 4a3bc31491..5293585b4a 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -34,11 +34,15 @@ def activate?
end
def edit?
- signed_in_user.can_super_admin?
+ signed_in_user.can_super_admin? || signed_in_user.can_org_admin?
end
def update?
- signed_in_user.can_super_admin?
+ signed_in_user.can_super_admin? || signed_in_user.can_org_admin?
+ end
+
+ def user_plans?
+ signed_in_user.can_super_admin? || signed_in_user.can_org_admin?
end
def update_email_preferences?
@@ -49,6 +53,18 @@ def acknowledge_notification?
true
end
+ def merge?
+ signed_in_user.can_super_admin?
+ end
+
+ def archive?
+ signed_in_user.can_super_admin?
+ end
+
+ def search?
+ signed_in_user.can_super_admin?
+ end
+
class Scope < Scope
def resolve
scope.where(org_id: user.org_id)
diff --git a/app/views/answers/_status.html.erb b/app/views/answers/_status.html.erb
index bddd28fb96..f842fb09bf 100644
--- a/app/views/answers/_status.html.erb
+++ b/app/views/answers/_status.html.erb
@@ -2,7 +2,7 @@
<%= _('Saving...') %>
-<% if answer.is_valid? %>
+<% if answer.answered? %>
<%= _('Answered')%>
<%= _(' by %{user_name}') %{ :user_name => answer.user.name } if answer.user.present? %>
diff --git a/app/views/api/v0/plans/index.json.jbuilder b/app/views/api/v0/plans/index.json.jbuilder
index 7934bfc837..1ca4cd4de1 100644
--- a/app/views/api/v0/plans/index.json.jbuilder
+++ b/app/views/api/v0/plans/index.json.jbuilder
@@ -6,6 +6,8 @@ json.array! @plans.each do |plan|
json.id plan.id
json.title plan.title
json.grant_number plan.grant_number
+ json.last_updated plan.updated_at
+ json.creation_date plan.created_at
json.template do
json.title plan.template.title
json.id plan.template.family_id
@@ -25,7 +27,6 @@ json.array! @plans.each do |plan|
end
json.users plan.roles.each do |role|
json.email role.user.email
-
end
json.description plan.description
json.plan_content plan.template.phases.each do |phase|
@@ -40,6 +41,9 @@ json.array! @plans.each do |plan|
json.number question.number
json.format question.question_format.title
json.option_based question.question_format.option_based
+ json.themes question.themes.each do |theme|
+ json.theme theme.title
+ end
answer = plan.answers.select{ |a| a.question_id = question.id }.first
if answer.present?
json.answered true
diff --git a/app/views/api/v0/statistics/plans.json.jbuilder b/app/views/api/v0/statistics/plans.json.jbuilder
index 06cff393c4..fbab651afe 100644
--- a/app/views/api/v0/statistics/plans.json.jbuilder
+++ b/app/views/api/v0/statistics/plans.json.jbuilder
@@ -4,19 +4,32 @@ json.plans @org_plans.each do |plan|
json.id plan.id
json.grant_number plan.grant_number
json.title plan.title
+
json.template do
json.title plan.template.title
json.id plan.template.family_id
end
+
json.funder do
json.name (plan.template.org.funder? ? plan.template.org.name : '')
end
+
json.principal_investigator do
json.name plan.principal_investigator
end
+
json.data_contact do
json.info plan.data_contact
end
+
json.description plan.description
-end
\ No newline at end of file
+ json.date_created plan.created_at
+ json.date_last_updated plan.updated_at
+
+ json.completion do
+ json.total_questions plan.questions.count
+ json.answered_questions plan.answers.count
+ end
+
+end
diff --git a/app/views/branded/contact_us/contacts/new.html.erb b/app/views/branded/contact_us/contacts/new.html.erb
index f4169d2e98..e53adf0387 100644
--- a/app/views/branded/contact_us/contacts/new.html.erb
+++ b/app/views/branded/contact_us/contacts/new.html.erb
@@ -10,7 +10,7 @@
<%= sanitize _('You can find out more about us on our website (new window). %{application_name} is provided by the %{organisation_name}. If you would like to contact us about %{application_name}, please fill out the form below.') % {organisation_name: Rails.configuration.branding[:organisation][:name],
organisation_url: Rails.configuration.branding[:organisation][:url],
- application_name: Rails.configuration.branding[:application][:name]},
+ application_name: Rails.configuration.branding[:application][:name],
open_in_new_window_text: _('Opens in new window') },
tags: %w( a br span em ) %> %>
diff --git a/app/views/branded/layouts/_analytics.html.erb b/app/views/branded/layouts/_analytics.html.erb
index 7d47fabbaf..e7fed22703 100644
--- a/app/views/branded/layouts/_analytics.html.erb
+++ b/app/views/branded/layouts/_analytics.html.erb
@@ -1,21 +1,9 @@
<% if Rails.configuration.branding[:keys].present? %>
+
+
<% if Rails.env.stage? %>
<% end %>
-
- <% # this only works without TurboLinks, if we need to add TurboLinks back
- # to the application then we need to work around, http://railsapps.github.io/rails-google-analytics.html %>
- <% if Rails.env.production? %>
-
- <% end %>
<% end %>
diff --git a/app/views/branded/layouts/_constants.html.erb b/app/views/branded/layouts/_constants.html.erb
index bd5cfec426..862c3a6765 100644
--- a/app/views/branded/layouts/_constants.html.erb
+++ b/app/views/branded/layouts/_constants.html.erb
@@ -24,6 +24,8 @@ constants_json = {
NO_TEMPLATE_FOUND_ERROR: _('Unable to find a suitable template for the research organisation and funder you selected.'),
NEW_PLAN_DISABLED_TOOLTIP: _('Please select a research organisation and funder to continue.'),
+ OPENS_IN_A_NEW_WINDOW_TEXT: _('Opens in a new window'),
+
AJAX_LOADING: _('Loading ...'),
AJAX_UNABLE_TO_LOAD_TEMPLATE_SECTION: _('Unable to load the section\'s content at this time.'),
AJAX_UNABLE_TO_LOAD_TEMPLATE_SECTION_QUESTION: _('Unable to load the question\'s content at this time.')
diff --git a/app/views/branded/layouts/_footer.html.erb b/app/views/branded/layouts/_footer.html.erb
index b8753ba3f5..fae87839da 100644
--- a/app/views/branded/layouts/_footer.html.erb
+++ b/app/views/branded/layouts/_footer.html.erb
@@ -13,8 +13,8 @@
+
+<%= render 'shared/copy_link_modal' %>
diff --git a/app/views/branded/plans/_edit_details.html.erb b/app/views/branded/plans/_edit_details.html.erb
new file mode 100644
index 0000000000..4619188e14
--- /dev/null
+++ b/app/views/branded/plans/_edit_details.html.erb
@@ -0,0 +1,224 @@
+<% project_title_tooltip = _('If applying for funding, state the name exactly as in the grant proposal.') %>
+<% project_abstract_tooltip = _("Briefly summarise your research project to help others understand the purposes for which the data are being collected or created.") %>
+<% id_tooltip = _('A pertinent ID as determined by the funder and/or organisation.') %>
+
+
<%= _('To help you write your plan, %{application_name} can show you guidance from a variety of organisations.') %
+ {application_name: Rails.configuration.branding[:application][:name]} %>
+
+
+
+ <% if @all_guidance_groups.length > @important_ggs.length %>
+
<%= _('Find guidance from additional organisations below') %>
<%= _("There is no additional guidance for this template.") %>
+ <% end %>
+
+
+ <% if @all_guidance_groups.length > @important_ggs.length %>
+
+
+
+
+
+
+
<%= _('Select Guidance') %>
+
+ <%= _('To help you write your plan, %{application_name} can show you guidance from a variety of organisations. Please choose up to 6 organisations of the following organisations who offer guidance relevant to your plan.') %
+ {application_name: Rails.configuration.branding[:application][:name]} %>
+
+
<%= _("Don't forget to save your changes after making your selections.") %>
diff --git a/app/views/branded/plans/index.html.erb b/app/views/branded/plans/index.html.erb
new file mode 100644
index 0000000000..102595b4ca
--- /dev/null
+++ b/app/views/branded/plans/index.html.erb
@@ -0,0 +1,51 @@
+<% title _('My Dashboard') %>
+
+
+
<%= _('My Dashboard') %>
+
+
+
+ <% if @plans.count > 0 %>
+ <%= _('The table below lists the plans that you have created, and that have been shared with you by others. You can edit, share, download, make a copy, or remove these plans at any time.')%>
+ <% else %>
+ <%= _("Welcome") %>
+ <%= _("You are now ready to create your first DMP.") %>
+ <%= _("Click the 'Create plan' button below to begin.")%>
+ <% end %>
+
+
+<%# DMPTool Customization - we already have this link in the nav bar! %>
+
+
+ <%#= link_to _('Create plan'), new_plan_path, class: "btn btn-primary" %>
+
+
+
+
+
+ <% if @organisationally_or_publicly_visible.any? && !current_user.org.is_other? %>
+
<%= _('The table below lists the plans that users at your organisation have created and shared within your organisation. This allows you to download a PDF and view their plans as samples or to discover new research data.') %>
<%= sanitize _('ARK (Archival Resource Key) – a URL with extra features allowing you to ask for descriptive and archival metadata and to recognize certain kinds of relationships between identifiers. ARKs are used by memory organizations such as libraries, archives, and museums. They are resolved at "%{nt2_url}". Resolution depends on HTTP redirection and can be managed through an API or a user interface.') % { ark_url: 'https://confluence.ucop.edu/display/Curation/ARK', nt2_url: 'http://www.nt2.net' } %>
+
<%= sanitize _('ARK (Archival Resource Key) – a URL with extra features allowing you to ask for descriptive and archival metadata and to recognize certain kinds of relationships between identifiers. ARKs are used by memory organizations such as libraries, archives, and museums. They are resolved at "%{nt2_url}". Resolution depends on HTTP redirection and can be managed through an API or a user interface.') % { ark_url: 'http://n2t.net/e/ark_ids.html', nt2_url: 'http://www.nt2.net' } %>
<%= sanitize _('DOI (Digital Object Identifier) – an identifier that becomes actionable when embedded in a URL. DOIs are very popular in academic journal publishing. They are resolved at "%{doi_resolver_url}". Resolution depends on HTTP redirection and the Handle identifier protocol, and can be managed through an API or a user interface.') % { doi_url: 'http://www.doi.org/', doi_resolver_url: 'http://dx.doi.org' } %>
<%= sanitize _('Handle – an identifier that becomes actionable when embedded in a URL. Handles are resolved at "%{handle_url}". Resolution depends on HTTP redirection and the Handle protocol, and can be managed through an API or a user interface.') % { handle_url: 'http://www.handle.net/' } %>
<%= sanitize _('InChI (IUPAC International Chemical Identifier) – a non-actionable identifier for chemical substances that can be used in printed and electronic data sources, thus enabling easier linking of diverse data compilations.') % { inchi_url: 'https://iupac.org/who-we-are/divisions/division-details/inchi/' } %>
@@ -334,7 +334,7 @@
<%= sanitize _('Evaluate the anonymity of your data. Consider to what extent your data contains direct or indirect identifiers that could be combined with other public information to identify research participants.') %>
<%= sanitize _('Obtain a confidentiality review. A benefit of depositing your data with ICPSR is that their staff offers a Disclosure review service to check your data for confidential information.') % { icpsr_url: "https://www.icpsr.umich.edu/icpsrweb/content/deposit/guide/chapter5.html" } %>
<%= sanitize _('Comply with UC regulations. Researchers concerned about confidentiality issues with their data should consult the UC policy for Protection of Human Subjects in Research.') % { uc_policy_url: 'http://policy.ucop.edu/doc/2500499/ProtectnHumanSubj' } %>
-
<%= sanitize _('Comply with regulations for health research set forth in the Health Insurance Portability and Accountability Act (HIPPA).') % { icpsr_url: 'https://www.icpsr.umich.edu/icpsrweb/', hippa_url: 'https://privacyruleandresearch.nih.gov/' } %>
+
<%= sanitize _('Comply with regulations for health research set forth in the Health Insurance Portability and Accountability Act (HIPPA).') % { icpsr_url: 'https://www.icpsr.umich.edu/icpsrweb/', hippa_url: 'https://www.hhs.gov/hipaa/for-professionals/special-topics/research/index.html' } %>
<%= sanitize _('To ethically share confidential data, you may be able to:
Gain informed consent for data sharing (e.g. deposit in a repository or archive)
Anonymize the data by removing identifying information. Be aware, however, that any dataset that contains enough information to be useful will always present some risk.
<%= sanitize _('The information you enter into this system can be seen by you, people you have chosen to share access with, and—solely for the purposes of maintaining the service—system administrators at the CDL. We compile anonymized, automated, and aggregated information from plans, but we will not directly access, make use of, or share your content with anyone beyond CDL and your home institution without your permission. Authorized users at your home institution may access your plans for specific purposes—for example, to track compliance with funder/institutional requirements, to calculate storage requirements, or to assess demand for data management services across disciplines. For a detailed description of what information (other than the plans) we collect from visitors to this website and how it is used and managed, please see the CDL Privacy Policy and Baseline Supporting Practices listed at %{policies_url}') % {policies_url: 'http://www.cdlib.org/about/policies.html' } %>
+
<%= sanitize _('The information you enter into this system can be seen by you, people you have chosen to share access with, and—solely for the purposes of maintaining the service—system administrators at the CDL. We compile anonymized, automated, and aggregated information from plans, but we will not directly access, make use of, or share your content with anyone beyond CDL and your home institution without your permission. Authorized users at your home institution may access your plans for specific purposes—for example, to track compliance with funder/institutional requirements, to calculate storage requirements, or to assess demand for data management services across disciplines. For a detailed description of what information (other than the plans) we collect from visitors to this website and how it is used and managed, please see the CDL Privacy Policy and Baseline Supporting Practices listed at %{policies_url}') % {policies_url: 'https://cdlib.org/about/policies-and-guidelines/privacy-policy/' } %>
<%= f.button(_('Save'), class: 'btn btn-default', type: "submit", id: "personal_details_registration_form_submit") %>
- <%= link_to _('Archive'), super_admin_user_path(id: @user.id), 'data-method': 'delete', rel: 'nofollow',
+ <%= link_to _('Archive'), archive_super_admin_user_path(id: @user.id), 'data-method': 'put', rel: 'nofollow',
'data-confirm': _("You are about to archive %{user_name}. This will remove their personal information, but retain their plans, answers, and comments. They will be unable to sign-in. Are you sure?") % {user_name: @user.name},
class: 'btn btn-default', role: 'button' %>
- <%= sanitize _('%{application_name} is provided by the %{organisation_name}. You can find out more about us on our website (new window). If you would like to contact us about %{application_name}, please fill out the form below.') % {organisation_name: Rails.configuration.branding[:organisation][:name],
+ <%= sanitize _('%{application_name} is provided by the %{organisation_name}. You can find out more about us on our website (new window)%{open_in_new_window_text}. If you would like to contact us about %{application_name}, please fill out the form below.') % {
+ organisation_name: Rails.configuration.branding[:organisation][:name],
organisation_url: Rails.configuration.branding[:organisation][:url],
- application_name: Rails.configuration.branding[:application][:name]} %>
+ application_name: Rails.configuration.branding[:application][:name],
+ open_in_new_window_text: _('Opens in new window') },
+ tags: %w( a br span em ) %>
<%= f.label _('Name'), for: :name, class: "control-label" %>
- <%= f.text_field :name, as: :string, class: "form-control", 'data-toggle': 'tooltip', title: _('Add an appropriate name for your guidance group. This name will be used to tell the end user where the guidance has come from. It will be appended to text identifying the theme e.g. "[guidance group name]: guidance on data sharing" so we suggest you just use the organisation or department name.'), 'aria-required': true %>
+ <%= f.text_field :name, as: :string, class: "form-control", 'data-toggle': 'tooltip',
+ title: _('Add an appropriate name for your guidance group. This name will be used to tell the end user where the guidance has come from. It will be appended to text identifying the theme e.g. "[guidance group name]: guidance on data sharing" so we suggest you just use the organisation or department name.'),
+ spellcheck: true, 'aria-required': true %>